From 7614757063d2dab530ca42504dd42b26b09d3c2b Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Thu, 3 May 2018 12:05:34 +0100 Subject: [PATCH 001/145] Fix a typo in the Registry index --- app/views/projects/registry/repositories/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml index 76f57320f99..2a683d5be0f 100644 --- a/app/views/projects/registry/repositories/index.html.haml +++ b/app/views/projects/registry/repositories/index.html.haml @@ -30,7 +30,7 @@ %br %p - deploy_token = link_to(_('deploy token'), help_page_path('user/project/deploy_tokens/index', anchor: 'read-container-registry-images'), target: '_blank') - = s_('ContainerRegistry|You can also %{deploy_token} for read-only access to the registry images.').html_safe % { deploy_token: deploy_token } + = s_('ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images.').html_safe % { deploy_token: deploy_token } %br %p = s_('ContainerRegistry|Once you log in, you’re free to create and upload a container image using the common %{build} and %{push} commands').html_safe % { build: "build".html_safe, push: "push".html_safe } From 7be65f5d8fd7789b6f630ea04b7bcec8847ab436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Tue, 8 May 2018 12:20:54 +0200 Subject: [PATCH 002/145] Add cached_attr_time_reader to RedisCacheable --- app/models/concerns/redis_cacheable.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index b889f4202dc..0dd15734eae 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -12,6 +12,15 @@ module RedisCacheable end end end + + def cached_attr_time_reader(*attributes) + attributes.each do |attribute| + define_method("#{attribute}") do + cached_value = cached_attribute(attribute) + cached_value ? Time.zone.parse(cached_value) : read_attribute(attribute) + end + end + end end def cached_attribute(attribute) From 71be7a1c224813e627c9d258a32189f1949338ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Tue, 8 May 2018 12:23:07 +0200 Subject: [PATCH 003/145] Move Runner#contacted_at to RedisCacheable#cached_attr_time_reader --- app/models/ci/runner.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 23078f1c3ed..ce3e595a2e1 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -74,7 +74,8 @@ module Ci project_type: 3 } - cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at, :ip_address + cached_attr_reader :version, :revision, :platform, :architecture, :ip_address + cached_attr_time_reader :contacted_at chronic_duration_attr :maximum_timeout_human_readable, :maximum_timeout From bb3752c7d85b6f86a4ed9a92b7b3a09fc0ac9bb3 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 8 May 2018 17:09:27 +0100 Subject: [PATCH 004/145] Fixed inconsistent spacing in web IDE sidebar Closes #46162 --- app/assets/stylesheets/pages/repo.scss | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 00457717f00..bec7e57eacd 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -84,7 +84,6 @@ .ide-new-btn { display: none; - margin-right: -8px; } &:hover, @@ -116,7 +115,7 @@ display: flex; overflow: visible; align-items: center; - padding: 6px 12px; + padding: 6px $gl-padding; } .multi-file-loading-container { @@ -980,8 +979,8 @@ display: flex; align-items: center; padding: 10px 0; - margin-left: 10px; - margin-right: 10px; + margin-left: $gl-padding; + margin-right: $gl-padding; border-bottom: 1px solid $white-dark; .ide-new-btn { From 3d80fae582e332bc6cf547002ef236f33016dcc7 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 9 May 2018 14:38:17 +0100 Subject: [PATCH 005/145] fixed up spacing throughout sidebars --- .../ide/components/commit_sidebar/form.vue | 6 +- .../javascripts/ide/components/repo_file.vue | 4 +- app/assets/javascripts/ide/constants.js | 2 - app/assets/stylesheets/pages/repo.scss | 87 +++++++++++-------- 4 files changed, 56 insertions(+), 43 deletions(-) diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue index 4a645c827ad..81961fe3c57 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue @@ -5,7 +5,7 @@ import LoadingButton from '~/vue_shared/components/loading_button.vue'; import CommitMessageField from './message_field.vue'; import Actions from './actions.vue'; import SuccessMessage from './success_message.vue'; -import { activityBarViews, MAX_WINDOW_HEIGHT_COMPACT, COMMIT_ITEM_PADDING } from '../../constants'; +import { activityBarViews, MAX_WINDOW_HEIGHT_COMPACT } from '../../constants'; export default { components: { @@ -70,7 +70,7 @@ export default { ? this.$refs.formEl && this.$refs.formEl.offsetHeight : this.$refs.compactEl && this.$refs.compactEl.offsetHeight; - this.componentHeight = elHeight + COMMIT_ITEM_PADDING; + this.componentHeight = elHeight; }, enterTransition() { this.$nextTick(() => { @@ -78,7 +78,7 @@ export default { ? this.$refs.compactEl && this.$refs.compactEl.offsetHeight : this.$refs.formEl && this.$refs.formEl.offsetHeight; - this.componentHeight = elHeight + COMMIT_ITEM_PADDING; + this.componentHeight = elHeight; }); }, afterEndTransition() { diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index 14946f8c9fa..7bc865058c6 100644 --- a/app/assets/javascripts/ide/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -122,11 +122,11 @@ export default {
form, + > .commit-form-compact { + padding: $gl-padding 0; + margin-left: $gl-padding; + margin-right: $gl-padding; + border-top: 1px solid $white-dark; + } + .btn { font-size: $gl-font-size; } @@ -786,8 +802,9 @@ display: flex; flex: 1; flex-direction: column; - width: 100%; min-height: 140px; + margin-left: $gl-padding; + margin-right: $gl-padding; &.is-first { border-bottom: 1px solid $white-dark; @@ -979,8 +996,6 @@ display: flex; align-items: center; padding: 10px 0; - margin-left: $gl-padding; - margin-right: $gl-padding; border-bottom: 1px solid $white-dark; .ide-new-btn { @@ -1011,9 +1026,9 @@ .commit-form-slide-up-enter-active, .commit-form-slide-up-leave-active { position: absolute; - top: 16px; - left: 16px; - right: 16px; + top: 0; + left: 0; + right: 0; transition: all 0.3s ease; } From 953e1de296dc42e2f8510019413d47f43bebd609 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 9 May 2018 15:10:08 +0100 Subject: [PATCH 006/145] changed padding in headers --- app/assets/stylesheets/pages/repo.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 9865b65077f..0b9f9ee1ac7 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -502,7 +502,7 @@ align-items: center; margin-bottom: 0; border-bottom: 1px solid $white-dark; - padding: $grid-size 0; + padding: 12px 0; } .multi-file-commit-panel-header-title { @@ -995,7 +995,7 @@ .ide-tree-header { display: flex; align-items: center; - padding: 10px 0; + padding: 12px 0; border-bottom: 1px solid $white-dark; .ide-new-btn { From fb301b503018d5fb75f19c6c0d0f4ccf601ea70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 9 May 2018 18:38:30 +0200 Subject: [PATCH 007/145] Revert "Partially revert ebcd5711c5ff937bf925002bf9a5b636b037684e to fix runner pages" This reverts commit b14719ea04f29888e2bbbdccda872d3cb4e70ae7. --- app/views/admin/runners/_runner.html.haml | 2 +- app/views/shared/runners/show.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml index 6e76e7c2768..99fbbaec487 100644 --- a/app/views/admin/runners/_runner.html.haml +++ b/app/views/admin/runners/_runner.html.haml @@ -33,7 +33,7 @@ = tag %td - if runner.contacted_at - #{time_ago_in_words(runner.contacted_at)} ago + = time_ago_with_tooltip runner.contacted_at - else Never %td.admin-runner-btn-group-cell diff --git a/app/views/shared/runners/show.html.haml b/app/views/shared/runners/show.html.haml index 1265305608c..1a386d96bcd 100644 --- a/app/views/shared/runners/show.html.haml +++ b/app/views/shared/runners/show.html.haml @@ -66,6 +66,6 @@ %td Last contact %td - if @runner.contacted_at - #{time_ago_in_words(@runner.contacted_at)} ago + = time_ago_with_tooltip @runner.contacted_at - else Never From 0f4aa6598add80cdd6ae2ec0aede110b89e1d325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 9 May 2018 18:55:20 +0200 Subject: [PATCH 008/145] Add CHANGELOG --- .../46082-runner-contacted_at-is-not-always-a-time-type.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml diff --git a/changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml b/changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml new file mode 100644 index 00000000000..a021884f652 --- /dev/null +++ b/changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml @@ -0,0 +1,5 @@ +--- +title: Make Runner#contacted_at always a Time type. +merge_request: 18810 +author: +type: fixed From 4fd77b33186e72824861367962c524e023138aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 10 May 2018 12:38:51 +0200 Subject: [PATCH 009/145] Update CHANGELOG description --- .../46082-runner-contacted_at-is-not-always-a-time-type.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml b/changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml index a021884f652..07f67251b24 100644 --- a/changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml +++ b/changelogs/unreleased/46082-runner-contacted_at-is-not-always-a-time-type.yml @@ -1,5 +1,5 @@ --- -title: Make Runner#contacted_at always a Time type. +title: Fix Runner contacted at tooltip cache. merge_request: 18810 author: type: fixed From 8d49ec681ffe4638f4db3311879448958d34c6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 10 May 2018 12:41:09 +0200 Subject: [PATCH 010/145] Use symbol instead of string in RedisCacheable attribute definitions --- app/models/concerns/redis_cacheable.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index 0dd15734eae..bc86bbe6525 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -7,7 +7,7 @@ module RedisCacheable class_methods do def cached_attr_reader(*attributes) attributes.each do |attribute| - define_method("#{attribute}") do + define_method(attribute) do cached_attribute(attribute) || read_attribute(attribute) end end @@ -15,7 +15,7 @@ module RedisCacheable def cached_attr_time_reader(*attributes) attributes.each do |attribute| - define_method("#{attribute}") do + define_method(attribute) do cached_value = cached_attribute(attribute) cached_value ? Time.zone.parse(cached_value) : read_attribute(attribute) end From d3426a5f0003270af61e3b3ad38c0983d9cd4cfa Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 10 May 2018 12:03:14 +0100 Subject: [PATCH 011/145] fixed spacing of icon & dropdown button --- app/assets/stylesheets/pages/repo.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 0b9f9ee1ac7..baef3a64be2 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -86,7 +86,6 @@ .ide-new-btn { display: none; - margin-left: auto; } &:hover, From 7fd0f5fc9d52e749a9692e7e6b42308b20e247f3 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 10 May 2018 05:53:21 +0200 Subject: [PATCH 012/145] Adding progressbar explicitly and spec extensions for it --- Gemfile | 3 +++ Gemfile.lock | 1 + spec/support/helpers/rake_helpers.rb | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/Gemfile b/Gemfile index 2df14c032ed..12bff1ee867 100644 --- a/Gemfile +++ b/Gemfile @@ -174,6 +174,9 @@ gem 'httparty', '~> 0.13.3' # Colored output to console gem 'rainbow', '~> 2.2' +# Progress bar +gem 'ruby-progressbar' + # GitLab settings gem 'settingslogic', '~> 2.0.9' diff --git a/Gemfile.lock b/Gemfile.lock index ee28465decd..854a24ab0b4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1165,6 +1165,7 @@ DEPENDENCIES rubocop-rspec (~> 1.22.1) ruby-fogbugz (~> 0.2.1) ruby-prof (~> 0.17.0) + ruby-progressbar ruby_parser (~> 3.8) rufus-scheduler (~> 3.4) rugged (~> 0.27) diff --git a/spec/support/helpers/rake_helpers.rb b/spec/support/helpers/rake_helpers.rb index 86bfeed107c..acd9cce6a67 100644 --- a/spec/support/helpers/rake_helpers.rb +++ b/spec/support/helpers/rake_helpers.rb @@ -13,6 +13,10 @@ module RakeHelpers allow(main_object).to receive(:print) end + def silence_progress_bar + allow_any_instance_of(ProgressBar::Output).to receive(:stream).and_return(double().as_null_object) + end + def main_object @main_object ||= TOPLEVEL_BINDING.eval('self') end From 1736d74408a406468d9f66e4dda1a1492793b8d3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 May 2018 13:28:26 +0200 Subject: [PATCH 013/145] Improve fast specs helper to autoload the library --- spec/fast_spec_helper.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb index 978113a08a4..134eb25e4b1 100644 --- a/spec/fast_spec_helper.rb +++ b/spec/fast_spec_helper.rb @@ -3,14 +3,8 @@ require 'bundler/setup' ENV['GITLAB_ENV'] = 'test' ENV['IN_MEMORY_APPLICATION_SETTINGS'] = 'true' -unless Object.respond_to?(:require_dependency) - class Object - alias_method :require_dependency, :require - end -end - -# Defines Settings and Gitlab.config which are at the center of the app require_relative '../config/settings' -require_relative '../lib/gitlab' unless defined?(Gitlab.config) - require_relative 'support/rspec' +require 'active_support/all' + +ActiveSupport::Dependencies.autoload_paths << 'lib' From 6d0c10b1b791e1938ce42c332280f5fe7dcd489f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 May 2018 13:28:51 +0200 Subject: [PATCH 014/145] Make it possible to compare untrusted regexps --- lib/gitlab/untrusted_regexp.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb index 7ce2e9d636e..fb25755391d 100644 --- a/lib/gitlab/untrusted_regexp.rb +++ b/lib/gitlab/untrusted_regexp.rb @@ -9,7 +9,7 @@ module Gitlab # there is a strict limit on total execution time. See the RE2 documentation # at https://github.com/google/re2/wiki/Syntax for more details. class UntrustedRegexp - delegate :===, to: :regexp + delegate :===, :source, to: :regexp def initialize(pattern) @regexp = RE2::Regexp.new(pattern, log_errors: false) @@ -31,6 +31,10 @@ module Gitlab RE2.Replace(text, regexp, rewrite) end + def ==(other) + self.source == other.source + end + private attr_reader :regexp From 8b736c91fc928157df9ace050f769d0948b58c1d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 May 2018 13:29:05 +0200 Subject: [PATCH 015/145] Implement variables expression untrusted pattern lexeme --- .../ci/pipeline/expression/lexeme/pattern.rb | 26 +++++++++ .../expression/lexeme/pattern_spec.rb | 55 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb create mode 100644 spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb new file mode 100644 index 00000000000..8cb1af30252 --- /dev/null +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -0,0 +1,26 @@ +module Gitlab + module Ci + module Pipeline + module Expression + module Lexeme + class Pattern < Lexeme::Value + PATTERN = %r{/(?.+)/}.freeze + + def initialize(regexp) + @value = regexp + end + + def evaluate(variables = {}) + Gitlab::UntrustedRegexp.new(@value.to_s) + # TODO raise LexerError / ParserError in case of RegexpError + end + + def self.build(string) + new(string.match(PATTERN)[:regexp]) + end + end + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb new file mode 100644 index 00000000000..b7998b512f5 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -0,0 +1,55 @@ +require 'fast_spec_helper' +require_dependency 're2' + +describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do + describe '.build' do + it 'creates a new instance of the token' do + expect(described_class.build('/.*/')) + .to be_a(described_class) + end + end + + describe '.type' do + it 'is a value lexeme' do + expect(described_class.type).to eq :value + end + end + + describe '.scan' do + it 'correctly identifies a pattern token' do + scanner = StringScanner.new('/pattern/') + + token = described_class.scan(scanner) + + expect(token).not_to be_nil + expect(token.build.evaluate) + .to eq Gitlab::UntrustedRegexp.new('pattern') + end + + it 'is a greedy scanner for regexp boundaries' do + scanner = StringScanner.new('/some .* / pattern/') + + token = described_class.scan(scanner) + + expect(token).not_to be_nil + expect(token.build.evaluate) + .to eq Gitlab::UntrustedRegexp.new('some .* / pattern') + end + + it 'does not allow to use an empty pattern' do + scanner = StringScanner.new(%(//)) + + token = described_class.scan(scanner) + + expect(token).to be_nil + end + end + + describe '#evaluate' do + it 'returns a regular expression' do + string = described_class.new('abc') + + expect(string.evaluate).to eq Gitlab::UntrustedRegexp.new('abc') + end + end +end From 4c2b56897e17f884f28cb8824dacb724f85f96fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Fri, 11 May 2018 18:21:17 +0200 Subject: [PATCH 016/145] Add specs for #cached_attr_reader and cached_attr_time_reader --- spec/models/concerns/redis_cacheable_spec.rb | 81 +++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 3d7963120b6..ca09bf75216 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -1,21 +1,28 @@ require 'spec_helper' describe RedisCacheable do - let(:model) { double } + let(:model) do + Struct.new(:id, :attributes) do + def read_attribute(attribute) + attributes[attribute] + end + end + end + + let(:payload) { { name: 'value' } } + let(:instance) { model.new(1, payload) } + let(:cache_key) { instance.__send__(:cache_attribute_key) } before do - model.extend(described_class) - allow(model).to receive(:cache_attribute_key).and_return('key') + model.include(described_class) end describe '#cached_attribute' do - let(:payload) { { attribute: 'value' } } - - subject { model.cached_attribute(payload.keys.first) } + subject { instance.cached_attribute(payload.keys.first) } it 'gets the cache attribute' do Gitlab::Redis::SharedState.with do |redis| - expect(redis).to receive(:get).with('key') + expect(redis).to receive(:get).with(cache_key) .and_return(payload.to_json) end @@ -24,16 +31,68 @@ describe RedisCacheable do end describe '#cache_attributes' do - let(:values) { { name: 'new_name' } } - - subject { model.cache_attributes(values) } + subject { instance.cache_attributes(payload) } it 'sets the cache attributes' do Gitlab::Redis::SharedState.with do |redis| - expect(redis).to receive(:set).with('key', values.to_json, anything) + expect(redis).to receive(:set).with(cache_key, payload.to_json, anything) end subject end end + + describe '#cached_attr_reader' do + subject { instance.name } + + before do + model.cached_attr_reader(:name) + end + + context 'when there is no cached value' do + it 'checks the cached value first then reads the attribute' do + expect(instance).to receive(:cached_attribute).and_return(nil) + expect(instance).to receive(:read_attribute).and_return(payload[:name]) + + expect(subject).to eq(payload[:name]) + end + end + + context 'when there is a cached value' do + it 'reads the cached value' do + expect(instance).to receive(:cached_attribute).and_return(payload[:name]) + expect(instance).not_to receive(:read_attribute) + + expect(subject).to eq(payload[:name]) + end + end + end + + describe '#cached_attr_time_reader' do + subject { instance.time } + + before do + model.cached_attr_time_reader(:time) + end + + context 'when there is no cached value' do + it 'checks the cached value first then reads the attribute' do + expect(instance).to receive(:cached_attribute).and_return(nil) + expect(instance).to receive(:read_attribute).and_return(Time.zone.now) + + expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) + expect(subject).to be_within(1.minute).of(Time.zone.now) + end + end + + context 'when there is a cached value' do + it 'reads the cached value' do + expect(instance).to receive(:cached_attribute).and_return(Time.zone.now.to_s) + expect(instance).not_to receive(:read_attribute) + + expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) + expect(subject).to be_within(1.minute).of(Time.zone.now) + end + end + end end From 20cfc3fccec75562bdb514587d2c9f7b59554c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Fri, 11 May 2018 18:36:16 +0200 Subject: [PATCH 017/145] Clear memoization after caching new values --- app/models/concerns/redis_cacheable.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index bc86bbe6525..8bd0df8dfbe 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -31,6 +31,8 @@ module RedisCacheable Gitlab::Redis::SharedState.with do |redis| redis.set(cache_attribute_key, values.to_json, ex: CACHED_ATTRIBUTES_EXPIRY_TIME) end + + clear_memoization(:cached_attributes) end private From be73838bdfa01b7a80d087e421505dccf625ae55 Mon Sep 17 00:00:00 2001 From: Chantal Rollison Date: Fri, 11 May 2018 11:05:14 -0700 Subject: [PATCH 018/145] Add anchor for incoming email regex, closes 44989 --- changelogs/unreleased/ccr-incoming-email-regex-anchor.yml | 3 +++ lib/gitlab/incoming_email.rb | 2 +- spec/lib/gitlab/incoming_email_spec.rb | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/ccr-incoming-email-regex-anchor.yml diff --git a/changelogs/unreleased/ccr-incoming-email-regex-anchor.yml b/changelogs/unreleased/ccr-incoming-email-regex-anchor.yml new file mode 100644 index 00000000000..a0d787e570e --- /dev/null +++ b/changelogs/unreleased/ccr-incoming-email-regex-anchor.yml @@ -0,0 +1,3 @@ +title: Add anchor for incoming email regex +merge_request: !18917 +type: added diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb index c9122a23568..d323cb9dadf 100644 --- a/lib/gitlab/incoming_email.rb +++ b/lib/gitlab/incoming_email.rb @@ -57,7 +57,7 @@ module Gitlab regex = Regexp.escape(wildcard_address) regex = regex.sub(Regexp.escape(WILDCARD_PLACEHOLDER), '(.+)') - Regexp.new(regex).freeze + Regexp.new(/\A#{regex}\z/).freeze end end end diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb index ad087f42e06..4c0c3fcbcc7 100644 --- a/spec/lib/gitlab/incoming_email_spec.rb +++ b/spec/lib/gitlab/incoming_email_spec.rb @@ -83,6 +83,10 @@ describe Gitlab::IncomingEmail do it "returns reply key" do expect(described_class.key_from_address("replies+key@example.com")).to eq("key") end + + it 'does not match emails with extra bits' do + expect(described_class.key_from_address('somereplies+somekey@example.com.someotherdomain.com')).to be nil + end end context 'self.key_from_fallback_message_id' do From 544117a55b753c76ed3235aebb0a5fb808bf2cf1 Mon Sep 17 00:00:00 2001 From: Sarrah Vesselov Date: Sat, 12 May 2018 01:31:29 +0000 Subject: [PATCH 019/145] add specific details related to how the UX team uses labels to tackle issues --- CONTRIBUTING.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f5d19ce2ce..758c2a9ea6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -304,7 +304,24 @@ any potential community contributor to @-mention per above. ## Implement design & UI elements -Please see the [UX Guide for GitLab]. +For guidance on UX implementation at GitLab, please refer to our [Design System](https://design.gitlab.com/). + +The UX team uses labels to manage their workflow. + +The ~"UX" label on an issue is a signal to the UX team that it will need UX attention. +To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/ux/) of the handbook. + +Once an issue has been worked on and is ready for development, a UXer applies the ~"UX ready" label to that issue. + +The UX team has a special type label called ~"design artifact". This label indicates that the final output +for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone. +Any issue labelled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is +needed until the solution has been decided. + +~"design artifact" issues are like any other issue and should contain a milestone label when scheduled in the current milestone. + +Once the ~"design artifact" issue has been completed, the UXer removes the ~"design artifact" label and applies the ~"UX ready" label. The Product Manager can use the +existing issue or decide to create a whole new issue for the purpose of development. ## Issue tracker From abdaebc8281226f1921673b0be688ecd2ce1e6a9 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Wed, 9 May 2018 04:17:38 +0900 Subject: [PATCH 020/145] Apply NestingDepth (level 5) (pages/pipelines.scss) --- app/assets/stylesheets/pages/pipelines.scss | 58 +++++++------------ .../39584-nesting-depth-5-pages-pipelines.yml | 5 ++ 2 files changed, 26 insertions(+), 37 deletions(-) create mode 100644 changelogs/unreleased/39584-nesting-depth-5-pages-pipelines.yml diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 02803e7b040..1264d977b2f 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -66,13 +66,9 @@ } } - .btn-group { - &.open { - .btn-default { - background-color: $white-normal; - border-color: $border-white-normal; - } - } + .btn-group.open .btn-default { + background-color: $white-normal; + border-color: $border-white-normal; } .btn .text-center { @@ -361,16 +357,14 @@ &:not(:first-child) { margin-left: 44px; - .left-connector { - &::before { - content: ''; - position: absolute; - top: 48%; - left: -44px; - border-top: 2px solid $border-color; - width: 44px; - height: 1px; - } + .left-connector::before { + content: ''; + position: absolute; + top: 48%; + left: -44px; + border-top: 2px solid $border-color; + width: 44px; + height: 1px; } } } @@ -386,22 +380,16 @@ &:last-child { .build { // Remove right connecting horizontal line from first build in last stage - &:first-child { - &::after { - border: 0; - } + &:first-child::after { + border: 0; } // Remove right curved connectors from all builds in last stage - &:not(:first-child) { - &::after { - border: 0; - } + &:not(:first-child)::after { + border: 0; } // Remove opposite curve - .curve { - &::before { - display: none; - } + .curve::before { + display: none; } } } @@ -409,16 +397,12 @@ &:first-child { .build { // Remove left curved connectors from all builds in first stage - &:not(:first-child) { - &::before { - border: 0; - } + &:not(:first-child)::before { + border: 0; } // Remove opposite curve - .curve { - &::after { - display: none; - } + .curve::after { + display: none; } } } diff --git a/changelogs/unreleased/39584-nesting-depth-5-pages-pipelines.yml b/changelogs/unreleased/39584-nesting-depth-5-pages-pipelines.yml new file mode 100644 index 00000000000..9f07fcdfa0b --- /dev/null +++ b/changelogs/unreleased/39584-nesting-depth-5-pages-pipelines.yml @@ -0,0 +1,5 @@ +--- +title: Apply NestingDepth (level 5) (pages/pipelines.scss) +merge_request: 18830 +author: Takuya Noguchi +type: other From b1fcac85bd0f0904c277fb917790a253777d0e8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Sat, 12 May 2018 14:43:05 +0200 Subject: [PATCH 021/145] Make #cached_attr_reader and #cached_attr_time_reader specs Redis based --- spec/models/concerns/redis_cacheable_spec.rb | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index ca09bf75216..cb81ded7c48 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -42,7 +42,7 @@ describe RedisCacheable do end end - describe '#cached_attr_reader' do + describe '#cached_attr_reader', :clean_gitlab_redis_shared_state do subject { instance.name } before do @@ -51,7 +51,6 @@ describe RedisCacheable do context 'when there is no cached value' do it 'checks the cached value first then reads the attribute' do - expect(instance).to receive(:cached_attribute).and_return(nil) expect(instance).to receive(:read_attribute).and_return(payload[:name]) expect(subject).to eq(payload[:name]) @@ -60,15 +59,14 @@ describe RedisCacheable do context 'when there is a cached value' do it 'reads the cached value' do - expect(instance).to receive(:cached_attribute).and_return(payload[:name]) - expect(instance).not_to receive(:read_attribute) + instance.cache_attributes(payload) expect(subject).to eq(payload[:name]) end end end - describe '#cached_attr_time_reader' do + describe '#cached_attr_time_reader', :clean_gitlab_redis_shared_state do subject { instance.time } before do @@ -77,7 +75,6 @@ describe RedisCacheable do context 'when there is no cached value' do it 'checks the cached value first then reads the attribute' do - expect(instance).to receive(:cached_attribute).and_return(nil) expect(instance).to receive(:read_attribute).and_return(Time.zone.now) expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) @@ -87,8 +84,7 @@ describe RedisCacheable do context 'when there is a cached value' do it 'reads the cached value' do - expect(instance).to receive(:cached_attribute).and_return(Time.zone.now.to_s) - expect(instance).not_to receive(:read_attribute) + instance.cache_attributes(time: Time.zone.now) expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) expect(subject).to be_within(1.minute).of(Time.zone.now) From f4810647a0e962c7296ba4626b9c6b5e36662efd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Sat, 12 May 2018 15:32:13 +0200 Subject: [PATCH 022/145] Add RedisCacheable specs for memoization correctness --- spec/models/concerns/redis_cacheable_spec.rb | 26 ++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index cb81ded7c48..06d194008c1 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -9,7 +9,7 @@ describe RedisCacheable do end end - let(:payload) { { name: 'value' } } + let(:payload) { { name: 'value', time: Time.zone.now } } let(:instance) { model.new(1, payload) } let(:cache_key) { instance.__send__(:cache_attribute_key) } @@ -50,9 +50,7 @@ describe RedisCacheable do end context 'when there is no cached value' do - it 'checks the cached value first then reads the attribute' do - expect(instance).to receive(:read_attribute).and_return(payload[:name]) - + it 'reads the attribute' do expect(subject).to eq(payload[:name]) end end @@ -64,6 +62,14 @@ describe RedisCacheable do expect(subject).to eq(payload[:name]) end end + + it 'always returns the latest values' do + expect(instance.name).to eq(payload[:name]) + + instance.cache_attributes(name: 'new_value') + + expect(instance.name).to eq('new_value') + end end describe '#cached_attr_time_reader', :clean_gitlab_redis_shared_state do @@ -74,9 +80,7 @@ describe RedisCacheable do end context 'when there is no cached value' do - it 'checks the cached value first then reads the attribute' do - expect(instance).to receive(:read_attribute).and_return(Time.zone.now) - + it 'reads the attribute' do expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) expect(subject).to be_within(1.minute).of(Time.zone.now) end @@ -90,5 +94,13 @@ describe RedisCacheable do expect(subject).to be_within(1.minute).of(Time.zone.now) end end + + it 'always returns the latest values' do + expect(instance.time).to be_within(1.minute).of(Time.zone.now) + + instance.cache_attributes(time: 1.hour.ago) + + expect(instance.time).to be_within(1.minute).of(1.hour.ago) + end end end From b784a985f2188e328da32cc9fdc73c8d4ac63733 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 May 2018 14:24:59 +0200 Subject: [PATCH 023/145] Do not raise if variable expression can not be evaluated --- lib/gitlab/ci/pipeline/expression/statement.rb | 2 ++ .../ci/pipeline/expression/statement_spec.rb | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb index 09a7c98464b..363e0b708a6 100644 --- a/lib/gitlab/ci/pipeline/expression/statement.rb +++ b/lib/gitlab/ci/pipeline/expression/statement.rb @@ -35,6 +35,8 @@ module Gitlab def truthful? evaluate.present? + rescue StatementError + false end def valid? diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index 6685bf5385b..633c932eabb 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'fast_spec_helper' describe Gitlab::Ci::Pipeline::Expression::Statement do subject do @@ -114,7 +114,8 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do ['$UNDEFINED_VARIABLE == null', true], ['$PRESENT_VARIABLE', true], ['$UNDEFINED_VARIABLE', false], - ['$EMPTY_VARIABLE', false] + ['$EMPTY_VARIABLE', false], + ['$INVALID = 1', false] ] statements.each do |expression, value| @@ -126,5 +127,16 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do end end end + + context 'when evaluating expression raises an error' do + let(:text) { '$PRESENT_VARIABLE' } + + it 'returns false' do + allow(subject).to receive(:evaluate) + .and_raise(described_class::StatementError) + + expect(subject.truthful?).to be_falsey + end + end end end From ac65257c40052f739492f0648f6b7c06a1c95250 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 May 2018 14:38:08 +0200 Subject: [PATCH 024/145] Raise variables statement exception if pattern is invalid --- lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb | 6 ++++-- lib/gitlab/ci/pipeline/expression/parser.rb | 2 ++ .../ci/pipeline/expression/lexeme/pattern_spec.rb | 11 +++++++++-- spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb | 2 +- spec/lib/gitlab/ci/pipeline/expression/token_spec.rb | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb index 8cb1af30252..2ff527e34a8 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -11,8 +11,10 @@ module Gitlab end def evaluate(variables = {}) - Gitlab::UntrustedRegexp.new(@value.to_s) - # TODO raise LexerError / ParserError in case of RegexpError + # TODO multiline support + @regexp = Gitlab::UntrustedRegexp.new(@value) + rescue RegexpError + raise Parser::ParserError, 'Invalid regular expression!' end def self.build(string) diff --git a/lib/gitlab/ci/pipeline/expression/parser.rb b/lib/gitlab/ci/pipeline/expression/parser.rb index 90f94d0b763..fe23ab0b2f8 100644 --- a/lib/gitlab/ci/pipeline/expression/parser.rb +++ b/lib/gitlab/ci/pipeline/expression/parser.rb @@ -3,6 +3,8 @@ module Gitlab module Pipeline module Expression class Parser + ParserError = Class.new(Statement::StatementError) + def initialize(tokens) @tokens = tokens.to_enum @nodes = [] diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index b7998b512f5..ed69742cd7c 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -47,9 +47,16 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do describe '#evaluate' do it 'returns a regular expression' do - string = described_class.new('abc') + regexp = described_class.new('abc') - expect(string.evaluate).to eq Gitlab::UntrustedRegexp.new('abc') + expect(regexp.evaluate).to eq Gitlab::UntrustedRegexp.new('abc') + end + + it 'raises error if evaluated regexp is not valid' do + regexp = described_class.new('invalid ( .*') + + expect { regexp.evaluate } + .to raise_error(Gitlab::Ci::Pipeline::Expression::Parser::ParserError) end end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb index e8e6f585310..2b78b1dd4a7 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'fast_spec_helper' describe Gitlab::Ci::Pipeline::Expression::Parser do describe '#tree' do diff --git a/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb index 6d7453f0de5..cedfe270f9d 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/token_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'fast_spec_helper' describe Gitlab::Ci::Pipeline::Expression::Token do let(:value) { '$VARIABLE' } From f16f2b599412ed1514ba96d81758b9a2e6fd9c1f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 May 2018 15:03:10 +0200 Subject: [PATCH 025/145] Add pattern matching variables expression lexeme --- .../ci/pipeline/expression/lexeme/matches.rb | 29 ++++++++++ .../expression/lexeme/matches_spec.rb | 57 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 lib/gitlab/ci/pipeline/expression/lexeme/matches.rb create mode 100644 spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb new file mode 100644 index 00000000000..806f2082227 --- /dev/null +++ b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb @@ -0,0 +1,29 @@ +module Gitlab + module Ci + module Pipeline + module Expression + module Lexeme + class Matches < Lexeme::Operator + PATTERN = /=~/.freeze + + def initialize(left, right) + @left = left + @right = right + end + + def evaluate(variables = {}) + text = @left.evaluate(variables) + regexp = @right.evaluate(variables) + + regexp.scan(text).any? + end + + def self.build(_value, behind, ahead) + new(behind, ahead) + end + end + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb new file mode 100644 index 00000000000..22907b0554a --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb @@ -0,0 +1,57 @@ +require 'fast_spec_helper' +require_dependency 're2' + +describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do + let(:left) { double('left') } + let(:right) { double('right') } + + describe '.build' do + it 'creates a new instance of the token' do + expect(described_class.build('=~', left, right)) + .to be_a(described_class) + end + end + + describe '.type' do + it 'is an operator' do + expect(described_class.type).to eq :operator + end + end + + describe '#evaluate' do + it 'returns false when left and right do not match' do + allow(left).to receive(:evaluate).and_return('my-string') + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('something')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq false + end + + it 'returns true when left and right match' do + allow(left).to receive(:evaluate).and_return('my-awesome-string') + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('awesome.string$')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq true + end + + it 'supports multiline strings' do + allow(left).to receive(:evaluate).and_return <<~TEXT + My awesome contents + + My-text-string! + TEXT + + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('text-string')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq true + end + end +end From 13f68f55b31847fc8fab03ce3aadf92c930dd531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 14 May 2018 15:54:23 +0200 Subject: [PATCH 026/145] Expect calls to read_attribute depending on cache presence --- spec/models/concerns/redis_cacheable_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 06d194008c1..bd5319a0c8c 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -51,12 +51,16 @@ describe RedisCacheable do context 'when there is no cached value' do it 'reads the attribute' do + expect(instance).to receive(:read_attribute).and_call_original + expect(subject).to eq(payload[:name]) end end context 'when there is a cached value' do it 'reads the cached value' do + expect(instance).not_to receive(:read_attribute) + instance.cache_attributes(payload) expect(subject).to eq(payload[:name]) @@ -81,6 +85,8 @@ describe RedisCacheable do context 'when there is no cached value' do it 'reads the attribute' do + expect(instance).to receive(:read_attribute).and_call_original + expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) expect(subject).to be_within(1.minute).of(Time.zone.now) end @@ -88,6 +94,8 @@ describe RedisCacheable do context 'when there is a cached value' do it 'reads the cached value' do + expect(instance).not_to receive(:read_attribute) + instance.cache_attributes(time: Time.zone.now) expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) From 2c29e80a93dffb1a854f4c63171b1a91eddea8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Mon, 14 May 2018 16:12:18 +0200 Subject: [PATCH 027/145] Check for exact time matches --- spec/models/concerns/redis_cacheable_spec.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index bd5319a0c8c..2e496cfb439 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -79,6 +79,8 @@ describe RedisCacheable do describe '#cached_attr_time_reader', :clean_gitlab_redis_shared_state do subject { instance.time } + let(:other_time) { Time.zone.parse('May 14 2018') } + before do model.cached_attr_time_reader(:time) end @@ -88,7 +90,7 @@ describe RedisCacheable do expect(instance).to receive(:read_attribute).and_call_original expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) - expect(subject).to be_within(1.minute).of(Time.zone.now) + expect(subject).to eq(payload[:time]) end end @@ -96,19 +98,19 @@ describe RedisCacheable do it 'reads the cached value' do expect(instance).not_to receive(:read_attribute) - instance.cache_attributes(time: Time.zone.now) + instance.cache_attributes(time: other_time) expect(subject).to be_instance_of(ActiveSupport::TimeWithZone) - expect(subject).to be_within(1.minute).of(Time.zone.now) + expect(subject).to eq(other_time) end end it 'always returns the latest values' do - expect(instance.time).to be_within(1.minute).of(Time.zone.now) + expect(instance.time).to eq(payload[:time]) - instance.cache_attributes(time: 1.hour.ago) + instance.cache_attributes(time: other_time) - expect(instance.time).to be_within(1.minute).of(1.hour.ago) + expect(instance.time).to eq(other_time) end end end From 1fa1858546af5ca0339de9b0fef77d739496c7d9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 14 May 2018 18:29:21 +0100 Subject: [PATCH 028/145] Replace vue resource with axios for environments --- .../components/environments_app.vue | 3 +- .../environments/mixins/environments_mixin.js | 18 +- .../services/environments_service.js | 15 +- .../environments/environments_app_spec.js | 212 +++++++----------- .../folder/environments_folder_view_spec.js | 81 +++---- spec/javascripts/environments/mock_data.js | 1 + 6 files changed, 115 insertions(+), 215 deletions(-) diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue index c0be72f7401..3da762446c9 100644 --- a/app/assets/javascripts/environments/components/environments_app.vue +++ b/app/assets/javascripts/environments/components/environments_app.vue @@ -68,8 +68,7 @@ this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', showLoader); this.service.getFolderContent(folder.folder_path) - .then(resp => resp.json()) - .then(response => this.store.setfolderContent(folder, response.environments)) + .then(response => this.store.setfolderContent(folder, response.data.environments)) .then(() => this.store.updateEnvironmentProp(folder, 'isLoadingFolderContent', false)) .catch(() => { Flash(s__('Environments|An error occurred while fetching the environments.')); diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index 34d18d55120..c8745e35802 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -6,7 +6,6 @@ import Visibility from 'visibilityjs'; import Poll from '../../lib/utils/poll'; import { getParameterByName, - parseQueryStringIntoObject, } from '../../lib/utils/common_utils'; import { s__ } from '../../locale'; import Flash from '../../flash'; @@ -46,17 +45,14 @@ export default { methods: { saveData(resp) { - const headers = resp.headers; - return resp.json().then((response) => { - this.isLoading = false; + this.isLoading = false; - if (_.isEqual(parseQueryStringIntoObject(resp.url.split('?')[1]), this.requestData)) { - this.store.storeAvailableCount(response.available_count); - this.store.storeStoppedCount(response.stopped_count); - this.store.storeEnvironments(response.environments); - this.store.setPagination(headers); - } - }); + if (_.isEqual(resp.config.params, this.requestData)) { + this.store.storeAvailableCount(resp.data.available_count); + this.store.storeStoppedCount(resp.data.stopped_count); + this.store.storeEnvironments(resp.data.environments); + this.store.setPagination(resp.headers); + } }, /** diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js index 03ab74b3338..22563fad8d2 100644 --- a/app/assets/javascripts/environments/services/environments_service.js +++ b/app/assets/javascripts/environments/services/environments_service.js @@ -1,25 +1,22 @@ -/* eslint-disable class-methods-use-this */ -import Vue from 'vue'; -import VueResource from 'vue-resource'; - -Vue.use(VueResource); +import axios from '~/lib/utils/axios_utils'; export default class EnvironmentsService { constructor(endpoint) { - this.environments = Vue.resource(endpoint); + this.environmentsEndpoint = endpoint; this.folderResults = 3; } get(options = {}) { const { scope, page } = options; - return this.environments.get({ scope, page }); + return axios.get(this.environmentsEndpoint, { params: { scope, page } }); } + // eslint-disable-next-line class-methods-use-this postAction(endpoint) { - return Vue.http.post(endpoint, {}, { emulateJSON: true }); + return axios.post(endpoint, {}, { emulateJSON: true }); } getFolderContent(folderUrl) { - return Vue.http.get(`${folderUrl}.json?per_page=${this.folderResults}`); + return axios.get(`${folderUrl}.json?per_page=${this.folderResults}`); } } diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js index e4c3bf2bef1..615168645b4 100644 --- a/spec/javascripts/environments/environments_app_spec.js +++ b/spec/javascripts/environments/environments_app_spec.js @@ -1,8 +1,8 @@ -import _ from 'underscore'; import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import environmentsComponent from '~/environments/components/environments_app.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; -import { headersInterceptor } from 'spec/helpers/vue_resource_helper'; import { environment, folder } from './mock_data'; describe('Environment', () => { @@ -18,103 +18,76 @@ describe('Environment', () => { let EnvironmentsComponent; let component; + let mock; beforeEach(() => { + mock = new MockAdapter(axios); + EnvironmentsComponent = Vue.extend(environmentsComponent); }); + afterEach(() => { + component.$destroy(); + mock.restore(); + }); + describe('successfull request', () => { describe('without environments', () => { - const environmentsEmptyResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([]), { - status: 200, - })); - }; + beforeEach((done) => { + mock.onGet(mockData.endpoint).reply(200, { environments: [] }); - beforeEach(() => { - Vue.http.interceptors.push(environmentsEmptyResponseInterceptor); - Vue.http.interceptors.push(headersInterceptor); - }); - - afterEach(() => { - Vue.http.interceptors = _.without( - Vue.http.interceptors, environmentsEmptyResponseInterceptor, - ); - Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); - }); - - it('should render the empty state', (done) => { component = mountComponent(EnvironmentsComponent, mockData); setTimeout(() => { - expect( - component.$el.querySelector('.js-new-environment-button').textContent, - ).toContain('New environment'); - - expect( - component.$el.querySelector('.js-blank-state-title').textContent, - ).toContain('You don\'t have any environments right now.'); - done(); }, 0); }); + + it('should render the empty state', () => { + expect( + component.$el.querySelector('.js-new-environment-button').textContent, + ).toContain('New environment'); + + expect( + component.$el.querySelector('.js-blank-state-title').textContent, + ).toContain('You don\'t have any environments right now.'); + }); }); describe('with paginated environments', () => { - let backupInterceptors; - const environmentsResponseInterceptor = (request, next) => { - next((response) => { - response.headers.set('X-nExt-pAge', '2'); - }); - - next(request.respondWith(JSON.stringify({ + beforeEach((done) => { + mock.onGet(mockData.endpoint).reply(200, { environments: [environment], stopped_count: 1, available_count: 0, - }), { - status: 200, - headers: { - 'X-nExt-pAge': '2', - 'x-page': '1', - 'X-Per-Page': '1', - 'X-Prev-Page': '', - 'X-TOTAL': '37', - 'X-Total-Pages': '2', - }, - })); - }; + }, { + 'X-nExt-pAge': '2', + 'x-page': '1', + 'X-Per-Page': '1', + 'X-Prev-Page': '', + 'X-TOTAL': '37', + 'X-Total-Pages': '2', + }); - beforeEach(() => { - backupInterceptors = Vue.http.interceptors; - Vue.http.interceptors = [ - environmentsResponseInterceptor, - headersInterceptor, - ]; component = mountComponent(EnvironmentsComponent, mockData); - }); - afterEach(() => { - Vue.http.interceptors = backupInterceptors; - }); - - it('should render a table with environments', (done) => { setTimeout(() => { - expect(component.$el.querySelectorAll('table')).not.toBeNull(); - expect( - component.$el.querySelector('.environment-name').textContent.trim(), - ).toEqual(environment.name); done(); }, 0); }); + it('should render a table with environments', () => { + expect(component.$el.querySelectorAll('table')).not.toBeNull(); + expect( + component.$el.querySelector('.environment-name').textContent.trim(), + ).toEqual(environment.name); + }); + describe('pagination', () => { - it('should render pagination', (done) => { - setTimeout(() => { - expect( - component.$el.querySelectorAll('.gl-pagination li').length, - ).toEqual(5); - done(); - }, 0); + it('should render pagination', () => { + expect( + component.$el.querySelectorAll('.gl-pagination li').length, + ).toEqual(5); }); it('should make an API request when page is clicked', (done) => { @@ -133,50 +106,39 @@ describe('Environment', () => { expect(component.updateContent).toHaveBeenCalledWith({ scope: 'stopped', page: '1' }); done(); - }); + }, 0); }); }); }); }); describe('unsuccessfull request', () => { - const environmentsErrorResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([]), { - status: 500, - })); - }; + beforeEach((done) => { + mock.onGet(mockData.endpoint).reply(500, {}); - beforeEach(() => { - Vue.http.interceptors.push(environmentsErrorResponseInterceptor); - }); - - afterEach(() => { - Vue.http.interceptors = _.without( - Vue.http.interceptors, environmentsErrorResponseInterceptor, - ); - }); - - it('should render empty state', (done) => { component = mountComponent(EnvironmentsComponent, mockData); setTimeout(() => { - expect( - component.$el.querySelector('.js-blank-state-title').textContent, - ).toContain('You don\'t have any environments right now.'); done(); }, 0); }); + + it('should render empty state', () => { + expect( + component.$el.querySelector('.js-blank-state-title').textContent, + ).toContain('You don\'t have any environments right now.'); + }); }); describe('expandable folders', () => { - const environmentsResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify({ - environments: [folder], - stopped_count: 0, - available_count: 1, - }), { - status: 200, - headers: { + beforeEach(() => { + mock.onGet(mockData.endpoint).reply(200, + { + environments: [folder], + stopped_count: 0, + available_count: 1, + }, + { 'X-nExt-pAge': '2', 'x-page': '1', 'X-Per-Page': '1', @@ -184,18 +146,11 @@ describe('Environment', () => { 'X-TOTAL': '37', 'X-Total-Pages': '2', }, - })); - }; - - beforeEach(() => { - Vue.http.interceptors.push(environmentsResponseInterceptor); - component = mountComponent(EnvironmentsComponent, mockData); - }); - - afterEach(() => { - Vue.http.interceptors = _.without( - Vue.http.interceptors, environmentsResponseInterceptor, ); + + mock.onGet(environment.folder_path).reply(200, { environments: [environment] }); + + component = mountComponent(EnvironmentsComponent, mockData); }); it('should open a closed folder', (done) => { @@ -211,7 +166,7 @@ describe('Environment', () => { ).not.toContain('display: none'); done(); }); - }); + }, 0); }); it('should close an opened folder', (done) => { @@ -233,7 +188,7 @@ describe('Environment', () => { done(); }); }); - }); + }, 0); }); it('should show children environments and a button to show all environments', (done) => { @@ -242,49 +197,32 @@ describe('Environment', () => { component.$el.querySelector('.folder-name').click(); Vue.nextTick(() => { - const folderInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify({ - environments: [environment], - }), { status: 200 })); - }; - - Vue.http.interceptors.push(folderInterceptor); - // wait for next async request setTimeout(() => { expect(component.$el.querySelectorAll('.js-child-row').length).toEqual(1); expect(component.$el.querySelector('.text-center > a.btn').textContent).toContain('Show all'); - - Vue.http.interceptors = _.without(Vue.http.interceptors, folderInterceptor); done(); }); }); - }); + }, 0); }); }); describe('methods', () => { - const environmentsEmptyResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([]), { - status: 200, - })); - }; - beforeEach(() => { - Vue.http.interceptors.push(environmentsEmptyResponseInterceptor); - Vue.http.interceptors.push(headersInterceptor); + mock.onGet(mockData.endpoint).reply(200, + { + environments: [], + stopped_count: 0, + available_count: 1, + }, + {}, + ); component = mountComponent(EnvironmentsComponent, mockData); spyOn(history, 'pushState').and.stub(); }); - afterEach(() => { - Vue.http.interceptors = _.without( - Vue.http.interceptors, environmentsEmptyResponseInterceptor, - ); - Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); - }); - describe('updateContent', () => { it('should set given parameters', (done) => { component.updateContent({ scope: 'stopped', page: '3' }) diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js index 906a1116974..f5ce4df0bfe 100644 --- a/spec/javascripts/environments/folder/environments_folder_view_spec.js +++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js @@ -1,13 +1,15 @@ -import _ from 'underscore'; import Vue from 'vue'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue'; -import { headersInterceptor } from 'spec/helpers/vue_resource_helper'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { environmentsList } from '../mock_data'; describe('Environments Folder View', () => { let Component; let component; + let mock; + const mockData = { endpoint: 'environments.json', folderName: 'review', @@ -17,46 +19,35 @@ describe('Environments Folder View', () => { }; beforeEach(() => { + mock = new MockAdapter(axios); + Component = Vue.extend(environmentsFolderViewComponent); }); afterEach(() => { + mock.restore(); + component.$destroy(); }); describe('successfull request', () => { - const environmentsResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify({ + beforeEach(() => { + mock.onGet(mockData.endpoint).reply(200, { environments: environmentsList, stopped_count: 1, available_count: 0, - }), { - status: 200, - headers: { - 'X-nExt-pAge': '2', - 'x-page': '1', - 'X-Per-Page': '2', - 'X-Prev-Page': '', - 'X-TOTAL': '20', - 'X-Total-Pages': '10', - }, - })); - }; - - beforeEach(() => { - Vue.http.interceptors.push(environmentsResponseInterceptor); - Vue.http.interceptors.push(headersInterceptor); + }, { + 'X-nExt-pAge': '2', + 'x-page': '1', + 'X-Per-Page': '2', + 'X-Prev-Page': '', + 'X-TOTAL': '20', + 'X-Total-Pages': '10', + }); component = mountComponent(Component, mockData); }); - afterEach(() => { - Vue.http.interceptors = _.without( - Vue.http.interceptors, environmentsResponseInterceptor, - ); - Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); - }); - it('should render a table with environments', (done) => { setTimeout(() => { expect(component.$el.querySelectorAll('table')).not.toBeNull(); @@ -135,25 +126,15 @@ describe('Environments Folder View', () => { }); describe('unsuccessfull request', () => { - const environmentsErrorResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([]), { - status: 500, - })); - }; - beforeEach(() => { - Vue.http.interceptors.push(environmentsErrorResponseInterceptor); - }); + mock.onGet(mockData.endpoint).reply(500, { + environments: [], + }); - afterEach(() => { - Vue.http.interceptors = _.without( - Vue.http.interceptors, environmentsErrorResponseInterceptor, - ); + component = mountComponent(Component, mockData); }); it('should not render a table', (done) => { - component = mountComponent(Component, mockData); - setTimeout(() => { expect( component.$el.querySelector('table'), @@ -190,27 +171,15 @@ describe('Environments Folder View', () => { }); describe('methods', () => { - const environmentsEmptyResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([]), { - status: 200, - })); - }; - beforeEach(() => { - Vue.http.interceptors.push(environmentsEmptyResponseInterceptor); - Vue.http.interceptors.push(headersInterceptor); + mock.onGet(mockData.endpoint).reply(200, { + environments: [], + }); component = mountComponent(Component, mockData); spyOn(history, 'pushState').and.stub(); }); - afterEach(() => { - Vue.http.interceptors = _.without( - Vue.http.interceptors, environmentsEmptyResponseInterceptor, - ); - Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); - }); - describe('updateContent', () => { it('should set given parameters', (done) => { component.updateContent({ scope: 'stopped', page: '4' }) diff --git a/spec/javascripts/environments/mock_data.js b/spec/javascripts/environments/mock_data.js index 15e11aa686b..8a1e26935d9 100644 --- a/spec/javascripts/environments/mock_data.js +++ b/spec/javascripts/environments/mock_data.js @@ -82,6 +82,7 @@ export const environment = { stop_path: '/root/review-app/environments/7/stop', created_at: '2017-01-31T10:53:46.894Z', updated_at: '2017-01-31T10:53:46.894Z', + folder_path: '/root/review-app/environments/7', }, }; From 6a10ed3ff2ddd0e13fe22dbd43c3dcd5e3083910 Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 9 May 2018 16:43:46 -0500 Subject: [PATCH 029/145] Add dot to separate system notes content --- app/assets/javascripts/notes/components/note_header.vue | 5 ++++- .../javascripts/vue_shared/components/time_ago_tooltip.vue | 4 ++-- app/assets/stylesheets/pages/notes.scss | 4 ++++ app/views/shared/notes/_note.html.haml | 5 +++-- changelogs/unreleased/jivl-add-dot-system-notes.yml | 5 +++++ 5 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/jivl-add-dot-system-notes.yml diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue index c3d1ef1fcc6..76028f7f3f7 100644 --- a/app/assets/javascripts/notes/components/note_header.vue +++ b/app/assets/javascripts/notes/components/note_header.vue @@ -78,10 +78,13 @@ export default { v-html="actionTextHtml" class="system-note-message"> + + · + + class="note-timestamp system-note-separator"> - {{ timeFormated(time) }} + data-container="body" + v-text="timeFormated(time)"> diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 6d5c6cb136f..a8fd3aa6412 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -459,6 +459,10 @@ ul.notes { white-space: normal; } + .system-note-separator { + color: $gl-text-color-disabled; + } + a:hover { text-decoration: underline; } diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml index 893a7f26ebd..d4e67b5e7e3 100644 --- a/app/views/shared/notes/_note.html.haml +++ b/app/views/shared/notes/_note.html.haml @@ -41,8 +41,9 @@ - if note.system %span.system-note-message = markdown_field(note, :note) - %a{ href: "##{dom_id(note)}" } - = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago') + %span.system-note-separator + · + %a.system-note-separator{ href: "##{dom_id(note)}" }= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago') - unless note.system? .note-actions - if note.for_personal_snippet? diff --git a/changelogs/unreleased/jivl-add-dot-system-notes.yml b/changelogs/unreleased/jivl-add-dot-system-notes.yml new file mode 100644 index 00000000000..2246bab1464 --- /dev/null +++ b/changelogs/unreleased/jivl-add-dot-system-notes.yml @@ -0,0 +1,5 @@ +--- +title: Add dot to separate system notes content +merge_request: 18864 +author: +type: changed From 475d2edf04c13c6a5ed2b5c68d75efd2a8024c2b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 12:37:09 +0200 Subject: [PATCH 030/145] Reorganize exceptions in pipeline expressions module --- lib/gitlab/ci/pipeline/expression.rb | 10 ++++++++++ lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb | 3 +-- lib/gitlab/ci/pipeline/expression/lexer.rb | 4 ++-- lib/gitlab/ci/pipeline/expression/parser.rb | 2 -- lib/gitlab/ci/pipeline/expression/statement.rb | 6 +++--- .../ci/pipeline/expression/lexeme/pattern_spec.rb | 2 +- spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb | 8 ++++---- .../gitlab/ci/pipeline/expression/statement_spec.rb | 4 ++-- 8 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 lib/gitlab/ci/pipeline/expression.rb diff --git a/lib/gitlab/ci/pipeline/expression.rb b/lib/gitlab/ci/pipeline/expression.rb new file mode 100644 index 00000000000..f57df7c5637 --- /dev/null +++ b/lib/gitlab/ci/pipeline/expression.rb @@ -0,0 +1,10 @@ +module Gitlab + module Ci + module Pipeline + module Expression + ExpressionError = Class.new(StandardError) + RuntimeError = Class.new(ExpressionError) + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb index 2ff527e34a8..59b8e4fad4c 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -11,10 +11,9 @@ module Gitlab end def evaluate(variables = {}) - # TODO multiline support @regexp = Gitlab::UntrustedRegexp.new(@value) rescue RegexpError - raise Parser::ParserError, 'Invalid regular expression!' + raise Expression::RuntimeError, 'Invalid regular expression!' end def self.build(string) diff --git a/lib/gitlab/ci/pipeline/expression/lexer.rb b/lib/gitlab/ci/pipeline/expression/lexer.rb index e1c68b7c3c2..ebc6565266f 100644 --- a/lib/gitlab/ci/pipeline/expression/lexer.rb +++ b/lib/gitlab/ci/pipeline/expression/lexer.rb @@ -5,6 +5,8 @@ module Gitlab class Lexer include ::Gitlab::Utils::StrongMemoize + SyntaxError = Class.new(Expression::ExpressionError) + LEXEMES = [ Expression::Lexeme::Variable, Expression::Lexeme::String, @@ -12,8 +14,6 @@ module Gitlab Expression::Lexeme::Equals ].freeze - SyntaxError = Class.new(Statement::StatementError) - MAX_TOKENS = 100 def initialize(statement, max_tokens: MAX_TOKENS) diff --git a/lib/gitlab/ci/pipeline/expression/parser.rb b/lib/gitlab/ci/pipeline/expression/parser.rb index fe23ab0b2f8..90f94d0b763 100644 --- a/lib/gitlab/ci/pipeline/expression/parser.rb +++ b/lib/gitlab/ci/pipeline/expression/parser.rb @@ -3,8 +3,6 @@ module Gitlab module Pipeline module Expression class Parser - ParserError = Class.new(Statement::StatementError) - def initialize(tokens) @tokens = tokens.to_enum @nodes = [] diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb index 363e0b708a6..de37c50d2a2 100644 --- a/lib/gitlab/ci/pipeline/expression/statement.rb +++ b/lib/gitlab/ci/pipeline/expression/statement.rb @@ -3,7 +3,7 @@ module Gitlab module Pipeline module Expression class Statement - StatementError = Class.new(StandardError) + StatementError = Class.new(Expression::ExpressionError) GRAMMAR = [ %w[variable equals string], @@ -35,13 +35,13 @@ module Gitlab def truthful? evaluate.present? - rescue StatementError + rescue Expression::ExpressionError false end def valid? parse_tree.is_a?(Lexeme::Base) - rescue StatementError + rescue Expression::ExpressionError false end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index ed69742cd7c..47385ce0a5b 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -56,7 +56,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do regexp = described_class.new('invalid ( .*') expect { regexp.evaluate } - .to raise_error(Gitlab::Ci::Pipeline::Expression::Parser::ParserError) + .to raise_error(Gitlab::Ci::Pipeline::Expression::RuntimeError) end end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb index 230ceeb07f8..3f11b3f7673 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexer do end describe '#tokens' do - it 'tokenss single value' do + it 'returns single value' do tokens = described_class.new('$VARIABLE').tokens expect(tokens).to be_one @@ -20,14 +20,14 @@ describe Gitlab::Ci::Pipeline::Expression::Lexer do expect(tokens).to all(be_an_instance_of(token_class)) end - it 'tokenss multiple values of the same token' do + it 'returns multiple values of the same token' do tokens = described_class.new("$VARIABLE1 $VARIABLE2").tokens expect(tokens.size).to eq 2 expect(tokens).to all(be_an_instance_of(token_class)) end - it 'tokenss multiple values with different tokens' do + it 'returns multiple values with different tokens' do tokens = described_class.new('$VARIABLE "text" "value"').tokens expect(tokens.size).to eq 3 @@ -36,7 +36,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexer do expect(tokens.third.value).to eq '"value"' end - it 'tokenss tokens and operators' do + it 'returns tokens and operators' do tokens = described_class.new('$VARIABLE == "text"').tokens expect(tokens.size).to eq 3 diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index 633c932eabb..6d58838bf14 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -36,7 +36,7 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do '== "123"', # invalid left side '"some string"', # only string provided '$VAR ==', # invalid right side - '12345', # unknown syntax + 'null', # missing lexemes '' # empty statement ] @@ -44,7 +44,7 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do context "when expression grammar is #{syntax.inspect}" do let(:text) { syntax } - it 'aises a statement error exception' do + it 'raises a statement error exception' do expect { subject.parse_tree } .to raise_error described_class::StatementError end From 65f4e7b2a1fe8946109c7aa0d59999bfaaeba257 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 13:04:18 +0200 Subject: [PATCH 031/145] Add support for pattern matching in variables expressions --- lib/gitlab/ci/pipeline/expression/lexeme/matches.rb | 2 +- lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb | 2 ++ lib/gitlab/ci/pipeline/expression/lexer.rb | 4 +++- lib/gitlab/ci/pipeline/expression/statement.rb | 4 +++- .../ci/pipeline/expression/lexeme/matches_spec.rb | 10 ++++++++++ .../ci/pipeline/expression/lexeme/pattern_spec.rb | 1 - .../gitlab/ci/pipeline/expression/statement_spec.rb | 11 ++++++++--- 7 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb index 806f2082227..10957598f76 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/matches.rb @@ -15,7 +15,7 @@ module Gitlab text = @left.evaluate(variables) regexp = @right.evaluate(variables) - regexp.scan(text).any? + regexp.scan(text.to_s).any? end def self.build(_value, behind, ahead) diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb index 59b8e4fad4c..f7b12914249 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -3,6 +3,8 @@ module Gitlab module Pipeline module Expression module Lexeme + require_dependency 're2' + class Pattern < Lexeme::Value PATTERN = %r{/(?.+)/}.freeze diff --git a/lib/gitlab/ci/pipeline/expression/lexer.rb b/lib/gitlab/ci/pipeline/expression/lexer.rb index ebc6565266f..4cacb1e62c9 100644 --- a/lib/gitlab/ci/pipeline/expression/lexer.rb +++ b/lib/gitlab/ci/pipeline/expression/lexer.rb @@ -10,8 +10,10 @@ module Gitlab LEXEMES = [ Expression::Lexeme::Variable, Expression::Lexeme::String, + Expression::Lexeme::Pattern, Expression::Lexeme::Null, - Expression::Lexeme::Equals + Expression::Lexeme::Equals, + Expression::Lexeme::Matches ].freeze MAX_TOKENS = 100 diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb index de37c50d2a2..8886cbae516 100644 --- a/lib/gitlab/ci/pipeline/expression/statement.rb +++ b/lib/gitlab/ci/pipeline/expression/statement.rb @@ -6,12 +6,14 @@ module Gitlab StatementError = Class.new(Expression::ExpressionError) GRAMMAR = [ + %w[variable], %w[variable equals string], %w[variable equals variable], %w[variable equals null], %w[string equals variable], %w[null equals variable], - %w[variable] + %w[variable matches pattern], + %w[pattern matches variable] ].freeze def initialize(statement, variables = {}) diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb index 22907b0554a..a8890262402 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb @@ -39,6 +39,16 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do expect(operator.evaluate).to eq true end + it 'supports matching against a nil value' do + allow(left).to receive(:evaluate).and_return(nil) + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('pattern')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq false + end + it 'supports multiline strings' do allow(left).to receive(:evaluate).and_return <<~TEXT My awesome contents diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index 47385ce0a5b..a14a28056d8 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -1,5 +1,4 @@ require 'fast_spec_helper' -require_dependency 're2' describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do describe '.build' do diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index 6d58838bf14..a5733c13768 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -84,7 +84,6 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do describe '#evaluate' do statements = [ ['$PRESENT_VARIABLE == "my variable"', true], - ["$PRESENT_VARIABLE == 'my variable'", true], ['"my variable" == $PRESENT_VARIABLE', true], ['$PRESENT_VARIABLE == null', false], ['$EMPTY_VARIABLE == null', false], @@ -93,7 +92,11 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do ['$UNDEFINED_VARIABLE == null', true], ['null == $UNDEFINED_VARIABLE', true], ['$PRESENT_VARIABLE', 'my variable'], - ['$UNDEFINED_VARIABLE', nil] + ['$UNDEFINED_VARIABLE', nil], + ["$PRESENT_VARIABLE =~ /var.*e$/", true], + ["$PRESENT_VARIABLE =~ /^var.*/", false], + ["$EMPTY_VARIABLE =~ /var.*/", false], + ["$UNDEFINED_VARIABLE =~ /var.*/", false] ] statements.each do |expression, value| @@ -115,7 +118,9 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do ['$PRESENT_VARIABLE', true], ['$UNDEFINED_VARIABLE', false], ['$EMPTY_VARIABLE', false], - ['$INVALID = 1', false] + ['$INVALID = 1', false], + ["$PRESENT_VARIABLE =~ /var.*/", true], + ["$UNDEFINED_VARIABLE =~ /var.*/", false] ] statements.each do |expression, value| From 7babc59e4713e53162ac028eb570b78988d3bd6c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 13:15:38 +0200 Subject: [PATCH 032/145] Use parameterized RSpec to improve variables expressions specs --- .../ci/pipeline/expression/statement_spec.rb | 79 ++++++++++--------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index a5733c13768..bba5db7904a 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -1,4 +1,5 @@ require 'fast_spec_helper' +require 'rspec-parameterized' describe Gitlab::Ci::Pipeline::Expression::Statement do subject do @@ -82,54 +83,54 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do end describe '#evaluate' do - statements = [ - ['$PRESENT_VARIABLE == "my variable"', true], - ['"my variable" == $PRESENT_VARIABLE', true], - ['$PRESENT_VARIABLE == null', false], - ['$EMPTY_VARIABLE == null', false], - ['"" == $EMPTY_VARIABLE', true], - ['$EMPTY_VARIABLE', ''], - ['$UNDEFINED_VARIABLE == null', true], - ['null == $UNDEFINED_VARIABLE', true], - ['$PRESENT_VARIABLE', 'my variable'], - ['$UNDEFINED_VARIABLE', nil], - ["$PRESENT_VARIABLE =~ /var.*e$/", true], - ["$PRESENT_VARIABLE =~ /^var.*/", false], - ["$EMPTY_VARIABLE =~ /var.*/", false], - ["$UNDEFINED_VARIABLE =~ /var.*/", false] - ] + using RSpec::Parameterized::TableSyntax - statements.each do |expression, value| - context "when using expression `#{expression}`" do - let(:text) { expression } + where(:expression, :value) do + '$PRESENT_VARIABLE == "my variable"' | true + '"my variable" == $PRESENT_VARIABLE' | true + '$PRESENT_VARIABLE == null' | false + '$EMPTY_VARIABLE == null' | false + '"" == $EMPTY_VARIABLE' | true + '$EMPTY_VARIABLE' | '' + '$UNDEFINED_VARIABLE == null' | true + 'null == $UNDEFINED_VARIABLE' | true + '$PRESENT_VARIABLE' | 'my variable' + '$UNDEFINED_VARIABLE' | nil + "$PRESENT_VARIABLE =~ /var.*e$/" | true + "$PRESENT_VARIABLE =~ /^var.*/" | false + "$EMPTY_VARIABLE =~ /var.*/" | false + "$UNDEFINED_VARIABLE =~ /var.*/" | false + end - it "evaluates to `#{value.inspect}`" do - expect(subject.evaluate).to eq value - end + with_them do + let(:text) { expression } + + it "evaluates to `#{params[:value].inspect}`" do + expect(subject.evaluate).to eq value end end end describe '#truthful?' do - statements = [ - ['$PRESENT_VARIABLE == "my variable"', true], - ["$PRESENT_VARIABLE == 'no match'", false], - ['$UNDEFINED_VARIABLE == null', true], - ['$PRESENT_VARIABLE', true], - ['$UNDEFINED_VARIABLE', false], - ['$EMPTY_VARIABLE', false], - ['$INVALID = 1', false], - ["$PRESENT_VARIABLE =~ /var.*/", true], - ["$UNDEFINED_VARIABLE =~ /var.*/", false] - ] + using RSpec::Parameterized::TableSyntax - statements.each do |expression, value| - context "when using expression `#{expression}`" do - let(:text) { expression } + where(:expression, :value) do + '$PRESENT_VARIABLE == "my variable"' | true + "$PRESENT_VARIABLE == 'no match'" | false + '$UNDEFINED_VARIABLE == null' | true + '$PRESENT_VARIABLE' | true + '$UNDEFINED_VARIABLE' | false + '$EMPTY_VARIABLE' | false + '$INVALID = 1' | false + "$PRESENT_VARIABLE =~ /var.*/" | true + "$UNDEFINED_VARIABLE =~ /var.*/" | false + end - it "returns `#{value.inspect}`" do - expect(subject.truthful?).to eq value - end + with_them do + let(:text) { expression } + + it "returns `#{params[:value].inspect}`" do + expect(subject.truthful?).to eq value end end From df91580cc400897c5bf69d5e496b2e4db2f75e4f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 13:31:45 +0200 Subject: [PATCH 033/145] Do not support inverse variable pattern matching --- lib/gitlab/ci/pipeline/expression/statement.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb index 8886cbae516..b36f1e0f865 100644 --- a/lib/gitlab/ci/pipeline/expression/statement.rb +++ b/lib/gitlab/ci/pipeline/expression/statement.rb @@ -12,8 +12,7 @@ module Gitlab %w[variable equals null], %w[string equals variable], %w[null equals variable], - %w[variable matches pattern], - %w[pattern matches variable] + %w[variable matches pattern] ].freeze def initialize(statement, variables = {}) From 9b213583937d73c79115a043b7733c76f79f5d3c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 13:40:17 +0200 Subject: [PATCH 034/145] Add variables expressions regexp support changelog --- .../feature-gb-add-regexp-variables-expression.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/feature-gb-add-regexp-variables-expression.yml diff --git a/changelogs/unreleased/feature-gb-add-regexp-variables-expression.yml b/changelogs/unreleased/feature-gb-add-regexp-variables-expression.yml new file mode 100644 index 00000000000..d77c5b42497 --- /dev/null +++ b/changelogs/unreleased/feature-gb-add-regexp-variables-expression.yml @@ -0,0 +1,5 @@ +--- +title: Add support for variables expression pattern matching syntax +merge_request: 18902 +author: +type: added From c1377c6cf0647cccd04bc6fb65d745a2e446f827 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 14:35:12 +0200 Subject: [PATCH 035/145] Remove useless assignment in pattern lexeme --- lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb index f7b12914249..62927441035 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -13,7 +13,7 @@ module Gitlab end def evaluate(variables = {}) - @regexp = Gitlab::UntrustedRegexp.new(@value) + Gitlab::UntrustedRegexp.new(@value) rescue RegexpError raise Expression::RuntimeError, 'Invalid regular expression!' end From 73aee958e2f4f7428a961c3c63323a7b782599bf Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 14:41:20 +0200 Subject: [PATCH 036/145] Add docs on pattern matching syntax in variables expression --- doc/ci/variables/README.md | 7 +++++++ doc/ci/yaml/README.md | 13 ++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 42367bf13f7..cbd2ab979f4 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -530,6 +530,13 @@ Below you can find supported syntax reference: `$STAGING` value needs to a string, with length higher than zero. Variable that contains only whitespace characters is not an empty variable. +1. Pattern matching _(added in 11.0)_ + + > Example: `$VARIABLE =~ /^content.*/` + + It is possible perform pattern matching against a variable and regular + expression. Expression like this evaluates to truth if matches are found. + ### Unsupported predefined variables Because GitLab evaluates variables before creating jobs, we do not support a diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 2a17a51d7f8..3e77a6f58b7 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -344,10 +344,11 @@ job: kubernetes: active ``` -Example of using variables expressions: +Examples of using variables expressions: ```yaml deploy: + script: cap staging deploy only: refs: - branches @@ -356,6 +357,16 @@ deploy: - $STAGING ``` +Another use case is exluding jobs depending on a commit message _(added in 11.0)_: + +```yaml +end-to-end: + script: rake test:end-to-end + except: + variables: + - $CI_COMMIT_MESSAGE =~ /skip-end-to-end-tests/ +``` + Learn more about variables expressions on [a separate page][variables-expressions]. ## `tags` From f52de2f73cc9d26c26fd66c23892ac42bf973b05 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 15 May 2018 15:18:18 +0200 Subject: [PATCH 037/145] Make variables expression pattern case-sensitivity explicit --- doc/ci/variables/README.md | 8 ++++++++ .../ci/pipeline/expression/lexeme/matches_spec.rb | 13 +++++++++++++ .../gitlab/ci/pipeline/expression/statement_spec.rb | 1 + 3 files changed, 22 insertions(+) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index cbd2ab979f4..58acc030aee 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -537,6 +537,12 @@ Below you can find supported syntax reference: It is possible perform pattern matching against a variable and regular expression. Expression like this evaluates to truth if matches are found. + Pattern matching is case-sensitive by default. Prepend `(?i)` to a + match-group to make a pattern case-insensitive. + + Under the hood we are using [RE2 library][re2-library], see + [syntax documentation][re2-syntax] for reference. + ### Unsupported predefined variables Because GitLab evaluates variables before creating jobs, we do not support a @@ -577,3 +583,5 @@ These variables are also not supported in a context of a [builds-policies]: ../yaml/README.md#only-and-except-complex [dynamic-environments]: ../environments.md#dynamic-environments [gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token +[re2-library]: https://github.com/google/re2 +[re2-syntax]: https://github.com/google/re2/wiki/Syntax diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb index a8890262402..49e5af52f4d 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/matches_spec.rb @@ -63,5 +63,18 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Matches do expect(operator.evaluate).to eq true end + + it 'supports regexp flags' do + allow(left).to receive(:evaluate).and_return <<~TEXT + My AWESOME content + TEXT + + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('(?i)awesome')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq true + end end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index bba5db7904a..1ceb373e19c 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -100,6 +100,7 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do "$PRESENT_VARIABLE =~ /^var.*/" | false "$EMPTY_VARIABLE =~ /var.*/" | false "$UNDEFINED_VARIABLE =~ /var.*/" | false + "$PRESENT_VARIABLE =~ /(?i)VAR.*/" | true end with_them do From 3593b83a0279bab40f8ba97dc339b32c56f6e0df Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Tue, 15 May 2018 16:55:07 +0100 Subject: [PATCH 038/145] Handles action icons requests in a contained way and shows a loading icon to the user --- .../components/graph/action_component.vue | 67 +++++++++------ .../graph/dropdown_job_component.vue | 13 ++- .../components/graph/graph_component.vue | 11 ++- .../components/graph/job_component.vue | 12 +-- .../graph/stage_column_component.vue | 13 ++- .../components/pipelines_table_row.vue | 1 + .../pipelines/components/stage.vue | 17 ++++ .../pipelines/pipeline_details_bundle.js | 30 ++----- .../pipelines/graph/action_component_spec.js | 83 ++++++++----------- spec/javascripts/pipelines/stage_spec.js | 49 +++++++++++ 10 files changed, 177 insertions(+), 119 deletions(-) diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue index fd3491c7fe0..11a8bcb0772 100644 --- a/app/assets/javascripts/pipelines/components/graph/action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue @@ -1,15 +1,27 @@ diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 8e7430bf69e..61cf7c1b665 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -143,10 +143,10 @@ export default { pipelineActionRequestComplete() { if (this.type === 'PIPELINES_TABLE') { // warn the table to update - eventHub.$emit('clickedDropdown'); + eventHub.$emit('refreshPipelinesTable'); } else { - // refresh the content - this.fetchJobs(); + // close the dropdown in mr widget + $(this.$refs.dropdown).dropdown('toggle'); } }, }, @@ -167,6 +167,7 @@ export default { id="stageDropdown" aria-haspopup="true" aria-expanded="false" + ref="dropdown" > { expect(component.$el.querySelector('svg')).toBeDefined(); }); - it('renders a loading icon while component is loading', done => { - component.isLoading = true; - - component.$nextTick() - .then(() => { - expect(component.$el.querySelector('.fa-spin')).not.toBeNull(); - }) - .then(done) - .catch(done.fail); - }); - describe('on click', () => { it('emits `pipelineActionRequestComplete` after a successfull request', done => { spyOn(component, '$emit'); component.$el.click(); - expect(component.isLoading).toEqual(true); component.$nextTick() .then(() => { diff --git a/spec/javascripts/pipelines/stage_spec.js b/spec/javascripts/pipelines/stage_spec.js index 2ba5ecf92e7..16f6db39d6a 100644 --- a/spec/javascripts/pipelines/stage_spec.js +++ b/spec/javascripts/pipelines/stage_spec.js @@ -111,7 +111,7 @@ describe('Pipelines stage component', () => { }); describe('within pipeline table', () => { - it('emits `clickedDropdown` event when `pipelineActionRequestComplete` is triggered', done => { + it('emits `refreshPipelinesTable` event when `pipelineActionRequestComplete` is triggered', done => { spyOn(eventHub, '$emit'); component.type = 'PIPELINES_TABLE'; @@ -121,34 +121,12 @@ describe('Pipelines stage component', () => { component.$el.querySelector('.js-ci-action').click(); component.$nextTick() .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown'); - - expect(eventHub.$emit).toHaveBeenCalledTimes(2); + expect(eventHub.$emit).toHaveBeenCalledWith('refreshPipelinesTable'); }) .then(done) .catch(done.fail); }, 0); }); }); - - describe('without a type', () => { - it('fetches dropdown content again', done => { - spyOn(component, 'fetchJobs').and.callThrough(); - - component.$el.querySelector('button').click(); - - expect(component.fetchJobs).toHaveBeenCalledTimes(1); - - setTimeout(() => { - component.$el.querySelector('.js-ci-action').click(); - component.$nextTick() - .then(() => { - expect(component.fetchJobs).toHaveBeenCalledTimes(2); - }) - .then(done) - .catch(done.fail); - }, 0); - }); - }); }); }); From cdcde75bb782951b27ab9db0d54a71db7c94d7cb Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 11 May 2018 23:06:08 +0800 Subject: [PATCH 046/145] Only setup db in the first checkout! --- .gitlab-ci.yml | 3 ++- scripts/prepare_build.sh | 6 +----- scripts/utils.sh | 8 ++++++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b1445feee58..690e26711be 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -234,6 +234,7 @@ stages: <<: *dedicated-no-docs-and-no-qa-pull-cache-job variables: CREATE_DB_USER: "true" + SETUP_DB: "false" script: - git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0 - git checkout -f FETCH_HEAD @@ -242,7 +243,7 @@ stages: - cp config/gitlab.yml.example config/gitlab.yml - bundle exec rake db:drop db:create db:schema:load db:seed_fu - date - - git checkout $CI_COMMIT_SHA + - git checkout -f $CI_COMMIT_SHA - bundle install $BUNDLE_INSTALL_FLAGS - date - . scripts/prepare_build.sh diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 206d62dbc78..01cb01ed812 100644 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -60,9 +60,5 @@ if [ "$CREATE_DB_USER" != "false" ]; then fi if [ "$SETUP_DB" != "false" ]; then - bundle exec rake db:drop db:create db:schema:load db:migrate - - if [ "$GITLAB_DATABASE" = "mysql" ]; then - bundle exec rake add_limits_mysql - fi + setup_db fi diff --git a/scripts/utils.sh b/scripts/utils.sh index 6faa701f0ce..08c33f3f67e 100644 --- a/scripts/utils.sh +++ b/scripts/utils.sh @@ -12,3 +12,11 @@ retry() { done return 1 } + +setup_db() { + bundle exec rake db:drop db:create db:schema:load db:migrate + + if [ "$GITLAB_DATABASE" = "mysql" ]; then + bundle exec rake add_limits_mysql + fi +} From 0ab6469187285368d9f64f9ec67dbbcfa3e5a901 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 15 May 2018 01:49:46 +0800 Subject: [PATCH 047/145] Grant privileges after database is created Never drop the database when granting privileges --- .gitlab-ci.yml | 3 +-- scripts/create_mysql_user.sh | 1 - scripts/create_postgres_user.sh | 4 +--- scripts/prepare_build.sh | 12 ++---------- scripts/utils.sh | 10 ++++++++++ 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 690e26711be..84d8e69b84e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -189,7 +189,7 @@ stages: <<: *dedicated-no-docs-and-no-qa-pull-cache-job <<: *use-pg variables: - CREATE_DB_USER: "true" + SETUP_DB: "false" script: # Manually clone gitlab-test and only seed this project in # db/fixtures/development/04_project.rb thanks to SIZE=1 below @@ -233,7 +233,6 @@ stages: .migration-paths: &migration-paths <<: *dedicated-no-docs-and-no-qa-pull-cache-job variables: - CREATE_DB_USER: "true" SETUP_DB: "false" script: - git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0 diff --git a/scripts/create_mysql_user.sh b/scripts/create_mysql_user.sh index 286b1325f1d..35f68c581f3 100644 --- a/scripts/create_mysql_user.sh +++ b/scripts/create_mysql_user.sh @@ -1,7 +1,6 @@ #!/bin/bash mysql --user=root --host=mysql < Date: Wed, 16 May 2018 18:55:22 +0200 Subject: [PATCH 048/145] Update gitlab.pot with new externalized strings --- locale/gitlab.pot | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 90cdfd0dd03..14228b18332 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -8,8 +8,8 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-05-15 15:05+0200\n" -"PO-Revision-Date: 2018-05-15 15:05+0200\n" +"POT-Creation-Date: 2018-05-16 18:52+0200\n" +"PO-Revision-Date: 2018-05-16 18:52+0200\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" @@ -1872,6 +1872,9 @@ msgstr "" msgid "Enable the Performance Bar for a given group." msgstr "" +msgid "Ends at (UTC)" +msgstr "" + msgid "Environments" msgstr "" @@ -3780,6 +3783,9 @@ msgstr "" msgid "Started" msgstr "" +msgid "Starts at (UTC)" +msgstr "" + msgid "Status" msgstr "" From c75ae23729771869591b6bc4f1528581d3bfe999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 16 May 2018 20:20:11 +0200 Subject: [PATCH 049/145] Add :weight as an allowed serializable field in app/controllers/boards/issues_controller.rb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/controllers/boards/issues_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb index 7d7ff217e5d..09e143c23e8 100644 --- a/app/controllers/boards/issues_controller.rb +++ b/app/controllers/boards/issues_controller.rb @@ -94,7 +94,7 @@ module Boards def serialize_as_json(resource) resource.as_json( - only: [:id, :iid, :project_id, :title, :confidential, :due_date, :relative_position], + only: [:id, :iid, :project_id, :title, :confidential, :due_date, :relative_position, :weight], labels: true, issue_endpoints: true, include_full_project_path: board.group_board?, From 6d81905fafb23f20520eb5ffa46c94b18ccc8686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 16 May 2018 20:26:22 +0200 Subject: [PATCH 050/145] Introduce a new Keys::DestroyService service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/controllers/profiles/keys_controller.rb | 2 +- app/services/keys/base_service.rb | 2 +- app/services/keys/destroy_service.rb | 12 ++++++++++++ spec/services/keys/destroy_service_spec.rb | 13 +++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 app/services/keys/destroy_service.rb create mode 100644 spec/services/keys/destroy_service_spec.rb diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index f0e5d2aa94e..12a6cd11f80 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -23,7 +23,7 @@ class Profiles::KeysController < Profiles::ApplicationController def destroy @key = current_user.keys.find(params[:id]) - @key.destroy + Keys::DestroyService.new(current_user).execute(@key) respond_to do |format| format.html { redirect_to profile_keys_url, status: 302 } diff --git a/app/services/keys/base_service.rb b/app/services/keys/base_service.rb index f78791932a7..df8e82f5f60 100644 --- a/app/services/keys/base_service.rb +++ b/app/services/keys/base_service.rb @@ -2,7 +2,7 @@ module Keys class BaseService attr_accessor :user, :params - def initialize(user, params) + def initialize(user, params = {}) @user, @params = user, params @ip_address = @params.delete(:ip_address) end diff --git a/app/services/keys/destroy_service.rb b/app/services/keys/destroy_service.rb new file mode 100644 index 00000000000..785cfa3a1d8 --- /dev/null +++ b/app/services/keys/destroy_service.rb @@ -0,0 +1,12 @@ +module Keys + class DestroyService < ::Keys::BaseService + def execute(key) + key.destroy if destroy_possible?(key) + end + + # overriden in EE::Keys::DestroyService + def destroy_possible?(key) + true + end + end +end diff --git a/spec/services/keys/destroy_service_spec.rb b/spec/services/keys/destroy_service_spec.rb new file mode 100644 index 00000000000..28ac72ddd42 --- /dev/null +++ b/spec/services/keys/destroy_service_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe Keys::DestroyService do + let(:user) { create(:user) } + + subject { described_class.new(user) } + + it 'destroys a key' do + key = create(:key) + + expect { subject.execute(key) }.to change(Key, :count).by(-1) + end +end From 539042a81e91e641fc93d80b1126e492f67b073f Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 16 May 2018 10:54:05 -0700 Subject: [PATCH 051/145] Remove unneccessary imports --- app/assets/stylesheets/pages/boards.scss | 2 -- app/assets/stylesheets/pages/issues.scss | 2 -- 2 files changed, 4 deletions(-) diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss index 011d38532b4..6bb40bae9ed 100644 --- a/app/assets/stylesheets/pages/boards.scss +++ b/app/assets/stylesheets/pages/boards.scss @@ -1,5 +1,3 @@ -@import './issues/issue_count_badge'; - [v-cloak] { display: none; } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index b9390450477..0d17b9bae7e 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -1,5 +1,3 @@ -@import "./issues/issue_count_badge"; - .issues-list { .issue { padding: 10px 0 10px $gl-padding; From 8d024ba79a2e2a2f2d34f4ee678b496f0fbe64f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 16 May 2018 20:39:29 +0200 Subject: [PATCH 052/145] Backport changes from EE to minimize the CE/EE diff in Projects::Settings::IntegrationsController MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .../projects/settings/integrations_controller.rb | 9 ++++++++- app/models/project.rb | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb index 1ff08cce8cb..d9fecfecc40 100644 --- a/app/controllers/projects/settings/integrations_controller.rb +++ b/app/controllers/projects/settings/integrations_controller.rb @@ -11,7 +11,14 @@ module Projects @hook = ProjectHook.new # Services - @services = @project.find_or_initialize_services + @services = @project.find_or_initialize_services(exceptions: service_exceptions) + end + + private + + # Returns a list of services that should be hidden from the list + def service_exceptions + @project.disabled_services.dup end end end diff --git a/app/models/project.rb b/app/models/project.rb index 534a0e630af..107ee5f9a7e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -998,7 +998,7 @@ class Project < ActiveRecord::Base available_services_names = Service.available_services_names - exceptions - available_services_names.map do |service_name| + available_services = available_services_names.map do |service_name| service = find_service(services, service_name) if service @@ -1015,6 +1015,14 @@ class Project < ActiveRecord::Base end end end + + available_services.reject do |service| + disabled_services.include?(service.to_param) + end + end + + def disabled_services + [] end def find_or_initialize_service(name) From 42ab6f8557505595f86604735d8805f879247da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 16 May 2018 21:23:43 +0200 Subject: [PATCH 053/145] Move attribute casting to #cached_attribute --- app/models/concerns/redis_cacheable.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index 4fdaaddeee7..d40df5ba2d4 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -8,16 +8,15 @@ module RedisCacheable def cached_attr_reader(*attributes) attributes.each do |attribute| define_method(attribute) do - cached_value = cached_attribute(attribute) - cached_value = cast_value_from_cache(attribute, cached_value) if cached_value - cached_value || read_attribute(attribute) + cached_attribute(attribute) || read_attribute(attribute) end end end end def cached_attribute(attribute) - (cached_attributes || {})[attribute] + cached_value = (cached_attributes || {})[attribute] + cast_value_from_cache(attribute, cached_value) if cached_value end def cache_attributes(values) From a4b0876b391f0717365cabd78cf9715b64649797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 16 May 2018 21:36:20 +0200 Subject: [PATCH 054/145] Add attribute check in cached getter --- app/models/concerns/redis_cacheable.rb | 2 ++ spec/models/concerns/redis_cacheable_spec.rb | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index d40df5ba2d4..a8d96f63d7a 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -8,6 +8,8 @@ module RedisCacheable def cached_attr_reader(*attributes) attributes.each do |attribute| define_method(attribute) do + raise ArgumentError, "Not a database attribute" unless self.has_attribute?(attribute) + cached_attribute(attribute) || read_attribute(attribute) end end diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 827629c3180..2da0f33e27b 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -10,6 +10,10 @@ describe RedisCacheable do def cast_value_from_cache(attribute, cached_value) cached_value end + + def has_attribute?(attribute) + attributes.has_key?(attribute) + end end end From 4e1bb1d1014237df79db6b3cc2beb24228a4b228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 16 May 2018 21:49:09 +0200 Subject: [PATCH 055/145] Move argument check to cached getter definition class method --- app/models/concerns/redis_cacheable.rb | 4 ++-- spec/models/concerns/redis_cacheable_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index a8d96f63d7a..bf046c0e333 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -7,9 +7,9 @@ module RedisCacheable class_methods do def cached_attr_reader(*attributes) attributes.each do |attribute| - define_method(attribute) do - raise ArgumentError, "Not a database attribute" unless self.has_attribute?(attribute) + raise ArgumentError, "Not a database attribute" unless self.attribute_names.include?(attribute.to_s) + define_method(attribute) do cached_attribute(attribute) || read_attribute(attribute) end end diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 2da0f33e27b..089ae080b0c 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -11,8 +11,8 @@ describe RedisCacheable do cached_value end - def has_attribute?(attribute) - attributes.has_key?(attribute) + def self.attribute_names + %w[name time] end end end From eeb955a66d2410d7117737ecd1b33b0cfe67327b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Wed, 16 May 2018 22:43:38 +0200 Subject: [PATCH 056/145] Revert "Move argument check to cached getter definition class method" This reverts commit 4e1bb1d1014237df79db6b3cc2beb24228a4b228. --- app/models/concerns/redis_cacheable.rb | 4 ++-- spec/models/concerns/redis_cacheable_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index bf046c0e333..a8d96f63d7a 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -7,9 +7,9 @@ module RedisCacheable class_methods do def cached_attr_reader(*attributes) attributes.each do |attribute| - raise ArgumentError, "Not a database attribute" unless self.attribute_names.include?(attribute.to_s) - define_method(attribute) do + raise ArgumentError, "Not a database attribute" unless self.has_attribute?(attribute) + cached_attribute(attribute) || read_attribute(attribute) end end diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 089ae080b0c..2da0f33e27b 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -11,8 +11,8 @@ describe RedisCacheable do cached_value end - def self.attribute_names - %w[name time] + def has_attribute?(attribute) + attributes.has_key?(attribute) end end end From f494f2711a5a19b751c50832d8ae15c01ce3c3ee Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Wed, 16 May 2018 15:58:20 -0500 Subject: [PATCH 057/145] Respect the inheritance chain between Ci::Build and CommitStatus Also moves the assertions were they belong --- app/presenters/ci/build_presenter.rb | 25 +------------------ app/presenters/commit_status_presenter.rb | 24 +++++++++++++++++- spec/models/commit_status_spec.rb | 7 ++---- spec/models/generic_commit_status_spec.rb | 6 +++++ spec/presenters/ci/build_presenter_spec.rb | 2 +- .../commit_status_presenter_spec.rb | 15 +++++++++++ 6 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 spec/presenters/commit_status_presenter_spec.rb diff --git a/app/presenters/ci/build_presenter.rb b/app/presenters/ci/build_presenter.rb index 4873d7ce662..e0aaa5cb736 100644 --- a/app/presenters/ci/build_presenter.rb +++ b/app/presenters/ci/build_presenter.rb @@ -1,16 +1,5 @@ module Ci - class BuildPresenter < Gitlab::View::Presenter::Delegated - CALLOUT_FAILURE_MESSAGES = { - unknown_failure: 'There is an unknown failure, please try again', - script_failure: 'There has been a script failure. Check the job log for more information', - api_failure: 'There has been an API failure, please try again', - stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again', - runner_system_failure: 'There has been a runner system failure, please try again', - missing_dependency_failure: 'There has been a missing dependency failure, check the job log for more information' - }.freeze - - presents :build - + class BuildPresenter < CommitStatusPresenter def erased_by_user? # Build can be erased through API, therefore it does not have # `erased_by` user assigned in that case. @@ -44,14 +33,6 @@ module Ci "#{subject.name} - #{detailed_status.status_tooltip}" end - def callout_failure_message - CALLOUT_FAILURE_MESSAGES[failure_reason.to_sym] - end - - def recoverable? - failed? && !unrecoverable? - end - private def tooltip_for_badge @@ -61,9 +42,5 @@ module Ci def detailed_status @detailed_status ||= subject.detailed_status(user) end - - def unrecoverable? - script_failure? || missing_dependency_failure? - end end end diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb index 028deaf235c..c7f7aa836bd 100644 --- a/app/presenters/commit_status_presenter.rb +++ b/app/presenters/commit_status_presenter.rb @@ -1,2 +1,24 @@ -class CommitStatusPresenter < Ci::BuildPresenter +class CommitStatusPresenter < Gitlab::View::Presenter::Delegated + CALLOUT_FAILURE_MESSAGES = { + unknown_failure: 'There is an unknown failure, please try again', + script_failure: 'There has been a script failure. Check the job log for more information', + api_failure: 'There has been an API failure, please try again', + stuck_or_timeout_failure: 'There has been a timeout failure or the job got stuck. Check your timeout limits or try again', + runner_system_failure: 'There has been a runner system failure, please try again', + missing_dependency_failure: 'There has been a missing dependency failure, check the job log for more information' + }.freeze + + presents :build + + def callout_failure_message + CALLOUT_FAILURE_MESSAGES[failure_reason.to_sym] + end + + def recoverable? + failed? && !unrecoverable? + end + + def unrecoverable? + script_failure? || missing_dependency_failure? + end end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index eddcf616b53..f3f2bc28d2c 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -567,11 +567,8 @@ describe CommitStatus do end describe '#present' do - let(:generic_commit_status) { create(:generic_commit_status) } + subject { commit_status.present } - it 'returns a presenter' do - expect(commit_status.present).to be_a(Ci::BuildPresenter) - expect(generic_commit_status.present).to be_a(Ci::BuildPresenter) - end + it { is_expected.to be_a(CommitStatusPresenter) } end end diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index 673049d1cc4..a3e68d2e646 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -78,4 +78,10 @@ describe GenericCommitStatus do it { is_expected.not_to be_nil } end end + + describe '#present' do + subject { generic_commit_status.present } + + it { is_expected.to be_a(GenericCommitStatusPresenter) } + end end diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb index 4bc005df2fc..efd175247b5 100644 --- a/spec/presenters/ci/build_presenter_spec.rb +++ b/spec/presenters/ci/build_presenter_spec.rb @@ -10,7 +10,7 @@ describe Ci::BuildPresenter do end it 'inherits from Gitlab::View::Presenter::Delegated' do - expect(described_class.superclass).to eq(Gitlab::View::Presenter::Delegated) + expect(described_class.ancestors).to include(Gitlab::View::Presenter::Delegated) end describe '#initialize' do diff --git a/spec/presenters/commit_status_presenter_spec.rb b/spec/presenters/commit_status_presenter_spec.rb new file mode 100644 index 00000000000..f81ee44e371 --- /dev/null +++ b/spec/presenters/commit_status_presenter_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe CommitStatusPresenter do + let(:project) { create(:project) } + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, pipeline: pipeline) } + + subject(:presenter) do + described_class.new(build) + end + + it 'inherits from Gitlab::View::Presenter::Delegated' do + expect(described_class.superclass).to eq(Gitlab::View::Presenter::Delegated) + end +end From 51f5ee33c448ee8a157ad12dac80482a11a7c72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 17 May 2018 01:12:37 +0200 Subject: [PATCH 058/145] Use Gitlab.rails5? for checking if on rails5 --- app/models/concerns/redis_cacheable.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index a8d96f63d7a..3d24f0dfdc1 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -45,10 +45,10 @@ module RedisCacheable end def cast_value_from_cache(attribute, value) - if self.class.column_for_attribute(attribute).respond_to?(:type_cast_from_database) - self.class.column_for_attribute(attribute).type_cast_from_database(value) - else + if Gitlab.rails5? self.class.type_for_attribute(attribute).cast(value) + else + self.class.column_for_attribute(attribute).type_cast_from_database(value) end end end From 94b209b32c4d38eb7a350c175aff551f789b7c6b Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 16 May 2018 16:17:55 +0200 Subject: [PATCH 059/145] Move git archives downloading to Gitaly --- .../unreleased/zj-workhorse-archive-mandatory.yml | 5 +++++ lib/gitlab/git/repository.rb | 4 ++-- lib/gitlab/workhorse.rb | 7 +------ spec/lib/gitlab/git/repository_spec.rb | 4 ---- spec/lib/gitlab/workhorse_spec.rb | 12 +----------- 5 files changed, 9 insertions(+), 23 deletions(-) create mode 100644 changelogs/unreleased/zj-workhorse-archive-mandatory.yml diff --git a/changelogs/unreleased/zj-workhorse-archive-mandatory.yml b/changelogs/unreleased/zj-workhorse-archive-mandatory.yml new file mode 100644 index 00000000000..3a4a351a2b9 --- /dev/null +++ b/changelogs/unreleased/zj-workhorse-archive-mandatory.yml @@ -0,0 +1,5 @@ +--- +title: Workhorse will use Gitaly to create archives +merge_request: +author: +type: other diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 061865a7acf..c7a755319dc 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -403,10 +403,10 @@ module Gitlab prefix = archive_prefix(ref, commit.id, append_sha: append_sha) { - 'RepoPath' => path, 'ArchivePrefix' => prefix, 'ArchivePath' => archive_file_path(storage_path, commit.id, prefix, format), - 'CommitId' => commit.id + 'CommitId' => commit.id, + 'GitalyRepository' => gitaly_repository.to_h } end diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 1f060de657d..e893e46ee86 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -65,12 +65,7 @@ module Gitlab params = repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format, append_sha: append_sha) raise "Repository or ref not found" if params.empty? - if Gitlab::GitalyClient.feature_enabled?(:workhorse_archive, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) - params.merge!( - 'GitalyServer' => gitaly_server_hash(repository), - 'GitalyRepository' => repository.gitaly_repository.to_h - ) - end + params['GitalyServer'] = gitaly_server_hash(repository) # If present DisableCache must be a Boolean. Otherwise workhorse ignores it. params['DisableCache'] = true if git_archive_cache_disabled? diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index fcb690d8aa3..740d12c443d 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -249,10 +249,6 @@ describe Gitlab::Git::Repository, seed_helper: true do subject(:metadata) { repository.archive_metadata(ref, storage_path, format, append_sha: append_sha) } - it 'sets RepoPath to the repository path' do - expect(metadata['RepoPath']).to eq(repository.path) - end - it 'sets CommitId to the commit SHA' do expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID) end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index e732b089d44..660671cefaf 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Workhorse do - let(:project) { create(:project, :repository) } + set(:project) { create(:project, :repository) } let(:repository) { project.repository } def decode_workhorse_header(array) @@ -55,16 +55,6 @@ describe Gitlab::Workhorse do end end - context 'when Gitaly workhorse_archive feature is disabled', :disable_gitaly do - it 'sets the header correctly' do - key, command, params = decode_workhorse_header(subject) - - expect(key).to eq('Gitlab-Workhorse-Send-Data') - expect(command).to eq('git-archive') - expect(params).to eq(base_params) - end - end - context "when the repository doesn't have an archive file path" do before do allow(project.repository).to receive(:archive_metadata).and_return(Hash.new) From 92b0cefb95408d7ae78eaaa8deaf26101b3b70bb Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 17 May 2018 08:47:00 +0100 Subject: [PATCH 060/145] fixed up spacing above & below lists fixed `no changes` spacing --- app/assets/stylesheets/pages/repo.scss | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index baef3a64be2..e52fa7e9b33 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -41,6 +41,7 @@ flex: 1; padding-left: $gl-padding; padding-right: $gl-padding; + padding-bottom: $grid-size; .file { cursor: pointer; @@ -523,7 +524,7 @@ .multi-file-commit-list { flex: 1; overflow: auto; - padding: $gl-padding 0; + padding: $grid-size 0; margin-left: -$grid-size; margin-right: -$grid-size; min-height: 60px; @@ -532,6 +533,11 @@ margin-left: 0; margin-right: 0; } + + &.help-block { + margin-left: 0; + right: 0; + } } .multi-file-commit-list-item { @@ -994,6 +1000,7 @@ .ide-tree-header { display: flex; align-items: center; + margin-bottom: 8px; padding: 12px 0; border-bottom: 1px solid $white-dark; From b1135b61e60654f5b3f1b68e18c40627b5fce786 Mon Sep 17 00:00:00 2001 From: "Balasankar \"Balu\" C" Date: Thu, 3 May 2018 12:37:28 +0530 Subject: [PATCH 061/145] Add doc about backup-restore in docker and k8s --- doc/raketasks/backup_restore.md | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 785cc32d590..77139c50d07 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -64,6 +64,13 @@ If you are running GitLab within a Docker container, you can run the backup from docker exec -t gitlab-rake gitlab:backup:create ``` +If you are using the gitlab-omnibus helm chart on a Kubernetes cluster, you can +run the backup task on the gitlab application pod using kubectl + +``` +kubectl exec -it gitlab-rake gitlab:backup:create +``` + Example output: ``` @@ -601,6 +608,34 @@ If there is a GitLab version mismatch between your backup tar file and the insta version of GitLab, the restore command will abort with an error. Install the [correct GitLab version](https://packages.gitlab.com/gitlab/) and try again. +### Restore for Docker image and gitlab-omnibus helm chart + +For GitLab installations using docker image or the gitlab-omnibus helm chart on +a Kubernetes cluster, restore task expects the restore directories to be empty. +However, with docker and Kubernetes volume mounts, some system level directories +may be created at the volume roots, like `lost+found` directory found in Linux +operating systems. These directories are usually owned by `root`, which can +cause access permission errors since the restore rake task runs as `git` user. +So, to restore a GitLab installation, users have to confirm the restore target +directories are empty. + +For both these installation types, the backup tarball has to be available in the +backup location (default location is `/var/opt/gitlab/backups`). + +For docker installations, the restore task can be run from host using the +command + +``` +docker exec -it gitlab-rake gitlab:backup:restore +``` + +Similarly, for gitlab-omnibus helm chart, the restore task can be run on the +gitlab application pod using kubectl + +``` +kubectl exec -it gitlab-rake gitlab:backup:restore +``` + ## Alternative backup strategies If your GitLab server contains a lot of Git repository data you may find the GitLab backup script to be too slow. From 068186555cb85e85bbfe04afe858fb3eb4801207 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 17 May 2018 09:45:43 +0100 Subject: [PATCH 062/145] Update method name --- .../javascripts/environments/mixins/environments_mixin.js | 6 +++--- .../environments/services/environments_service.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index c8745e35802..a7a79dbca70 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -66,7 +66,7 @@ export default { updateContent(parameters) { this.updateInternalState(parameters); // fetch new data - return this.service.get(this.requestData) + return this.service.fetchEnvironments(this.requestData) .then(response => this.successCallback(response)) .then(() => { // restart polling @@ -101,7 +101,7 @@ export default { fetchEnvironments() { this.isLoading = true; - return this.service.get(this.requestData) + return this.service.fetchEnvironments(this.requestData) .then(this.successCallback) .catch(this.errorCallback); }, @@ -137,7 +137,7 @@ export default { this.poll = new Poll({ resource: this.service, - method: 'get', + method: 'fetchEnvironments', data: this.requestData, successCallback: this.successCallback, errorCallback: this.errorCallback, diff --git a/app/assets/javascripts/environments/services/environments_service.js b/app/assets/javascripts/environments/services/environments_service.js index 22563fad8d2..3b121551aca 100644 --- a/app/assets/javascripts/environments/services/environments_service.js +++ b/app/assets/javascripts/environments/services/environments_service.js @@ -6,7 +6,7 @@ export default class EnvironmentsService { this.folderResults = 3; } - get(options = {}) { + fetchEnvironments(options = {}) { const { scope, page } = options; return axios.get(this.environmentsEndpoint, { params: { scope, page } }); } From 01275667e323d4702cc396f6f756305b06cba726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=99=88=20=20jacopo=20beschi=20=F0=9F=99=89?= Date: Thu, 17 May 2018 09:19:47 +0000 Subject: [PATCH 063/145] Resolve "Opening Project with invite but without accepting leads to 404 error page" --- .../concerns/accepts_pending_invitations.rb | 15 +++ app/controllers/confirmations_controller.rb | 4 + app/controllers/registrations_controller.rb | 4 +- app/models/user.rb | 10 ++ .../notify/member_invited_email.html.haml | 2 +- .../unreleased/42531-open-invite-404.yml | 5 + spec/features/invites_spec.rb | 112 ++++++++++++++++-- spec/mailers/notify_spec.rb | 2 +- spec/models/user_spec.rb | 18 +++ 9 files changed, 157 insertions(+), 15 deletions(-) create mode 100644 app/controllers/concerns/accepts_pending_invitations.rb create mode 100644 changelogs/unreleased/42531-open-invite-404.yml diff --git a/app/controllers/concerns/accepts_pending_invitations.rb b/app/controllers/concerns/accepts_pending_invitations.rb new file mode 100644 index 00000000000..6e8aef52b52 --- /dev/null +++ b/app/controllers/concerns/accepts_pending_invitations.rb @@ -0,0 +1,15 @@ +module AcceptsPendingInvitations + extend ActiveSupport::Concern + + def accept_pending_invitations + return unless resource.active_for_authentication? + + clear_stored_location_for_resource if resource.accept_pending_invitations!.any? + end + + def clear_stored_location_for_resource + session_key = stored_location_key_for(resource) + + session.delete(session_key) + end +end diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb index 6d9c38d9581..7bc46a6ccc0 100644 --- a/app/controllers/confirmations_controller.rb +++ b/app/controllers/confirmations_controller.rb @@ -1,4 +1,6 @@ class ConfirmationsController < Devise::ConfirmationsController + include AcceptsPendingInvitations + def almost_there flash[:notice] = nil render layout: "devise_empty" @@ -11,6 +13,8 @@ class ConfirmationsController < Devise::ConfirmationsController end def after_confirmation_path_for(resource_name, resource) + accept_pending_invitations + # incoming resource can either be a :user or an :email if signed_in?(:user) after_sign_in(resource) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 1848c806c41..f5a222b3a48 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -1,5 +1,6 @@ class RegistrationsController < Devise::RegistrationsController include Recaptcha::Verify + include AcceptsPendingInvitations before_action :whitelist_query_limiting, only: [:destroy] @@ -16,6 +17,7 @@ class RegistrationsController < Devise::RegistrationsController end if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha + accept_pending_invitations super else flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' @@ -60,7 +62,7 @@ class RegistrationsController < Devise::RegistrationsController def after_sign_up_path_for(user) Gitlab::AppLogger.info("User Created: username=#{user.username} email=#{user.email} ip=#{request.remote_ip} confirmed:#{user.confirmed?}") - user.confirmed? ? dashboard_projects_path : users_almost_there_path + user.confirmed? ? stored_location_for(user) || dashboard_projects_path : users_almost_there_path end def after_inactive_sign_up_path_for(resource) diff --git a/app/models/user.rb b/app/models/user.rb index 474fde36c02..8ef3c3ceff0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -860,6 +860,16 @@ class User < ActiveRecord::Base confirmed? && !temp_oauth_email? end + def accept_pending_invitations! + pending_invitations.select do |member| + member.accept_invite!(self) + end + end + + def pending_invitations + Member.where(invite_email: verified_emails).invite + end + def all_emails all_emails = [] all_emails << email unless temp_oauth_email? diff --git a/app/views/notify/member_invited_email.html.haml b/app/views/notify/member_invited_email.html.haml index b8b75da3f2f..6730172242b 100644 --- a/app/views/notify/member_invited_email.html.haml +++ b/app/views/notify/member_invited_email.html.haml @@ -4,7 +4,7 @@ by = link_to member.created_by.name, user_url(member.created_by) to join the - = link_to member_source.human_name, member_source.web_url + = link_to member_source.human_name, member_source.public? ? member_source.web_url : invite_url(@token) #{member_source.model_name.singular} as #{member.human_access}. %p diff --git a/changelogs/unreleased/42531-open-invite-404.yml b/changelogs/unreleased/42531-open-invite-404.yml new file mode 100644 index 00000000000..73729f4a929 --- /dev/null +++ b/changelogs/unreleased/42531-open-invite-404.yml @@ -0,0 +1,5 @@ +--- +title: Automatically accepts project/group invite by email after user signup +merge_request: 17634 +author: Jacopo Beschi @jacopo-beschi +type: changed diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index e4be6193b8b..a986ddc4abc 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -5,18 +5,41 @@ describe 'Invites' do let(:owner) { create(:user, name: 'John Doe') } let(:group) { create(:group, name: 'Owned') } let(:project) { create(:project, :repository, namespace: group) } - let(:invite) { group.group_members.invite.last } + let(:group_invite) { group.group_members.invite.last } before do project.add_master(owner) group.add_user(owner, Gitlab::Access::OWNER) group.add_developer('user@example.com', owner) - invite.generate_invite_token! + group_invite.generate_invite_token! + end + + def confirm_email_and_sign_in(new_user) + new_user_token = User.find_by_email(new_user.email).confirmation_token + + visit user_confirmation_path(confirmation_token: new_user_token) + fill_in_sign_in_form(new_user) + end + + def fill_in_sign_up_form(new_user) + fill_in 'new_user_name', with: new_user.name + fill_in 'new_user_username', with: new_user.username + fill_in 'new_user_email', with: new_user.email + fill_in 'new_user_email_confirmation', with: new_user.email + fill_in 'new_user_password', with: new_user.password + click_button "Register" + end + + def fill_in_sign_in_form(user) + fill_in 'user_login', with: user.email + fill_in 'user_password', with: user.password + check 'user_remember_me' + click_button 'Sign in' end context 'when signed out' do before do - visit invite_path(invite.raw_invite_token) + visit invite_path(group_invite.raw_invite_token) end it 'renders sign in page with sign in notice' do @@ -25,12 +48,9 @@ describe 'Invites' do end it 'sign in and redirects to invitation page' do - fill_in 'user_login', with: user.email - fill_in 'user_password', with: user.password - check 'user_remember_me' - click_button 'Sign in' + fill_in_sign_in_form(user) - expect(current_path).to eq(invite_path(invite.raw_invite_token)) + expect(current_path).to eq(invite_path(group_invite.raw_invite_token)) expect(page).to have_content( 'You have been invited by John Doe to join group Owned as Developer.' ) @@ -45,7 +65,7 @@ describe 'Invites' do end it 'shows message user already a member' do - visit invite_path(invite.raw_invite_token) + visit invite_path(group_invite.raw_invite_token) expect(page).to have_content('However, you are already a member of this group.') end end @@ -53,7 +73,7 @@ describe 'Invites' do describe 'accepting the invitation' do before do sign_in(user) - visit invite_path(invite.raw_invite_token) + visit invite_path(group_invite.raw_invite_token) end it 'grants access and redirects to group page' do @@ -69,7 +89,7 @@ describe 'Invites' do context 'when signed in' do before do sign_in(user) - visit invite_path(invite.raw_invite_token) + visit invite_path(group_invite.raw_invite_token) end it 'declines application and redirects to dashboard' do @@ -83,7 +103,7 @@ describe 'Invites' do context 'when signed out' do before do - visit decline_invite_path(invite.raw_invite_token) + visit decline_invite_path(group_invite.raw_invite_token) end it 'declines application and redirects to sign in page' do @@ -94,4 +114,72 @@ describe 'Invites' do end end end + + describe 'invite an user using their email address' do + let(:new_user) { build_stubbed(:user) } + let(:invite_email) { new_user.email } + let(:group_invite) { create(:group_member, :invited, group: group, invite_email: invite_email) } + let!(:project_invite) { create(:project_member, :invited, project: project, invite_email: invite_email) } + + before do + stub_application_setting(send_user_confirmation_email: send_email_confirmation) + visit invite_path(group_invite.raw_invite_token) + end + + context 'email confirmation disabled' do + let(:send_email_confirmation) { false } + + it 'signs up and redirects to the dashboard page with all the projects/groups invitations automatically accepted' do + fill_in_sign_up_form(new_user) + + expect(current_path).to eq(dashboard_projects_path) + expect(page).to have_content(project.full_name) + visit group_path(group) + expect(page).to have_content(group.full_name) + end + + context 'the user sign-up using a different email address' do + let(:invite_email) { build_stubbed(:user).email } + + it 'signs up and redirects to the invitation page' do + fill_in_sign_up_form(new_user) + + expect(current_path).to eq(invite_path(group_invite.raw_invite_token)) + end + end + end + + context 'email confirmation enabled' do + let(:send_email_confirmation) { true } + + it 'signs up and redirects to root page with all the project/groups invitation automatically accepted' do + fill_in_sign_up_form(new_user) + confirm_email_and_sign_in(new_user) + + expect(current_path).to eq(root_path) + expect(page).to have_content(project.full_name) + visit group_path(group) + expect(page).to have_content(group.full_name) + end + + it "doesn't accept invitations until the user confirm his email" do + fill_in_sign_up_form(new_user) + sign_in(owner) + + visit project_project_members_path(project) + expect(page).to have_content 'Invited' + end + + context 'the user sign-up using a different email address' do + let(:invite_email) { build_stubbed(:user).email } + + it 'signs up and redirects to the invitation page' do + fill_in_sign_up_form(new_user) + confirm_email_and_sign_in(new_user) + + expect(current_path).to eq(invite_path(group_invite.raw_invite_token)) + end + end + end + end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 84ddbbbf2ee..8a52c151cc4 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -594,7 +594,7 @@ describe Notify do it 'contains all the useful information' do is_expected.to have_subject "Invitation to join the #{project.full_name} project" is_expected.to have_html_escaped_body_text project.full_name - is_expected.to have_body_text project.web_url + is_expected.to have_body_text project.full_name is_expected.to have_body_text project_member.human_access is_expected.to have_body_text project_member.invite_token end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8d3ddd1f87d..684fa030baf 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1223,6 +1223,24 @@ describe User do end end + describe '#accept_pending_invitations!' do + let(:user) { create(:user, email: 'user@email.com') } + let!(:project_member_invite) { create(:project_member, :invited, invite_email: user.email) } + let!(:group_member_invite) { create(:group_member, :invited, invite_email: user.email) } + let!(:external_project_member_invite) { create(:project_member, :invited, invite_email: 'external@email.com') } + let!(:external_group_member_invite) { create(:group_member, :invited, invite_email: 'external@email.com') } + + it 'accepts all the user members pending invitations and returns the accepted_members' do + accepted_members = user.accept_pending_invitations! + + expect(accepted_members).to match_array([project_member_invite, group_member_invite]) + expect(group_member_invite.reload).not_to be_invite + expect(project_member_invite.reload).not_to be_invite + expect(external_project_member_invite.reload).to be_invite + expect(external_group_member_invite.reload).to be_invite + end + end + describe '#all_emails' do let(:user) { create(:user) } From 1bf74bfd255372f17f9135ba0ce6ad9111c845d9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 17 May 2018 10:25:11 +0100 Subject: [PATCH 064/145] Moves string to a constant --- .../javascripts/pipelines/components/pipelines_table_row.vue | 4 +++- app/assets/javascripts/pipelines/components/stage.vue | 3 ++- app/assets/javascripts/pipelines/constants.js | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index 41b5e418dd1..fdf8c04207f 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -9,6 +9,7 @@ import CommitComponent from '../../vue_shared/components/commit.vue'; import LoadingButton from '../../vue_shared/components/loading_button.vue'; import Icon from '../../vue_shared/components/icon.vue'; + import { PIPELINES_TABLE } from '../constants.js' /** * Pipeline table row. @@ -46,6 +47,7 @@ required: true, }, }, + pipelinesTable: PIPELINES_TABLE, data() { return { isRetrying: false, @@ -297,7 +299,7 @@ v-for="(stage, index) in pipeline.details.stages" :key="index"> diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 61cf7c1b665..1e6392cf60a 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -21,6 +21,7 @@ import Icon from '../../vue_shared/components/icon.vue'; import LoadingIcon from '../../vue_shared/components/loading_icon.vue'; import JobComponent from './graph/job_component.vue'; import tooltip from '../../vue_shared/directives/tooltip'; +import { PIPELINES_TABLE } from '../constants.js' export default { components: { @@ -141,7 +142,7 @@ export default { }, pipelineActionRequestComplete() { - if (this.type === 'PIPELINES_TABLE') { + if (this.type === PIPELINES_TABLE) { // warn the table to update eventHub.$emit('refreshPipelinesTable'); } else { diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js index b384c7500e7..eaa11a84cb9 100644 --- a/app/assets/javascripts/pipelines/constants.js +++ b/app/assets/javascripts/pipelines/constants.js @@ -1,2 +1,2 @@ -// eslint-disable-next-line import/prefer-default-export export const CANCEL_REQUEST = 'CANCEL_REQUEST'; +export const PIPELINES_TABLE = 'PIPELINES_TABLE'; From a63ada5e77c4d817b05552d066dc6004003aaf98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 17 May 2018 11:57:23 +0200 Subject: [PATCH 065/145] Include class name and argument name in argument error --- app/models/concerns/redis_cacheable.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index 3d24f0dfdc1..b5425295130 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -8,7 +8,9 @@ module RedisCacheable def cached_attr_reader(*attributes) attributes.each do |attribute| define_method(attribute) do - raise ArgumentError, "Not a database attribute" unless self.has_attribute?(attribute) + unless self.has_attribute?(attribute) + raise ArgumentError, "`cached_attr_reader` requires the #{self.class.name}\##{attribute} attribute to have a database column" + end cached_attribute(attribute) || read_attribute(attribute) end From 0ce63efe966840edb6e6184cf1abcef272a24dfc Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 17 May 2018 12:29:47 +0200 Subject: [PATCH 066/145] Add extended /regexp/ scheme support to untrusted regexp --- lib/gitlab/untrusted_regexp.rb | 23 +++++++++++ spec/lib/gitlab/untrusted_regexp_spec.rb | 49 +++++++++++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb index ce1cf737663..70d1a7c6535 100644 --- a/lib/gitlab/untrusted_regexp.rb +++ b/lib/gitlab/untrusted_regexp.rb @@ -9,6 +9,8 @@ module Gitlab # there is a strict limit on total execution time. See the RE2 documentation # at https://github.com/google/re2/wiki/Syntax for more details. class UntrustedRegexp + require_dependency 're2' + delegate :===, :source, to: :regexp def initialize(pattern, multiline: false) @@ -52,6 +54,27 @@ module Gitlab Regexp.new(pattern) end + def self.valid?(pattern) + self.fabricate(pattern) + rescue RegexpError + false + end + + def self.fabricate(pattern) + matches = pattern.match(%r{^/(?.+)/(?[ismU]*)$}) + + if matches + expression = matches[:regexp] + flags = matches[:flags] + + expression.prepend("(?#{flags})") if flags.present? + + self.new(expression, multiline: false) + else + self.new(pattern, multiline: false) + end + end + private attr_reader :regexp diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb index 0ee7fa1e570..4bca320ac2c 100644 --- a/spec/lib/gitlab/untrusted_regexp_spec.rb +++ b/spec/lib/gitlab/untrusted_regexp_spec.rb @@ -1,6 +1,53 @@ -require 'spec_helper' +require 'fast_spec_helper' +require 'support/shared_examples/malicious_regexp_shared_examples' describe Gitlab::UntrustedRegexp do + describe '.valid?' do + it 'returns true if regexp is valid' do + end + + it 'returns true if regexp is invalid' do + end + end + + describe '.fabricate' do + context 'when regexp is using /regexp/ scheme with flags' do + it 'fabricates regexp with a single flag' do + regexp = described_class.fabricate('/something/i') + + expect(regexp).to eq described_class.new('(?i)something') + expect(regexp.scan('SOMETHING')).to be_one + end + + it 'fabricates regexp with multiple flags' do + regexp = described_class.fabricate('/something/im') + + expect(regexp).to eq described_class.new('(?im)something') + end + + it 'fabricates regexp without flags' do + regexp = described_class.fabricate('/something/') + + expect(regexp).to eq described_class.new('something') + end + end + + context 'when regexp is not plain pattern' do + it 'fabricates regexp without flags' do + regexp = described_class.fabricate('something') + + expect(regexp).to eq described_class.new('something') + end + end + + context 'when regexp is invalid' do + it 'raises an error' do + expect { described_class.fabricate('/some ( thing/') } + .to raise_error(RegexpError) + end + end + end + describe '#initialize' do subject { described_class.new(pattern) } From a1f1e08670a7f8bd5499e16c778be16106210a44 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 17 May 2018 12:35:20 +0200 Subject: [PATCH 067/145] Add anti-corruption layer above expressions pattern matching --- .../ci/pipeline/expression/lexeme/pattern.rb | 4 +-- .../expression/lexeme/pattern_spec.rb | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb index 62927441035..53fb5f769d8 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -6,14 +6,14 @@ module Gitlab require_dependency 're2' class Pattern < Lexeme::Value - PATTERN = %r{/(?.+)/}.freeze + PATTERN = %r{^(?/.+/[ismU]*)$}.freeze def initialize(regexp) @value = regexp end def evaluate(variables = {}) - Gitlab::UntrustedRegexp.new(@value) + Gitlab::UntrustedRegexp.fabricate(@value) rescue RegexpError raise Expression::RuntimeError, 'Invalid regular expression!' end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index a14a28056d8..6435ee5c915 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -42,6 +42,34 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do expect(token).to be_nil end + + it 'support single flag' do + scanner = StringScanner.new('/pattern/i') + + token = described_class.scan(scanner) + + expect(token).not_to be_nil + expect(token.build.evaluate) + .to eq Gitlab::UntrustedRegexp.new('(?i)pattern') + end + + it 'support multiple flags' do + scanner = StringScanner.new('/pattern/im') + + token = described_class.scan(scanner) + + expect(token).not_to be_nil + expect(token.build.evaluate) + .to eq Gitlab::UntrustedRegexp.new('(?im)pattern') + end + + it 'does not support arbitrary flags' do + scanner = StringScanner.new('/pattern/x') + + token = described_class.scan(scanner) + + expect(token).to be_nil + end end describe '#evaluate' do From 9e61d26c35acd6e60ac0fb0df711dbfdfda7c448 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 17 May 2018 10:25:11 +0100 Subject: [PATCH 068/145] Fix eslint --- .../javascripts/pipelines/components/pipelines_table_row.vue | 2 +- app/assets/javascripts/pipelines/components/stage.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue index fdf8c04207f..0f671ceea21 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue @@ -9,7 +9,7 @@ import CommitComponent from '../../vue_shared/components/commit.vue'; import LoadingButton from '../../vue_shared/components/loading_button.vue'; import Icon from '../../vue_shared/components/icon.vue'; - import { PIPELINES_TABLE } from '../constants.js' + import { PIPELINES_TABLE } from '../constants'; /** * Pipeline table row. diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 1e6392cf60a..f9769815796 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -21,7 +21,7 @@ import Icon from '../../vue_shared/components/icon.vue'; import LoadingIcon from '../../vue_shared/components/loading_icon.vue'; import JobComponent from './graph/job_component.vue'; import tooltip from '../../vue_shared/directives/tooltip'; -import { PIPELINES_TABLE } from '../constants.js' +import { PIPELINES_TABLE } from '../constants'; export default { components: { From 8b3e21b66b734b38e88f63727ee77b978ea21bfc Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 17 May 2018 12:44:46 +0200 Subject: [PATCH 069/145] Add variables expression pattern validation support --- lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb | 4 ++++ spec/lib/gitlab/ci/config/entry/policy_spec.rb | 10 +++++++++- .../ci/pipeline/expression/lexeme/pattern_spec.rb | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb index 53fb5f769d8..70a221010f3 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -10,6 +10,10 @@ module Gitlab def initialize(regexp) @value = regexp + + unless Gitlab::UntrustedRegexp.valid?(@value) + raise Lexer::SyntaxError, 'Invalid regular expression!' + end end def evaluate(variables = {}) diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb index 08718c382b9..83d39b82068 100644 --- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb @@ -111,7 +111,15 @@ describe Gitlab::Ci::Config::Entry::Policy do context 'when specifying invalid variables expressions token' do let(:config) { { variables: ['$MY_VAR == 123'] } } - it 'reports an error about invalid statement' do + it 'reports an error about invalid expression' do + expect(entry.errors).to include /invalid expression syntax/ + end + end + + context 'when using invalid variables expressions regexp' do + let(:config) { { variables: ['$MY_VAR =~ /some ( thing/'] } } + + it 'reports an error about invalid expression' do expect(entry.errors).to include /invalid expression syntax/ end end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index 6435ee5c915..c63c38b1dbc 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -6,6 +6,11 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do expect(described_class.build('/.*/')) .to be_a(described_class) end + + it 'raises an error if pattern is invalid' do + expect { described_class.build('/ some ( thin/i') } + .to raise_error(Gitlab::Ci::Pipeline::Expression::Lexer::SyntaxError) + end end describe '.type' do @@ -80,6 +85,8 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do end it 'raises error if evaluated regexp is not valid' do + allow(Gitlab::UntrustedRegexp).to receive(:valid?).and_return(true) + regexp = described_class.new('invalid ( .*') expect { regexp.evaluate } From c808de25515f9c977270a0663796ce34d1c81fd1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 17 May 2018 12:47:41 +0200 Subject: [PATCH 070/145] Update variables expressions pattern matching docs --- doc/ci/variables/README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 58acc030aee..f66b2b374ba 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -537,11 +537,8 @@ Below you can find supported syntax reference: It is possible perform pattern matching against a variable and regular expression. Expression like this evaluates to truth if matches are found. - Pattern matching is case-sensitive by default. Prepend `(?i)` to a - match-group to make a pattern case-insensitive. - - Under the hood we are using [RE2 library][re2-library], see - [syntax documentation][re2-syntax] for reference. + Pattern matching is case-sensitive by default. Use `i` flag modifier, like + `/pattern/i` to make a pattern case-insensitive. ### Unsupported predefined variables @@ -583,5 +580,3 @@ These variables are also not supported in a context of a [builds-policies]: ../yaml/README.md#only-and-except-complex [dynamic-environments]: ../environments.md#dynamic-environments [gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token -[re2-library]: https://github.com/google/re2 -[re2-syntax]: https://github.com/google/re2/wiki/Syntax From 61d55b56ab4ee7d11484eeea6fabb2412af6578f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 17 May 2018 12:55:19 +0200 Subject: [PATCH 071/145] Update variables expressions statement specs --- spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index 1ceb373e19c..11e73294f18 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -100,7 +100,7 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do "$PRESENT_VARIABLE =~ /^var.*/" | false "$EMPTY_VARIABLE =~ /var.*/" | false "$UNDEFINED_VARIABLE =~ /var.*/" | false - "$PRESENT_VARIABLE =~ /(?i)VAR.*/" | true + "$PRESENT_VARIABLE =~ /VAR.*/i" | true end with_them do From 13298bb3d1f1d8f10be5568d1617348e6ec0c0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 17 May 2018 13:06:23 +0200 Subject: [PATCH 072/145] Add test for #cast_value_from_cache --- spec/models/concerns/redis_cacheable_spec.rb | 36 ++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 2da0f33e27b..066021a32cd 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -83,4 +83,40 @@ describe RedisCacheable do expect(instance.name).to eq('new_value') end end + + describe '#cast_value_from_cache' do + let(:instance) { Ci::Runner.new } + + subject { instance.__send__(:cast_value_from_cache, attribute, value) } + + shared_context 'runner contacted_at context' do + let(:attribute) { :contacted_at } + let(:value) { '2018-05-07 13:53:08 UTC' } + end + + context 'on rails5' do + include_context 'runner contacted_at context' + + before do + allow(Gitlab).to receive(:rails5?).and_return(true) + end + + it 'converts cache string to appropriate type' do + pending 'Migration to rails5' + expect(subject).to be_an_instance_of(ActiveSupport::TimeWithZone) + end + end + + context 'not on rails5' do + include_context 'runner contacted_at context' + + before do + allow(Gitlab).to receive(:rails5?).and_return(false) + end + + it 'converts cache string to appropriate type' do + expect(subject).to be_an_instance_of(ActiveSupport::TimeWithZone) + end + end + end end From 17c4e53e80af6c652f427733dc7dce4b7f88c8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Thu, 17 May 2018 13:23:57 +0200 Subject: [PATCH 073/145] Remove rails5 specific tests for #cast_value_from_cache --- spec/models/concerns/redis_cacheable_spec.rb | 26 ++------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/spec/models/concerns/redis_cacheable_spec.rb b/spec/models/concerns/redis_cacheable_spec.rb index 066021a32cd..23c6c6233e9 100644 --- a/spec/models/concerns/redis_cacheable_spec.rb +++ b/spec/models/concerns/redis_cacheable_spec.rb @@ -85,34 +85,12 @@ describe RedisCacheable do end describe '#cast_value_from_cache' do - let(:instance) { Ci::Runner.new } - subject { instance.__send__(:cast_value_from_cache, attribute, value) } - shared_context 'runner contacted_at context' do + context 'with runner contacted_at' do + let(:instance) { Ci::Runner.new } let(:attribute) { :contacted_at } let(:value) { '2018-05-07 13:53:08 UTC' } - end - - context 'on rails5' do - include_context 'runner contacted_at context' - - before do - allow(Gitlab).to receive(:rails5?).and_return(true) - end - - it 'converts cache string to appropriate type' do - pending 'Migration to rails5' - expect(subject).to be_an_instance_of(ActiveSupport::TimeWithZone) - end - end - - context 'not on rails5' do - include_context 'runner contacted_at context' - - before do - allow(Gitlab).to receive(:rails5?).and_return(false) - end it 'converts cache string to appropriate type' do expect(subject).to be_an_instance_of(ActiveSupport::TimeWithZone) From 70985aa19b389c2ee8234edfbb516b5403a7bfcf Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 17 Apr 2018 15:13:38 +0200 Subject: [PATCH 074/145] Limit the number of pipelines to count When displaying the project pipelines dashboard we display a few tabs for different pipeline states. For every such tab we count the number of pipelines that belong to it. For large projects such as GitLab CE this means having to count over 80 000 rows, which can easily take between 70 and 100 milliseconds per query. To improve this we apply a technique we already use for search results: we limit the number of rows to count. The current limit is 1000, which means that if more than 1000 rows are present for a state we will show "1000+" instead of the exact number. The SQL queries used for this perform much better than a regular COUNT, even when a project has a lot of pipelines. Prior to these changes we would end up running a query like this: SELECT COUNT(*) FROM ci_pipelines WHERE project_id = 13083 AND status IN ('success', 'failed', 'canceled') This would produce a plan along the lines of the following: Aggregate (cost=3147.55..3147.56 rows=1 width=8) (actual time=501.413..501.413 rows=1 loops=1) Buffers: shared hit=17116 read=861 dirtied=2 -> Index Only Scan using index_ci_pipelines_on_project_id_and_ref_and_status_and_id on ci_pipelines (cost=0.56..2984.14 rows=65364 width=0) (actual time=0.095..490.263 rows=80388 loops=1) Index Cond: (project_id = 13083) Filter: ((status)::text = ANY ('{success,failed,canceled}'::text[])) Rows Removed by Filter: 2894 Heap Fetches: 353 Buffers: shared hit=17116 read=861 dirtied=2 Planning time: 1.409 ms Execution time: 501.519 ms Using the LIMIT count technique we instead run the following query: SELECT COUNT(*) FROM ( SELECT 1 FROM ci_pipelines WHERE project_id = 13083 AND status IN ('success', 'failed', 'canceled') LIMIT 1001 ) for_count This query produces the following plan: Aggregate (cost=58.77..58.78 rows=1 width=8) (actual time=1.726..1.727 rows=1 loops=1) Buffers: shared hit=169 read=15 -> Limit (cost=0.56..46.25 rows=1001 width=4) (actual time=0.164..1.570 rows=1001 loops=1) Buffers: shared hit=169 read=15 -> Index Only Scan using index_ci_pipelines_on_project_id_and_ref_and_status_and_id on ci_pipelines (cost=0.56..2984.14 rows=65364 width=4) (actual time=0.162..1.426 rows=1001 loops=1) Index Cond: (project_id = 13083) Filter: ((status)::text = ANY ('{success,failed,canceled}'::text[])) Rows Removed by Filter: 9 Heap Fetches: 10 Buffers: shared hit=169 read=15 Planning time: 1.832 ms Execution time: 1.821 ms While this query still uses a Filter for the "status" field the number of rows that it may end up filtering (at most 1001) is small enough that an additional index does not appear to be necessary at this time. See https://gitlab.com/gitlab-org/gitlab-ce/issues/43132#note_68659234 for more information. --- .../projects/pipelines_controller.rb | 21 +++++++++---------- .../projects/pipelines_controller_spec.rb | 8 +++---- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index f7417a6a5aa..329aaafb76d 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -18,17 +18,10 @@ class Projects::PipelinesController < Projects::ApplicationController .page(params[:page]) .per(30) - @running_count = PipelinesFinder - .new(project, scope: 'running').execute.count - - @pending_count = PipelinesFinder - .new(project, scope: 'pending').execute.count - - @finished_count = PipelinesFinder - .new(project, scope: 'finished').execute.count - - @pipelines_count = PipelinesFinder - .new(project).execute.count + @running_count = limited_pipelines_count(project, 'running') + @pending_count = limited_pipelines_count(project, 'pending') + @finished_count = limited_pipelines_count(project, 'finished') + @pipelines_count = limited_pipelines_count(project) @pipelines.map(&:commit) # List commits for batch loading @@ -185,4 +178,10 @@ class Projects::PipelinesController < Projects::ApplicationController def authorize_update_pipeline! return access_denied! unless can?(current_user, :update_pipeline, @pipeline) end + + def limited_pipelines_count(project, scope = nil) + finder = PipelinesFinder.new(project, scope: scope) + + view_context.limited_counter_with_delimiter(finder.execute) + end end diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index a451bbb97b6..c22e455ea2d 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -35,10 +35,10 @@ describe Projects::PipelinesController do expect(json_response).to include('pipelines') expect(json_response['pipelines'].count).to eq 4 - expect(json_response['count']['all']).to eq 4 - expect(json_response['count']['running']).to eq 1 - expect(json_response['count']['pending']).to eq 1 - expect(json_response['count']['finished']).to eq 1 + expect(json_response['count']['all']).to eq '4' + expect(json_response['count']['running']).to eq '1' + expect(json_response['count']['pending']).to eq '1' + expect(json_response['count']['finished']).to eq '1' end context 'when performing gitaly calls', :request_store do From 19428e800895ba20eacb3357285acef8d69f6d8c Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 7 May 2018 18:22:07 +0200 Subject: [PATCH 075/145] Preload pipeline data for project pipelines When displaying the pipelines of a project we now preload the following data: 1. Authors of the commits that belong to these pipelines 2. The number of warnings per pipeline, which is used by Ci::Pipeline#has_warnings? == Commit Authors Previously this data was queried for every Commit separately, leading to 20 SQL queries being executed in the worst case. With an average of 3 to 5 milliseconds per SQL query this could result in 100 milliseconds being spent in _just_ getting Commit authors. To preload this data Commit#author now uses BatchLoader (through Commit#lazy_author), and a separate module Gitlab::Ci::Pipeline::Preloader is used to ensure all authors are loaded before they are used. == Number of warnings This changes Ci::Pipeline#has_warnings? so it supports preloading of the number of warnings per pipeline. This removes the need for executing a COUNT(*) query for every pipeline just to see if it has any warnings or not. --- .../projects/pipelines_controller.rb | 2 +- app/models/ci/pipeline.rb | 13 ++- app/models/commit.rb | 28 ++++++- lib/gitlab/ci/pipeline/preloader.rb | 28 +++++++ spec/lib/gitlab/ci/pipeline/preloader_spec.rb | 20 +++++ spec/models/ci/pipeline_spec.rb | 27 ++++++ spec/models/commit_spec.rb | 84 ++++++++++++++++++- 7 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 lib/gitlab/ci/pipeline/preloader.rb create mode 100644 spec/lib/gitlab/ci/pipeline/preloader_spec.rb diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 329aaafb76d..91afe24b707 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -23,7 +23,7 @@ class Projects::PipelinesController < Projects::ApplicationController @finished_count = limited_pipelines_count(project, 'finished') @pipelines_count = limited_pipelines_count(project) - @pipelines.map(&:commit) # List commits for batch loading + Gitlab::Ci::Pipeline::Preloader.preload(@pipelines) respond_to do |format| format.html diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 1f49764e7cc..c26f0b6dcdc 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -406,7 +406,18 @@ module Ci end def has_warnings? - builds.latest.failed_but_allowed.any? + number_of_warnings.positive? + end + + def number_of_warnings + BatchLoader.for(id).batch(default_value: 0) do |pipeline_ids, loader| + Build.where(commit_id: pipeline_ids) + .latest + .failed_but_allowed + .group(:commit_id) + .count + .each { |id, amount| loader.call(id, amount) } + end end def set_config_source diff --git a/app/models/commit.rb b/app/models/commit.rb index b46f9f34689..56d4c86774e 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -224,8 +224,34 @@ class Commit Gitlab::ClosingIssueExtractor.new(project, current_user).closed_by_message(safe_message) end + def lazy_author + BatchLoader.for(author_email.downcase).batch do |emails, loader| + # A Hash that maps user Emails to the corresponding User objects. The + # Emails at this point are the _primary_ Emails of the Users. + users_for_emails = User + .by_any_email(emails) + .each_with_object({}) { |user, hash| hash[user.email] = user } + + users_for_ids = users_for_emails + .values + .each_with_object({}) { |user, hash| hash[user.id] = user } + + # Some commits may have used an alternative Email address. In this case we + # need to query the "emails" table to map those addresses to User objects. + Email + .where(email: emails - users_for_emails.keys) + .pluck(:email, :user_id) + .each { |(email, id)| users_for_emails[email] = users_for_ids[id] } + + users_for_emails.each { |email, user| loader.call(email, user) } + end + end + def author - User.find_by_any_email(author_email.downcase) + # We use __sync so that we get the actual objects back (including an actual + # nil), instead of a wrapper, as returning a wrapped nil breaks a lot of + # code. + lazy_author.__sync end request_cache(:author) { author_email.downcase } diff --git a/lib/gitlab/ci/pipeline/preloader.rb b/lib/gitlab/ci/pipeline/preloader.rb new file mode 100644 index 00000000000..e7a2e5511cf --- /dev/null +++ b/lib/gitlab/ci/pipeline/preloader.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Pipeline + # Class for preloading data associated with pipelines such as commit + # authors. + module Preloader + def self.preload(pipelines) + # This ensures that all the pipeline commits are eager loaded before we + # start using them. + pipelines.each(&:commit) + + pipelines.each do |pipeline| + # This preloads the author of every commit. We're using "lazy_author" + # here since "author" immediately loads the data on the first call. + pipeline.commit.try(:lazy_author) + + # This preloads the number of warnings for every pipeline, ensuring + # that Ci::Pipeline#has_warnings? doesn't execute any additional + # queries. + pipeline.number_of_warnings + end + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/preloader_spec.rb b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb new file mode 100644 index 00000000000..477c7477df0 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/preloader_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Preloader do + describe '.preload' do + it 'preloads the author of every pipeline commit' do + commit = double(:commit) + pipeline = double(:pipeline, commit: commit) + + expect(commit) + .to receive(:lazy_author) + + expect(pipeline) + .to receive(:number_of_warnings) + + described_class.preload([pipeline]) + end + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index ddd66a6be87..e7845b693a1 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -774,6 +774,33 @@ describe Ci::Pipeline, :mailer do end end + describe '#number_of_warnings' do + it 'returns the number of warnings' do + create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop') + + expect(pipeline.number_of_warnings).to eq(1) + end + + it 'supports eager loading of the number of warnings' do + pipeline2 = create(:ci_empty_pipeline, status: :created, project: project) + + create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop') + create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline2, name: 'rubocop') + + pipelines = project.pipelines.to_a + + pipelines.each(&:number_of_warnings) + + # To run the queries we need to actually use the lazy objects, which we do + # by just sending "to_i" to them. + amount = ActiveRecord::QueryRecorder + .new { pipelines.each { |p| p.number_of_warnings.to_i } } + .count + + expect(amount).to eq(1) + end + end + shared_context 'with some outdated pipelines' do before do create_pipeline(:canceled, 'ref', 'A', project) diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 448b813c2e1..090f91168ad 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -52,22 +52,98 @@ describe Commit do end end - describe '#author' do + describe '#author', :request_store do it 'looks up the author in a case-insensitive way' do user = create(:user, email: commit.author_email.upcase) expect(commit.author).to eq(user) end - it 'caches the author', :request_store do + it 'caches the author' do user = create(:user, email: commit.author_email) - expect(User).to receive(:find_by_any_email).and_call_original expect(commit.author).to eq(user) + key = "Commit:author:#{commit.author_email.downcase}" - expect(RequestStore.store[key]).to eq(user) + expect(RequestStore.store[key]).to eq(user) expect(commit.author).to eq(user) end + + context 'using eager loading' do + let!(:alice) { create(:user, email: 'alice@example.com') } + let!(:bob) { create(:user, email: 'hunter2@example.com') } + + let(:alice_commit) do + described_class.new(RepoHelpers.sample_commit, project).tap do |c| + c.author_email = 'alice@example.com' + end + end + + let(:bob_commit) do + # The commit for Bob uses one of his alternative Emails, instead of the + # primary one. + described_class.new(RepoHelpers.sample_commit, project).tap do |c| + c.author_email = 'bob@example.com' + end + end + + let(:eve_commit) do + described_class.new(RepoHelpers.sample_commit, project).tap do |c| + c.author_email = 'eve@example.com' + end + end + + let!(:commits) { [alice_commit, bob_commit, eve_commit] } + + before do + create(:email, user: bob, email: 'bob@example.com') + end + + it 'executes only two SQL queries' do + recorder = ActiveRecord::QueryRecorder.new do + # Running this first ensures we don't run one query for every + # commit. + commits.each(&:lazy_author) + + # This forces the execution of the SQL queries necessary to load the + # data. + commits.each { |c| c.author.try(:id) } + end + + expect(recorder.count).to eq(2) + end + + it "preloads the authors for Commits matching a user's primary Email" do + commits.each(&:lazy_author) + + expect(alice_commit.author).to eq(alice) + end + + it "preloads the authors for Commits using a User's alternative Email" do + commits.each(&:lazy_author) + + expect(bob_commit.author).to eq(bob) + end + + it 'sets the author to Nil if an author could not be found for a Commit' do + commits.each(&:lazy_author) + + expect(eve_commit.author).to be_nil + end + + it 'does not execute SQL queries once the authors are preloaded' do + commits.each(&:lazy_author) + commits.each { |c| c.author.try(:id) } + + recorder = ActiveRecord::QueryRecorder.new do + alice_commit.author + bob_commit.author + eve_commit.author + end + + expect(recorder.count).to be_zero + end + end end describe '#to_reference' do From 878ca2e69b371e6c12acee6bddd32b4406c651d7 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 11 May 2018 16:17:03 +0200 Subject: [PATCH 076/145] Exclude coverage data from the pipelines page When displaying a project's pipelines (Projects::PipelinesController#index) we now exclude the coverage data. This data was not used by the frontend, yet getting it would require one SQL query per pipeline. These queries in turn could be quite expensive on GitLab.com. --- app/controllers/projects/pipelines_controller.rb | 2 +- app/serializers/pipeline_entity.rb | 6 +++++- spec/controllers/projects/pipelines_controller_spec.rb | 6 ++++++ spec/serializers/pipeline_entity_spec.rb | 7 +++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 91afe24b707..6b40fc2fe68 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -34,7 +34,7 @@ class Projects::PipelinesController < Projects::ApplicationController pipelines: PipelineSerializer .new(project: @project, current_user: @current_user) .with_pagination(request, response) - .represent(@pipelines), + .represent(@pipelines, disable_coverage: true), count: { all: @pipelines_count, running: @running_count, diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb index 6457294b285..f782b411b84 100644 --- a/app/serializers/pipeline_entity.rb +++ b/app/serializers/pipeline_entity.rb @@ -4,7 +4,11 @@ class PipelineEntity < Grape::Entity expose :id expose :user, using: UserEntity expose :active?, as: :active - expose :coverage + + # Coverage isn't always necessary (e.g. when displaying project pipelines in + # the UI). Instead of creating an entirely different entity we just allow the + # disabling of this specific field whenever necessary. + expose :coverage, unless: proc { options[:disable_coverage] } expose :source expose :created_at, :updated_at diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index c22e455ea2d..9e7bc20a6d1 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -41,6 +41,12 @@ describe Projects::PipelinesController do expect(json_response['count']['finished']).to eq '1' end + it 'does not include coverage data for the pipelines' do + subject + + expect(json_response['pipelines'][0]).not_to include('coverage') + end + context 'when performing gitaly calls', :request_store do it 'limits the Gitaly requests' do expect { subject }.to change { Gitlab::GitalyClient.get_request_count }.by(3) diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb index 2473c561f4b..e67d12b7a89 100644 --- a/spec/serializers/pipeline_entity_spec.rb +++ b/spec/serializers/pipeline_entity_spec.rb @@ -26,6 +26,13 @@ describe PipelineEntity do expect(subject).to include :updated_at, :created_at end + it 'excludes coverage data when disabled' do + entity = described_class + .represent(pipeline, request: request, disable_coverage: true) + + expect(entity.as_json).not_to include(:coverage) + end + it 'contains details' do expect(subject).to include :details expect(subject[:details]) From da7bbef8fed767483236f9503553942b4c1acd05 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 11 May 2018 16:22:09 +0200 Subject: [PATCH 077/145] Added changelog for pipelines page performance --- changelogs/unreleased/pipelines-index-performance.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/pipelines-index-performance.yml diff --git a/changelogs/unreleased/pipelines-index-performance.yml b/changelogs/unreleased/pipelines-index-performance.yml new file mode 100644 index 00000000000..928c2ddab72 --- /dev/null +++ b/changelogs/unreleased/pipelines-index-performance.yml @@ -0,0 +1,5 @@ +--- +title: Improve performance of project pipelines pages +merge_request: +author: +type: performance From 143a98a3054644f70fbcc725c453f443c4546e3c Mon Sep 17 00:00:00 2001 From: Fabio Busatto Date: Thu, 17 May 2018 12:57:49 +0000 Subject: [PATCH 078/145] Update quick_start_guide.md --- doc/topics/autodevops/quick_start_guide.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md index 15567715c98..0b16af2953b 100644 --- a/doc/topics/autodevops/quick_start_guide.md +++ b/doc/topics/autodevops/quick_start_guide.md @@ -23,6 +23,10 @@ page](https://gitlab.com/auto-devops-examples/minimal-ruby-app) and press the **Fork** button. Soon you should have a project under your namespace with the necessary files. +You can also start a new project from a +[GitLab project template](https://gitlab.com/gitlab-org/project-templates) if +you want to use a different language. + ## Setup your own cluster on Google Kubernetes Engine If you do not already have a Google Cloud account, create one at From 368697ecf343c51db4f1a36372a2aa1ce5f950a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 17 May 2018 15:23:04 +0200 Subject: [PATCH 079/145] Loosen the matcher in the MR creation test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- qa/qa/specs/features/merge_request/create_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb index fbf9a4d17e5..0931e649e24 100644 --- a/qa/qa/specs/features/merge_request/create_spec.rb +++ b/qa/qa/specs/features/merge_request/create_spec.rb @@ -11,7 +11,7 @@ module QA expect(page).to have_content('This is a merge request') expect(page).to have_content('Great feature') - expect(page).to have_content('Opened less than a minute ago') + expect(page).to have_content(/Opened [\w\s]+ a minute ago/) end end end From 22d3e90ced30532ede17509d373c2e88257a7fe1 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Thu, 17 May 2018 08:50:52 -0500 Subject: [PATCH 080/145] Add changelog --- .../46177-fix-present-on-generic-commit-status.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml diff --git a/changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml b/changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml new file mode 100644 index 00000000000..2f885c5c927 --- /dev/null +++ b/changelogs/unreleased/46177-fix-present-on-generic-commit-status.yml @@ -0,0 +1,5 @@ +--- +title: Allow CommitStatus class to use presentable methods +merge_request: 18979 +author: +type: fixed From 88fa0ecdd9a2982795bb9ddd56873e6ac5c23815 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 17 May 2018 12:59:20 +0000 Subject: [PATCH 081/145] Fix group lists visual * Reset p bottom margin for group lists to fix vertical alignment * Remove double border for group lists to be consistent with project lists Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 11 +---------- app/assets/stylesheets/pages/groups.scss | 4 ++++ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index f1a8a46dda4..4110d7f15a8 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -279,7 +279,6 @@ ul.indent-list { padding: 10px 0 0 30px; } - // Specific styles for tree list @keyframes spin-avatar { from { transform: rotate(0deg); } @@ -424,18 +423,10 @@ ul.indent-list { &:first-child { border-top: 1px solid $white-normal; } - - &:last-of-type { - .group-row-contents:not(:hover) { - border-bottom: 1px solid transparent; - } - } } .group-row-contents { - padding: 10px 10px 8px; - border-top: solid 1px transparent; - border-bottom: solid 1px $white-normal; + padding: $gl-padding-top; &:hover { border-color: $row-hover-border; diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 6ee8b33bd39..c378ad50836 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -18,6 +18,10 @@ .group-row { @include basic-list-stats; + + .description p { + margin-bottom: 0; + } } .ldap-group-links { From b11c218ad9d4635c2230e7f8d105236ca32e5072 Mon Sep 17 00:00:00 2001 From: Harish Ved Date: Thu, 17 May 2018 16:20:41 +0000 Subject: [PATCH 082/145] Fix: Use case in-sensitive ordering by name for groups --- app/models/concerns/sortable.rb | 4 +- ...nsitive-ordering-for-dashboard-filters.yml | 5 + ...20180504195842_project_name_lower_index.rb | 32 ++++++ lib/tasks/migrate/setup_postgresql.rake | 2 + spec/models/concerns/sortable_spec.rb | 108 ++++++++++++++++++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/use-case-insensitive-ordering-for-dashboard-filters.yml create mode 100644 db/migrate/20180504195842_project_name_lower_index.rb create mode 100644 spec/models/concerns/sortable_spec.rb diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index cefa5c13c5f..db7254c27e0 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -12,8 +12,8 @@ module Sortable scope :order_created_asc, -> { reorder(created_at: :asc) } scope :order_updated_desc, -> { reorder(updated_at: :desc) } scope :order_updated_asc, -> { reorder(updated_at: :asc) } - scope :order_name_asc, -> { reorder(name: :asc) } - scope :order_name_desc, -> { reorder(name: :desc) } + scope :order_name_asc, -> { reorder("lower(name) asc") } + scope :order_name_desc, -> { reorder("lower(name) desc") } end module ClassMethods diff --git a/changelogs/unreleased/use-case-insensitive-ordering-for-dashboard-filters.yml b/changelogs/unreleased/use-case-insensitive-ordering-for-dashboard-filters.yml new file mode 100644 index 00000000000..098e4b1d5fa --- /dev/null +++ b/changelogs/unreleased/use-case-insensitive-ordering-for-dashboard-filters.yml @@ -0,0 +1,5 @@ +--- +title: "Use case in-sensitive ordering by name for dashboard" +merge_request: 18553 +author: "@vedharish" +type: fixed diff --git a/db/migrate/20180504195842_project_name_lower_index.rb b/db/migrate/20180504195842_project_name_lower_index.rb new file mode 100644 index 00000000000..d6f25d3d4ab --- /dev/null +++ b/db/migrate/20180504195842_project_name_lower_index.rb @@ -0,0 +1,32 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class ProjectNameLowerIndex < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + INDEX_NAME = 'index_projects_on_lower_name' + + disable_ddl_transaction! + + def up + return unless Gitlab::Database.postgresql? + + disable_statement_timeout + + execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON projects (LOWER(name))" + end + + def down + return unless Gitlab::Database.postgresql? + + disable_statement_timeout + + if supports_drop_index_concurrently? + execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME}" + else + execute "DROP INDEX IF EXISTS #{INDEX_NAME}" + end + end +end diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake index af30ecb0e9b..e7aab50e42a 100644 --- a/lib/tasks/migrate/setup_postgresql.rake +++ b/lib/tasks/migrate/setup_postgresql.rake @@ -8,6 +8,7 @@ task setup_postgresql: :environment do require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like') require Rails.root.join('db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb') require Rails.root.join('db/migrate/20180215181245_users_name_lower_index.rb') + require Rails.root.join('db/migrate/20180504195842_project_name_lower_index.rb') require Rails.root.join('db/post_migrate/20180306164012_add_path_index_to_redirect_routes.rb') NamespacesProjectsPathLowerIndexes.new.up @@ -18,5 +19,6 @@ task setup_postgresql: :environment do IndexRedirectRoutesPathForLike.new.up AddIndexOnNamespacesLowerName.new.up UsersNameLowerIndex.new.up + ProjectNameLowerIndex.new.up AddPathIndexToRedirectRoutes.new.up end diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb new file mode 100644 index 00000000000..b821a84d5e0 --- /dev/null +++ b/spec/models/concerns/sortable_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' + +describe Sortable do + describe '.order_by' do + let(:relation) { Group.all } + + describe 'ordering by id' do + it 'ascending' do + expect(relation).to receive(:reorder).with(id: :asc) + + relation.order_by('id_asc') + end + + it 'descending' do + expect(relation).to receive(:reorder).with(id: :desc) + + relation.order_by('id_desc') + end + end + + describe 'ordering by created day' do + it 'ascending' do + expect(relation).to receive(:reorder).with(created_at: :asc) + + relation.order_by('created_asc') + end + + it 'descending' do + expect(relation).to receive(:reorder).with(created_at: :desc) + + relation.order_by('created_desc') + end + + it 'order by "date"' do + expect(relation).to receive(:reorder).with(created_at: :desc) + + relation.order_by('created_date') + end + end + + describe 'ordering by name' do + it 'ascending' do + expect(relation).to receive(:reorder).with("lower(name) asc") + + relation.order_by('name_asc') + end + + it 'descending' do + expect(relation).to receive(:reorder).with("lower(name) desc") + + relation.order_by('name_desc') + end + end + + describe 'ordering by Updated Time' do + it 'ascending' do + expect(relation).to receive(:reorder).with(updated_at: :asc) + + relation.order_by('updated_asc') + end + + it 'descending' do + expect(relation).to receive(:reorder).with(updated_at: :desc) + + relation.order_by('updated_desc') + end + end + + it 'does not call reorder in case of unrecognized ordering' do + expect(relation).not_to receive(:reorder) + + relation.order_by('random_ordering') + end + end + + describe 'sorting groups' do + def ordered_group_names(order) + Group.all.order_by(order).map(&:name) + end + + let!(:ref_time) { Time.parse('2018-05-01 00:00:00') } + let!(:group1) { create(:group, name: 'aa', id: 1, created_at: ref_time - 15.seconds, updated_at: ref_time) } + let!(:group2) { create(:group, name: 'AAA', id: 2, created_at: ref_time - 10.seconds, updated_at: ref_time - 5.seconds) } + let!(:group3) { create(:group, name: 'BB', id: 3, created_at: ref_time - 5.seconds, updated_at: ref_time - 10.seconds) } + let!(:group4) { create(:group, name: 'bbb', id: 4, created_at: ref_time, updated_at: ref_time - 15.seconds) } + + it 'sorts groups by id' do + expect(ordered_group_names('id_asc')).to eq(%w(aa AAA BB bbb)) + expect(ordered_group_names('id_desc')).to eq(%w(bbb BB AAA aa)) + end + + it 'sorts groups by name via case-insentitive comparision' do + expect(ordered_group_names('name_asc')).to eq(%w(aa AAA BB bbb)) + expect(ordered_group_names('name_desc')).to eq(%w(bbb BB AAA aa)) + end + + it 'sorts groups by created_at' do + expect(ordered_group_names('created_asc')).to eq(%w(aa AAA BB bbb)) + expect(ordered_group_names('created_desc')).to eq(%w(bbb BB AAA aa)) + expect(ordered_group_names('created_date')).to eq(%w(bbb BB AAA aa)) + end + + it 'sorts groups by updated_at' do + expect(ordered_group_names('updated_asc')).to eq(%w(bbb BB AAA aa)) + expect(ordered_group_names('updated_desc')).to eq(%w(aa AAA BB bbb)) + end + end +end From e4adf0150b58d0b7f8437cbb9a3cb3ac8aa31bec Mon Sep 17 00:00:00 2001 From: Jacopo Date: Tue, 15 May 2018 12:40:17 +0200 Subject: [PATCH 083/145] Fixes 500 error on /estimate BIG_VALUE --- app/models/concerns/time_trackable.rb | 4 ++++ changelogs/unreleased/46193-fix-big-estimate.yml | 5 +++++ spec/models/concerns/issuable_spec.rb | 13 +++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 changelogs/unreleased/46193-fix-big-estimate.yml diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb index 73fc5048dcf..1caf47072bc 100644 --- a/app/models/concerns/time_trackable.rb +++ b/app/models/concerns/time_trackable.rb @@ -53,6 +53,10 @@ module TimeTrackable Gitlab::TimeTrackingFormatter.output(time_estimate) end + def time_estimate=(val) + val.is_a?(Integer) ? super([val, Gitlab::Database::MAX_INT_VALUE].min) : super(val) + end + private def touchable? diff --git a/changelogs/unreleased/46193-fix-big-estimate.yml b/changelogs/unreleased/46193-fix-big-estimate.yml new file mode 100644 index 00000000000..d0da0c10033 --- /dev/null +++ b/changelogs/unreleased/46193-fix-big-estimate.yml @@ -0,0 +1,5 @@ +--- +title: Fixes 500 error on /estimate BIG_VALUE +merge_request: 18964 +author: Jacopo Beschi @jacopo-beschi +type: fixed diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 3d3092b8ac9..bd6bf5b0712 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -266,6 +266,19 @@ describe Issuable do end end + describe '#time_estimate=' do + it 'coerces the value below Gitlab::Database::MAX_INT_VALUE' do + expect { issue.time_estimate = 100 }.to change { issue.time_estimate }.to(100) + expect { issue.time_estimate = Gitlab::Database::MAX_INT_VALUE + 100 }.to change { issue.time_estimate }.to(Gitlab::Database::MAX_INT_VALUE) + end + + it 'skips coercion for not Integer values' do + expect { issue.time_estimate = nil }.to change { issue.time_estimate }.to(nil) + expect { issue.time_estimate = 'invalid time' }.not_to raise_error(StandardError) + expect { issue.time_estimate = 22.33 }.not_to raise_error(StandardError) + end + end + describe '#to_hook_data' do let(:builder) { double } From bd2b57d20bc92db8fa601abcc8360d37c94a716c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 May 2018 11:43:54 -0500 Subject: [PATCH 084/145] Bring CE-EE parity to app/services/lfs/unlock_file_service.rb --- app/services/lfs/unlock_file_service.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/services/lfs/unlock_file_service.rb b/app/services/lfs/unlock_file_service.rb index 6c93dc69bb0..7eb89339a92 100644 --- a/app/services/lfs/unlock_file_service.rb +++ b/app/services/lfs/unlock_file_service.rb @@ -2,14 +2,14 @@ module Lfs class UnlockFileService < BaseService def execute unless can?(current_user, :push_code, project) - raise Gitlab::GitAccess::UnauthorizedError, 'You have no permissions' + raise Gitlab::GitAccess::UnauthorizedError, _('You have no permissions') end unlock_file rescue Gitlab::GitAccess::UnauthorizedError => ex error(ex.message, 403) rescue ActiveRecord::RecordNotFound - error('Lock not found', 404) + error(_('Lock not found'), 404) rescue => ex error(ex.message, 500) end @@ -24,9 +24,9 @@ module Lfs success(lock: lock, http_status: :ok) elsif forced - error('You must have master access to force delete a lock', 403) + error(_('You must have master access to force delete a lock'), 403) else - error("#{lock.path} is locked by GitLab User #{lock.user_id}", 403) + error(_("%{lock_path} is locked by GitLab User %{lock_user_id}") % { lock_path: lock.path, lock_user_id: lock.user_id }, 403) end end From d1f44952da9e45d90a6815bd3630093de9eaea66 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 17 May 2018 11:51:55 -0500 Subject: [PATCH 085/145] Bring CE-EE parity to app/services/milestones/base_service.rb --- app/services/milestones/base_service.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/services/milestones/base_service.rb b/app/services/milestones/base_service.rb index 4963601ea8b..cce0863d611 100644 --- a/app/services/milestones/base_service.rb +++ b/app/services/milestones/base_service.rb @@ -5,6 +5,7 @@ module Milestones def initialize(parent, user, params = {}) @parent, @current_user, @params = parent, user, params.dup + super end end end From 90100c26611216ac9b1c64c4ed54a8b5a20df5c9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 17 May 2018 10:12:48 -0700 Subject: [PATCH 086/145] Conditionally add Gitaly deprecation warnings based on ENV variable --- config/initializers/deprecations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/deprecations.rb b/config/initializers/deprecations.rb index c8d7f742bb1..14616e726d9 100644 --- a/config/initializers/deprecations.rb +++ b/config/initializers/deprecations.rb @@ -1,4 +1,4 @@ -if Gitlab.dev_env_or_com? +if Rails.env.development? || ENV['GITLAB_LEGACY_PATH_LOG_MESSAGE'] deprecator = ActiveSupport::Deprecation.new('11.0', 'GitLab') deprecator.behavior = -> (message, callstack) { From 63c58a6dd0d8a4b3db172ffe6d0e32e127cdabd1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 17 May 2018 21:20:15 +0200 Subject: [PATCH 087/145] Memoize Gitlab::Database.version This removes the need for running a database query every time we want to check the database version. --- changelogs/unreleased/memoize-database-version.yml | 5 +++++ lib/gitlab/database.rb | 2 +- spec/lib/gitlab/database_spec.rb | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/memoize-database-version.yml diff --git a/changelogs/unreleased/memoize-database-version.yml b/changelogs/unreleased/memoize-database-version.yml new file mode 100644 index 00000000000..575348a53a1 --- /dev/null +++ b/changelogs/unreleased/memoize-database-version.yml @@ -0,0 +1,5 @@ +--- +title: Memoize Gitlab::Database.version +merge_request: +author: +type: performance diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 76501dd50e8..d49d055c3f2 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -43,7 +43,7 @@ module Gitlab end def self.version - database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] + @version ||= database_version.match(/\A(?:PostgreSQL |)([^\s]+).*\z/)[1] end def self.join_lateral_supported? diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index 1fe1d3926ad..8ac36ae8bab 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -32,6 +32,12 @@ describe Gitlab::Database do end describe '.version' do + around do |example| + described_class.instance_variable_set(:@version, nil) + example.run + described_class.instance_variable_set(:@version, nil) + end + context "on mysql" do it "extracts the version number" do allow(described_class).to receive(:database_version) @@ -49,6 +55,14 @@ describe Gitlab::Database do expect(described_class.version).to eq '9.4.4' end end + + it 'memoizes the result' do + count = ActiveRecord::QueryRecorder + .new { 2.times { described_class.version } } + .count + + expect(count).to eq(1) + end end describe '.join_lateral_supported?' do From 3babd1e0cb7173e09df1844f82427102c4104ba9 Mon Sep 17 00:00:00 2001 From: Murat Dogan Date: Thu, 17 May 2018 19:36:56 +0000 Subject: [PATCH 088/145] fix / assigne username wrapping problem has been fixed --- app/assets/stylesheets/pages/issuable.scss | 18 +++++++++++++++--- .../unreleased/fix-assignee-name-wrap.yml | 5 +++++ 2 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/fix-assignee-name-wrap.yml diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index b2dad4a358a..a8110f069d4 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -197,9 +197,21 @@ } &.assignee { - .author_link:hover { - .author { - text-decoration: underline; + .author_link { + display: block; + padding-left: 42px; + position: relative; + + &:hover { + .author { + text-decoration: underline; + } + } + + .avatar { + left: 0; + position: absolute; + top: 0; } } } diff --git a/changelogs/unreleased/fix-assignee-name-wrap.yml b/changelogs/unreleased/fix-assignee-name-wrap.yml new file mode 100644 index 00000000000..2407288785f --- /dev/null +++ b/changelogs/unreleased/fix-assignee-name-wrap.yml @@ -0,0 +1,5 @@ +--- +title: Wrapping problem on the issues page has been fixed +merge_request: +author: +type: fixed From 35b37cfbc39cac341ed4caec5c4baaf0646a4b66 Mon Sep 17 00:00:00 2001 From: Sarrah Vesselov Date: Thu, 17 May 2018 20:29:41 +0000 Subject: [PATCH 089/145] fix typos. add a reference to deliverable and stretch for design artifact --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 758c2a9ea6c..7f28da092cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -168,7 +168,7 @@ hits. They are not always necessary, but very convenient. If you are an expert in a particular area, it makes it easier to find issues to work on. You can also subscribe to those labels to receive an email each time an -issue is labelled with a subject label corresponding to your expertise. +issue is labeled with a subject label corresponding to your expertise. Examples of subject labels are ~wiki, ~"container registry", ~ldap, ~api, ~issues, ~"merge requests", ~labels, and ~"container registry". @@ -315,10 +315,10 @@ Once an issue has been worked on and is ready for development, a UXer applies th The UX team has a special type label called ~"design artifact". This label indicates that the final output for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone. -Any issue labelled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is +Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is needed until the solution has been decided. -~"design artifact" issues are like any other issue and should contain a milestone label when scheduled in the current milestone. +~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone. Once the ~"design artifact" issue has been completed, the UXer removes the ~"design artifact" label and applies the ~"UX ready" label. The Product Manager can use the existing issue or decide to create a whole new issue for the purpose of development. From 0bc78d08000ae1d5ee0943c8991d6d3fee7977e3 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Thu, 17 May 2018 14:10:37 -0700 Subject: [PATCH 090/145] Build cloud native images on tags When on a tag, trigger a multi-project pipeline in the CNG repostiory. Opting for a trigger rather than an addition to our release-tools project for a few reasons: - The Dockerfiles in the CNG image repo change infrequently, and as a result I don't feel the need/overhead for stable branches in that repo at this time - My intent with the CNG repo, is that once stable, the Dockerfiles would actualy move to their component projects, to be versioned with the code they are building - It is likely that we will want to followup with a manually triggered package for branches for devs, and possibly review apps, so it made sense to build the CNG ci jobs to accept this sort of pipeline. --- .gitlab-ci.yml | 19 ++++++++++ scripts/trigger-build-cloud-native | 61 ++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100755 scripts/trigger-build-cloud-native diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 84d8e69b84e..cc6fd5e2bfe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -298,6 +298,25 @@ review-docs-cleanup: script: - ./trigger-build-docs cleanup +## +# Trigger a docker image build in CNG (Cloud Native GitLab) repository +# +cloud-native-image: + image: ruby:2.4-alpine + before_script: [] + stage: build + allow_failure: true + cache: {} + retry: 0 + before_script: + - gem install gitlab --no-doc + - chmod 755 ./scripts/trigger-build-cloud-native + script: + - ./scripts/trigger-build-cloud-native + only: + - tags@gitlab-org/gitlab-ce + - tags@gitlab-org/gitlab-ee + # Retrieve knapsack and rspec_flaky reports retrieve-tests-metadata: <<: *tests-metadata-state diff --git a/scripts/trigger-build-cloud-native b/scripts/trigger-build-cloud-native new file mode 100755 index 00000000000..b6ca75a588d --- /dev/null +++ b/scripts/trigger-build-cloud-native @@ -0,0 +1,61 @@ +#!/usr/bin/env ruby + +require 'gitlab' + +# +# Configure credentials to be used with gitlab gem +# +Gitlab.configure do |config| + config.endpoint = 'https://gitlab.com/api/v4' +end + +# +# The remote project +# +GITLAB_CNG_REPO = 'gitlab-org/build/CNG'.freeze + +def ee? + ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md') +end + +def read_file_version(filename) + raw_version = File.read(filename).strip + + # if the version matches semver format, treat it as a tag and prepend `v` + if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/) + "v#{raw_version}" + else + raw_version + end +end + +def params + params = { + 'GITLAB_SHELL_VERSION' => read_file_version('GITLAB_SHELL_VERSION'), + 'GITALY_VERSION' => read_file_version('GITALY_SERVER_VERSION'), + 'TRIGGERED_USER' => ENV['GITLAB_USER_NAME'], + 'TRIGGER_SOURCE' => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}" + } + + if ee? + params['EE_PIPELINE'] = 'true' + params['GITLAB_EE_VERSION'] = ENV['CI_COMMIT_REF_NAME'] + else + params['CE_PIPELINE'] = 'true' + params['GITLAB_CE_VERSION'] = ENV['CI_COMMIT_REF_NAME'] + end + + params +end + +# +# Trigger a pipeline +# +def trigger_pipeline + # Create the cross project pipeline using CI_JOB_TOKEN + pipeline = Gitlab.run_trigger(GITLAB_CNG_REPO, ENV['CI_JOB_TOKEN'], 'master', params) + + puts "Triggered https://gitlab.com/#{GITLAB_CNG_REPO}/pipelines/#{pipeline.id}" +end + +trigger_pipeline From b6d1e20c918608844cd8438c73120016a6dab2d4 Mon Sep 17 00:00:00 2001 From: Joshua Lambert Date: Thu, 17 May 2018 18:27:10 -0400 Subject: [PATCH 091/145] Update CICD to use rocket icon --- app/views/layouts/nav/sidebar/_project.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index c3ea592a6b5..4d7a5a6e856 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -154,7 +154,7 @@ = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts]) do = link_to project_pipelines_path(@project), class: 'shortcuts-pipelines' do .nav-icon-container - = sprite_icon('pipeline') + = sprite_icon('rocket') %span.nav-item-name = _('CI / CD') From 6c190d273d18d21e50dea65645185839bf067714 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 18 May 2018 01:05:11 +0000 Subject: [PATCH 092/145] Move API group deletion to Sidekiq --- .../unreleased/sh-move-delete-groups-api-async.yml | 5 +++++ doc/api/groups.md | 3 +++ lib/api/groups.rb | 4 +++- lib/api/v3/groups.rb | 5 +++-- spec/requests/api/groups_spec.rb | 9 ++++++--- spec/requests/api/v3/groups_spec.rb | 8 +++++--- 6 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/sh-move-delete-groups-api-async.yml diff --git a/changelogs/unreleased/sh-move-delete-groups-api-async.yml b/changelogs/unreleased/sh-move-delete-groups-api-async.yml new file mode 100644 index 00000000000..1b200cac5c5 --- /dev/null +++ b/changelogs/unreleased/sh-move-delete-groups-api-async.yml @@ -0,0 +1,5 @@ +--- +title: Move API group deletion to Sidekiq +merge_request: +author: +type: changed diff --git a/doc/api/groups.md b/doc/api/groups.md index 923fd662a5b..96842ef330f 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -487,6 +487,9 @@ Parameters: - `id` (required) - The ID or path of a user group +This will queue a background job to delete all projects in the group. The +response will be a 202 Accepted if the user has authorization. + ## Search for group Get all groups that match your string in their name or path. diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 0d125cd7831..03b6b30a0d8 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -167,8 +167,10 @@ module API Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/46285') destroy_conditionally!(group) do |group| - ::Groups::DestroyService.new(group, current_user).execute + ::Groups::DestroyService.new(group, current_user).async_execute end + + accepted! end desc 'Get a list of projects in this group.' do diff --git a/lib/api/v3/groups.rb b/lib/api/v3/groups.rb index 3844fd4810d..4fa7d196e50 100644 --- a/lib/api/v3/groups.rb +++ b/lib/api/v3/groups.rb @@ -131,8 +131,9 @@ module API delete ":id" do group = find_group!(params[:id]) authorize! :admin_group, group - Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/46285') - present ::Groups::DestroyService.new(group, current_user).execute, with: Entities::GroupDetail, current_user: current_user + ::Groups::DestroyService.new(group, current_user).async_execute + + accepted! end desc 'Get a list of projects in this group.' do diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index bb0034e3237..7d923932309 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -738,13 +738,16 @@ describe API::Groups do describe "DELETE /groups/:id" do context "when authenticated as user" do it "removes group" do - delete api("/groups/#{group1.id}", user1) + Sidekiq::Testing.fake! do + expect { delete api("/groups/#{group1.id}", user1) }.to change(GroupDestroyWorker.jobs, :size).by(1) + end - expect(response).to have_gitlab_http_status(204) + expect(response).to have_gitlab_http_status(202) end it_behaves_like '412 response' do let(:request) { api("/groups/#{group1.id}", user1) } + let(:success_status) { 202 } end it "does not remove a group if not an owner" do @@ -773,7 +776,7 @@ describe API::Groups do it "removes any existing group" do delete api("/groups/#{group2.id}", admin) - expect(response).to have_gitlab_http_status(204) + expect(response).to have_gitlab_http_status(202) end it "does not remove a non existing group" do diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb index a1cdf583de3..34d4b8e9565 100644 --- a/spec/requests/api/v3/groups_spec.rb +++ b/spec/requests/api/v3/groups_spec.rb @@ -458,9 +458,11 @@ describe API::V3::Groups do describe "DELETE /groups/:id" do context "when authenticated as user" do it "removes group" do - delete v3_api("/groups/#{group1.id}", user1) + Sidekiq::Testing.fake! do + expect { delete v3_api("/groups/#{group1.id}", user1) }.to change(GroupDestroyWorker.jobs, :size).by(1) + end - expect(response).to have_gitlab_http_status(200) + expect(response).to have_gitlab_http_status(202) end it "does not remove a group if not an owner" do @@ -489,7 +491,7 @@ describe API::V3::Groups do it "removes any existing group" do delete v3_api("/groups/#{group2.id}", admin) - expect(response).to have_gitlab_http_status(200) + expect(response).to have_gitlab_http_status(202) end it "does not remove a non existing group" do From b5821e5fb3882a744ce4f30367859c5cff66d765 Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Fri, 18 May 2018 02:18:42 +0000 Subject: [PATCH 093/145] Add Keyboard shortcuts for "Kubernetes" and "Environments" --- .../javascripts/shortcuts_navigation.js | 5 ++- app/views/help/_shortcuts.html.haml | 14 ++++++- .../layouts/nav/sidebar/_project.html.haml | 2 +- ...427-add-keyboard-shortcut-environments.yml | 5 +++ ...46427-add-keyboard-shortcut-kubernetes.yml | 5 +++ ...nge-keyboard-shortcut-of-activity-feed.yml | 5 +++ .../46427-remove-outdated-todos-shortcut.yml | 5 +++ doc/workflow/shortcuts.md | 10 +++-- .../projects/user_uses_shortcuts_spec.rb | 41 ++++++++++++++++++- 9 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/46427-add-keyboard-shortcut-environments.yml create mode 100644 changelogs/unreleased/46427-add-keyboard-shortcut-kubernetes.yml create mode 100644 changelogs/unreleased/46427-change-keyboard-shortcut-of-activity-feed.yml create mode 100644 changelogs/unreleased/46427-remove-outdated-todos-shortcut.yml diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js index a4d10850471..78f7353eb0d 100644 --- a/app/assets/javascripts/shortcuts_navigation.js +++ b/app/assets/javascripts/shortcuts_navigation.js @@ -7,7 +7,7 @@ export default class ShortcutsNavigation extends Shortcuts { super(); Mousetrap.bind('g p', () => findAndFollowLink('.shortcuts-project')); - Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-project-activity')); + Mousetrap.bind('g v', () => findAndFollowLink('.shortcuts-project-activity')); Mousetrap.bind('g f', () => findAndFollowLink('.shortcuts-tree')); Mousetrap.bind('g c', () => findAndFollowLink('.shortcuts-commits')); Mousetrap.bind('g j', () => findAndFollowLink('.shortcuts-builds')); @@ -16,9 +16,10 @@ export default class ShortcutsNavigation extends Shortcuts { Mousetrap.bind('g i', () => findAndFollowLink('.shortcuts-issues')); Mousetrap.bind('g b', () => findAndFollowLink('.shortcuts-issue-boards')); Mousetrap.bind('g m', () => findAndFollowLink('.shortcuts-merge_requests')); - Mousetrap.bind('g t', () => findAndFollowLink('.shortcuts-todos')); Mousetrap.bind('g w', () => findAndFollowLink('.shortcuts-wiki')); Mousetrap.bind('g s', () => findAndFollowLink('.shortcuts-snippets')); + Mousetrap.bind('g k', () => findAndFollowLink('.shortcuts-kubernetes')); + Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-environments')); Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue')); this.enabledHelp.push('.hidden-shortcut.project'); diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 1c5b4aecabb..2244d16f0a6 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -121,7 +121,7 @@ %tr %td.shortcut .key g - .key e + .key v %td Go to the project's activity feed %tr @@ -172,6 +172,18 @@ .key m %td Go to merge requests + %tr + %td.shortcut + .key g + .key e + %td + Go to environments + %tr + %td.shortcut + .key g + .key k + %td + Go to kubernetes %tr %td.shortcut .key g diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index c3ea592a6b5..0023ede2be9 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -212,7 +212,7 @@ - if project_nav_tab? :clusters - show_cluster_hint = show_gke_cluster_integration_callout?(@project) = nav_link(controller: [:clusters, :user, :gcp]) do - = link_to project_clusters_path(@project), title: _('Kubernetes'), class: 'shortcuts-cluster' do + = link_to project_clusters_path(@project), title: _('Kubernetes'), class: 'shortcuts-kubernetes' do %span = _('Kubernetes') - if show_cluster_hint diff --git a/changelogs/unreleased/46427-add-keyboard-shortcut-environments.yml b/changelogs/unreleased/46427-add-keyboard-shortcut-environments.yml new file mode 100644 index 00000000000..609968f3230 --- /dev/null +++ b/changelogs/unreleased/46427-add-keyboard-shortcut-environments.yml @@ -0,0 +1,5 @@ +--- +title: Adds keyboard shortcut `g e` for Environments on Project pages +merge_request: 19002 +author: +type: added diff --git a/changelogs/unreleased/46427-add-keyboard-shortcut-kubernetes.yml b/changelogs/unreleased/46427-add-keyboard-shortcut-kubernetes.yml new file mode 100644 index 00000000000..48e51b2615e --- /dev/null +++ b/changelogs/unreleased/46427-add-keyboard-shortcut-kubernetes.yml @@ -0,0 +1,5 @@ +--- +title: Adds keyboard shortcut `g k` for Kubernetes on Project pages +merge_request: 19002 +author: +type: added diff --git a/changelogs/unreleased/46427-change-keyboard-shortcut-of-activity-feed.yml b/changelogs/unreleased/46427-change-keyboard-shortcut-of-activity-feed.yml new file mode 100644 index 00000000000..9a7cf0d6944 --- /dev/null +++ b/changelogs/unreleased/46427-change-keyboard-shortcut-of-activity-feed.yml @@ -0,0 +1,5 @@ +--- +title: Changes keyboard shortcut of Activity feed to `g v` +merge_request: 19002 +author: +type: changed diff --git a/changelogs/unreleased/46427-remove-outdated-todos-shortcut.yml b/changelogs/unreleased/46427-remove-outdated-todos-shortcut.yml new file mode 100644 index 00000000000..f416e35030e --- /dev/null +++ b/changelogs/unreleased/46427-remove-outdated-todos-shortcut.yml @@ -0,0 +1,5 @@ +--- +title: Removes outdated `g t` shortcut for TODO in favor of `Shift+T` +merge_request: 19002 +author: +type: removed diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md index 2e1bd6bfe5c..930f2e73683 100644 --- a/doc/workflow/shortcuts.md +++ b/doc/workflow/shortcuts.md @@ -46,15 +46,19 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?' | Keyboard Shortcut | Description | | ----------------- | ----------- | | g + p | Go to the project's home page | -| g + e | Go to the project's activity feed | +| g + v | Go to the project's activity feed | | g + f | Go to files | | g + c | Go to commits | -| g + b | Go to jobs | +| g + j | Go to jobs | | g + n | Go to network graph | -| g + g | Go to repository charts | +| g + d | Go to repository charts | | g + i | Go to issues | +| g + b | Go to issue boards | | g + m | Go to merge requests | +| g + e | Go to environments | +| g + k | Go to kubernetes | | g + s | Go to snippets | +| g + w | Go to wiki | | t | Go to finding file | | i | New issue | diff --git a/spec/features/projects/user_uses_shortcuts_spec.rb b/spec/features/projects/user_uses_shortcuts_spec.rb index 47c5a8161d9..495a010b32c 100644 --- a/spec/features/projects/user_uses_shortcuts_spec.rb +++ b/spec/features/projects/user_uses_shortcuts_spec.rb @@ -13,6 +13,8 @@ describe 'User uses shortcuts', :js do context 'when navigating to the Project pages' do it 'redirects to the details page' do + visit project_issues_path(project) + find('body').native.send_key('g') find('body').native.send_key('p') @@ -22,7 +24,7 @@ describe 'User uses shortcuts', :js do it 'redirects to the activity page' do find('body').native.send_key('g') - find('body').native.send_key('e') + find('body').native.send_key('v') expect(page).to have_active_navigation('Project') expect(page).to have_active_sub_navigation('Activity') @@ -72,10 +74,19 @@ describe 'User uses shortcuts', :js do expect(page).to have_active_sub_navigation('List') end + it 'redirects to the issue board page' do + find('body').native.send_key('g') + find('body').native.send_key('b') + + expect(page).to have_active_navigation('Issues') + expect(page).to have_active_sub_navigation('Board') + end + it 'redirects to the new issue page' do find('body').native.send_key('i') expect(page).to have_content(project.title) + expect(page).to have_content('New Issue') end end @@ -88,6 +99,34 @@ describe 'User uses shortcuts', :js do end end + context 'when navigating to the CI / CD pages' do + it 'redirects to the Jobs page' do + find('body').native.send_key('g') + find('body').native.send_key('j') + + expect(page).to have_active_navigation('CI / CD') + expect(page).to have_active_sub_navigation('Jobs') + end + end + + context 'when navigating to the Operations pages' do + it 'redirects to the Environments page' do + find('body').native.send_key('g') + find('body').native.send_key('e') + + expect(page).to have_active_navigation('Operations') + expect(page).to have_active_sub_navigation('Environments') + end + + it 'redirects to the Kubernetes page' do + find('body').native.send_key('g') + find('body').native.send_key('k') + + expect(page).to have_active_navigation('Operations') + expect(page).to have_active_sub_navigation('Kubernetes') + end + end + context 'when navigating to the Snippets pages' do it 'redirects to the snippets page' do find('body').native.send_key('g') From db0454fe74776ef13ef1ad6b6ce688867d9a6c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lu=C3=ADs?= Date: Fri, 18 May 2018 03:51:36 +0000 Subject: [PATCH 094/145] =?UTF-8?q?Resolve=20"Web=20IDE:=20Previewing=20Ma?= =?UTF-8?q?rkdown=20in=20Firefox=20doesn=E2=80=99t=20show=20a=20scroll=20b?= =?UTF-8?q?ar"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/stylesheets/pages/repo.scss | 18 ++++++++++++------ .../45934-ide-firefox-scroll-md-preview.yml | 5 +++++ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/45934-ide-firefox-scroll-md-preview.yml diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 00457717f00..7637a3f577c 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -306,8 +306,18 @@ } .preview-container { - height: 100%; - overflow: auto; + flex-grow: 1; + position: relative; + + .md-previewer { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: auto; + padding: $gl-padding; + } .file-container { background-color: $gray-darker; @@ -347,10 +357,6 @@ color: $diff-image-info-color; } } - - .md-previewer { - padding: $gl-padding; - } } .ide-mode-tabs { diff --git a/changelogs/unreleased/45934-ide-firefox-scroll-md-preview.yml b/changelogs/unreleased/45934-ide-firefox-scroll-md-preview.yml new file mode 100644 index 00000000000..b9e70bc5679 --- /dev/null +++ b/changelogs/unreleased/45934-ide-firefox-scroll-md-preview.yml @@ -0,0 +1,5 @@ +--- +title: Fix unscrollable Markdown preview of WebIDE on Firefox +merge_request: +author: +type: fixed From d47d02acfb4e244a6c85b137253f865e2d463ae3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 18 May 2018 09:33:45 +0300 Subject: [PATCH 095/145] Move group lists css from framework/lists.scss to pages/groups.scss Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 228 -------------------- app/assets/stylesheets/pages/groups.scss | 228 ++++++++++++++++++++ 2 files changed, 228 insertions(+), 228 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 4110d7f15a8..45517416e93 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -285,236 +285,8 @@ ul.indent-list { to { transform: rotate(360deg); } } -.groups-list-tree-container { - .has-no-search-results { - text-align: center; - padding: $gl-padding; - font-style: italic; - color: $well-light-text-color; - } - - > .group-list-tree > .group-row.has-children:first-child { - border-top: 0; - } -} - -.group-list-tree { - .avatar-container.content-loading { - position: relative; - - > a, - > a .avatar { - height: 100%; - border-radius: 50%; - } - - > a { - padding: 2px; - - .avatar { - border: 2px solid $white-normal; - - &.identicon { - line-height: 15px; - } - } - } - - &::after { - content: ""; - position: absolute; - height: 100%; - width: 100%; - background-color: transparent; - border: 2px outset $kdb-border; - border-radius: 50%; - animation: spin-avatar 3s infinite linear; - } - } - - .folder-toggle-wrap { - float: left; - line-height: $list-text-height; - font-size: 0; - - span { - font-size: $gl-font-size; - } - } - - .folder-caret, - .item-type-icon { - display: inline-block; - } - - .folder-caret { - width: 15px; - - svg { - margin-bottom: 2px; - } - } - - .item-type-icon { - margin-top: 2px; - width: 20px; - } - - > .group-row:not(.has-children) { - .folder-caret { - opacity: 0; - } - } - - .content-list li:last-child { - padding-bottom: 0; - } - - .group-list-tree { - margin-bottom: 0; - margin-left: 30px; - position: relative; - - &::before { - content: ''; - display: block; - width: 0; - position: absolute; - top: 5px; - bottom: 0; - left: -16px; - border-left: 2px solid $border-white-normal; - } - - .group-row { - position: relative; - - &::before { - content: ""; - display: block; - width: 10px; - height: 0; - border-top: 2px solid $border-white-normal; - position: absolute; - top: 30px; - left: -16px; - } - - &:last-child::before { - background: $white-light; - height: auto; - top: 30px; - bottom: 0; - } - - &.being-removed { - opacity: 0.5; - } - } - } - - .group-row { - padding: 0; - - &.has-children { - border-top: 0; - } - - &:first-child { - border-top: 1px solid $white-normal; - } - } - - .group-row-contents { - padding: $gl-padding-top; - - &:hover { - border-color: $row-hover-border; - background-color: $row-hover; - cursor: pointer; - } - - .avatar-container > a { - width: 100%; - text-decoration: none; - } - - &.has-more-items { - display: block; - padding: 20px 10px; - } - - .stats { - position: relative; - line-height: 46px; - - > span { - display: inline-flex; - align-items: center; - height: 16px; - min-width: 30px; - } - - > span:last-child { - margin-right: 0; - } - - .stat-value { - margin: 2px 0 0 5px; - } - } - - .controls { - margin-left: 5px; - - > .btn { - margin-right: $btn-xs-side-margin; - } - } - } - - .project-row-contents .stats { - line-height: inherit; - - > span:first-child { - margin-left: 25px; - } - - .item-visibility { - margin-right: 0; - } - - .last-updated { - position: absolute; - right: 12px; - min-width: 250px; - text-align: right; - color: $gl-text-color-secondary; - } - } -} - .namespace-title { .tooltip-inner { max-width: 350px; } } - -ul.group-list-tree { - li.group-row { - > .group-row-contents .title { - line-height: $list-text-height; - } - - &.has-description > .group-row-contents .title { - line-height: inherit; - } - } -} - -.js-groups-list-holder { - .groups-list-loading { - font-size: 34px; - text-align: center; - } -} diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index c378ad50836..409b7285f82 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -241,3 +241,231 @@ overflow-y: unset; } } + +.groups-list-tree-container { + .has-no-search-results { + text-align: center; + padding: $gl-padding; + font-style: italic; + color: $well-light-text-color; + } + + > .group-list-tree > .group-row.has-children:first-child { + border-top: 0; + } +} + +.group-list-tree { + .avatar-container.content-loading { + position: relative; + + > a, + > a .avatar { + height: 100%; + border-radius: 50%; + } + + > a { + padding: 2px; + + .avatar { + border: 2px solid $white-normal; + + &.identicon { + line-height: 15px; + } + } + } + + &::after { + content: ""; + position: absolute; + height: 100%; + width: 100%; + background-color: transparent; + border: 2px outset $kdb-border; + border-radius: 50%; + animation: spin-avatar 3s infinite linear; + } + } + + .folder-toggle-wrap { + float: left; + line-height: $list-text-height; + font-size: 0; + + span { + font-size: $gl-font-size; + } + } + + .folder-caret, + .item-type-icon { + display: inline-block; + } + + .folder-caret { + width: 15px; + + svg { + margin-bottom: 2px; + } + } + + .item-type-icon { + margin-top: 2px; + width: 20px; + } + + > .group-row:not(.has-children) { + .folder-caret { + opacity: 0; + } + } + + .content-list li:last-child { + padding-bottom: 0; + } + + .group-list-tree { + margin-bottom: 0; + margin-left: 30px; + position: relative; + + &::before { + content: ''; + display: block; + width: 0; + position: absolute; + top: 5px; + bottom: 0; + left: -16px; + border-left: 2px solid $border-white-normal; + } + + .group-row { + position: relative; + + &::before { + content: ""; + display: block; + width: 10px; + height: 0; + border-top: 2px solid $border-white-normal; + position: absolute; + top: 30px; + left: -16px; + } + + &:last-child::before { + background: $white-light; + height: auto; + top: 30px; + bottom: 0; + } + + &.being-removed { + opacity: 0.5; + } + } + } + + .group-row { + padding: 0; + + &.has-children { + border-top: 0; + } + + &:first-child { + border-top: 1px solid $white-normal; + } + } + + .group-row-contents { + padding: $gl-padding-top; + + &:hover { + border-color: $row-hover-border; + background-color: $row-hover; + cursor: pointer; + } + + .avatar-container > a { + width: 100%; + text-decoration: none; + } + + &.has-more-items { + display: block; + padding: 20px 10px; + } + + .stats { + position: relative; + line-height: 46px; + + > span { + display: inline-flex; + align-items: center; + height: 16px; + min-width: 30px; + } + + > span:last-child { + margin-right: 0; + } + + .stat-value { + margin: 2px 0 0 5px; + } + } + + .controls { + margin-left: 5px; + + > .btn { + margin-right: $btn-xs-side-margin; + } + } + } + + .project-row-contents .stats { + line-height: inherit; + + > span:first-child { + margin-left: 25px; + } + + .item-visibility { + margin-right: 0; + } + + .last-updated { + position: absolute; + right: 12px; + min-width: 250px; + text-align: right; + color: $gl-text-color-secondary; + } + } +} + +ul.group-list-tree { + li.group-row { + > .group-row-contents .title { + line-height: $list-text-height; + } + + &.has-description > .group-row-contents .title { + line-height: inherit; + } + } +} + +.js-groups-list-holder { + .groups-list-loading { + font-size: 34px; + text-align: center; + } +} From 6b98033d9120c7dfc5276f623e27e7af22dd7b88 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 17 May 2018 23:48:59 -0700 Subject: [PATCH 096/145] Fix api_json.log not always reporting the right HTTP status code As described in https://github.com/aserafin/grape_logging/issues/45, if a Grape error is caught by the handlers and a different return code is returned, then the api_json.log would have a 500 error code instead of the right value. Inserting the GrapeLogging middleware after the Grape middleware fixes this problem. Seen in https://gitlab.com/gitlab-com/infrastructure/issues/4249 --- .../sh-fix-grape-logging-status-code.yml | 5 +++++ lib/api/api.rb | 17 +++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/sh-fix-grape-logging-status-code.yml diff --git a/changelogs/unreleased/sh-fix-grape-logging-status-code.yml b/changelogs/unreleased/sh-fix-grape-logging-status-code.yml new file mode 100644 index 00000000000..aabf9a84bfb --- /dev/null +++ b/changelogs/unreleased/sh-fix-grape-logging-status-code.yml @@ -0,0 +1,5 @@ +--- +title: Fix api_json.log not always reporting the right HTTP status code +merge_request: +author: +type: fixed diff --git a/lib/api/api.rb b/lib/api/api.rb index 5139e869c71..2fbeaaffcfe 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -8,14 +8,15 @@ module API PROJECT_ENDPOINT_REQUIREMENTS = { id: NO_SLASH_URL_PART_REGEX }.freeze COMMIT_ENDPOINT_REQUIREMENTS = PROJECT_ENDPOINT_REQUIREMENTS.merge(sha: NO_SLASH_URL_PART_REGEX).freeze - use GrapeLogging::Middleware::RequestLogger, - logger: Logger.new(LOG_FILENAME), - formatter: Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp.new, - include: [ - GrapeLogging::Loggers::FilterParameters.new, - GrapeLogging::Loggers::ClientEnv.new, - Gitlab::GrapeLogging::Loggers::UserLogger.new - ] + insert_before Grape::Middleware::Error, + GrapeLogging::Middleware::RequestLogger, + logger: Logger.new(LOG_FILENAME), + formatter: Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp.new, + include: [ + GrapeLogging::Loggers::FilterParameters.new, + GrapeLogging::Loggers::ClientEnv.new, + Gitlab::GrapeLogging::Loggers::UserLogger.new + ] allow_access_with_scope :api prefix :api From af9b0bfbae84a402e5c706ac29772b0d70dfa156 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 18 May 2018 10:14:10 +0200 Subject: [PATCH 097/145] Simplify untrusted regexp factory method --- lib/gitlab/untrusted_regexp.rb | 15 ++++++--------- .../pipeline/expression/lexeme/pattern_spec.rb | 4 ++-- spec/lib/gitlab/untrusted_regexp_spec.rb | 16 ++++++---------- .../malicious_regexp_shared_examples.rb | 2 ++ 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb index 70d1a7c6535..dc2d91dfa23 100644 --- a/lib/gitlab/untrusted_regexp.rb +++ b/lib/gitlab/untrusted_regexp.rb @@ -55,7 +55,7 @@ module Gitlab end def self.valid?(pattern) - self.fabricate(pattern) + !!self.fabricate(pattern) rescue RegexpError false end @@ -63,16 +63,13 @@ module Gitlab def self.fabricate(pattern) matches = pattern.match(%r{^/(?.+)/(?[ismU]*)$}) - if matches - expression = matches[:regexp] - flags = matches[:flags] + raise RegexpError, 'Invalid regular expression!' if matches.nil? - expression.prepend("(?#{flags})") if flags.present? + expression = matches[:regexp] + flags = matches[:flags] + expression.prepend("(?#{flags})") if flags.present? - self.new(expression, multiline: false) - else - self.new(pattern, multiline: false) - end + self.new(expression, multiline: false) end private diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb index c63c38b1dbc..3ebc2e94727 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb @@ -79,7 +79,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do describe '#evaluate' do it 'returns a regular expression' do - regexp = described_class.new('abc') + regexp = described_class.new('/abc/') expect(regexp.evaluate).to eq Gitlab::UntrustedRegexp.new('abc') end @@ -87,7 +87,7 @@ describe Gitlab::Ci::Pipeline::Expression::Lexeme::Pattern do it 'raises error if evaluated regexp is not valid' do allow(Gitlab::UntrustedRegexp).to receive(:valid?).and_return(true) - regexp = described_class.new('invalid ( .*') + regexp = described_class.new('/invalid ( .*/') expect { regexp.evaluate } .to raise_error(Gitlab::Ci::Pipeline::Expression::RuntimeError) diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb index 4bca320ac2c..0a6ac0aa294 100644 --- a/spec/lib/gitlab/untrusted_regexp_spec.rb +++ b/spec/lib/gitlab/untrusted_regexp_spec.rb @@ -4,9 +4,13 @@ require 'support/shared_examples/malicious_regexp_shared_examples' describe Gitlab::UntrustedRegexp do describe '.valid?' do it 'returns true if regexp is valid' do + expect(described_class.valid?('/some ( thing/')) + .to be false end it 'returns true if regexp is invalid' do + expect(described_class.valid?('/some .* thing/')) + .to be true end end @@ -32,17 +36,9 @@ describe Gitlab::UntrustedRegexp do end end - context 'when regexp is not plain pattern' do - it 'fabricates regexp without flags' do - regexp = described_class.fabricate('something') - - expect(regexp).to eq described_class.new('something') - end - end - - context 'when regexp is invalid' do + context 'when regexp is a raw pattern' do it 'raises an error' do - expect { described_class.fabricate('/some ( thing/') } + expect { described_class.fabricate('some .* thing') } .to raise_error(RegexpError) end end diff --git a/spec/support/shared_examples/malicious_regexp_shared_examples.rb b/spec/support/shared_examples/malicious_regexp_shared_examples.rb index ac5d22298bb..65026f1d7c0 100644 --- a/spec/support/shared_examples/malicious_regexp_shared_examples.rb +++ b/spec/support/shared_examples/malicious_regexp_shared_examples.rb @@ -1,3 +1,5 @@ +require 'timeout' + shared_examples 'malicious regexp' do let(:malicious_text) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!' } let(:malicious_regexp) { '(?i)^(([a-z])+.)+[A-Z]([a-z])+$' } From afa245142117a7e90ff6046133a2402fb8c09cb1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 18 May 2018 10:14:19 +0200 Subject: [PATCH 098/145] Simplify pattern lexeme fabrication and matcher --- lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb index 70a221010f3..9b239c29ea4 100644 --- a/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb +++ b/lib/gitlab/ci/pipeline/expression/lexeme/pattern.rb @@ -6,7 +6,7 @@ module Gitlab require_dependency 're2' class Pattern < Lexeme::Value - PATTERN = %r{^(?/.+/[ismU]*)$}.freeze + PATTERN = %r{^/.+/[ismU]*$}.freeze def initialize(regexp) @value = regexp @@ -23,7 +23,7 @@ module Gitlab end def self.build(string) - new(string.match(PATTERN)[:regexp]) + new(string) end end end From 7feef84e1e60bef5cd460345125cc83dd23350cc Mon Sep 17 00:00:00 2001 From: Winnie Hellmann Date: Fri, 18 May 2018 08:39:42 +0000 Subject: [PATCH 099/145] Make stores export a createStore() which can be used in tests --- doc/development/fe_guide/vuex.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md index 8997a5889dc..858b03c60bf 100644 --- a/doc/development/fe_guide/vuex.md +++ b/doc/development/fe_guide/vuex.md @@ -37,12 +37,13 @@ import state from './state'; Vue.use(Vuex); -export default new Vuex.Store({ +export const createStore = () => new Vuex.Store({ actions, getters, mutations, state, }); +export default createStore(); ``` ### `state.js` @@ -320,10 +321,11 @@ In order to write unit tests for those components, we need to include the store ```javascript //component_spec.js import Vue from 'vue'; -import store from './store'; +import { createStore } from './store'; import component from './component.vue' describe('component', () => { + let store; let vm; let Component; @@ -340,6 +342,8 @@ describe('component', () => { name: 'Foo', age: '30', }; + + store = createStore(); // populate the store store.dispatch('addUser', user); From 9f7deb85b5d6937e6cf7068b864f49693b4a2623 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 18 May 2018 11:28:32 +0200 Subject: [PATCH 100/145] Do not allow to use `CI_PIPELINE_ID` in environment name --- app/models/ci/pipeline.rb | 9 +++++++-- spec/models/ci/pipeline_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index c26f0b6dcdc..7d7349b04bc 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -523,9 +523,14 @@ module Ci strong_memoize(:legacy_trigger) { trigger_requests.first } end + def persisted_variables + Gitlab::Ci::Variables::Collection.new.tap do |variables| + variables.append(key: 'CI_PIPELINE_ID', value: id.to_s) if persisted? + end + end + def predefined_variables - Gitlab::Ci::Variables::Collection.new - .append(key: 'CI_PIPELINE_ID', value: id.to_s) + persisted_variables .append(key: 'CI_CONFIG_PATH', value: ci_yaml_file_path) .append(key: 'CI_PIPELINE_SOURCE', value: source.to_s) .append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message) diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index e7845b693a1..0e393aa362b 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -167,6 +167,28 @@ describe Ci::Pipeline, :mailer do end end + describe '#persisted_variables' do + context 'when pipeline is not persisted yet' do + subject { build(:ci_pipeline).persisted_variables } + + it 'does not contain some variables' do + keys = subject.map { |variable| variable[:key] } + + expect(keys).not_to include 'CI_PIPELINE_ID' + end + end + + context 'when pipeline is persisted' do + subject { build_stubbed(:ci_pipeline).persisted_variables } + + it 'does not contain some variables' do + keys = subject.map { |variable| variable[:key] } + + expect(keys).to include 'CI_PIPELINE_ID' + end + end + end + describe '#predefined_variables' do subject { pipeline.predefined_variables } From 1f2c56dda538b11f95a6ec0b16a57a46c4182113 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Fri, 18 May 2018 10:38:43 +0100 Subject: [PATCH 101/145] Update EE > CE downgrade service removal steps --- doc/downgrade_ee_to_ce/README.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md index f656057e3da..236408762e3 100644 --- a/doc/downgrade_ee_to_ce/README.md +++ b/doc/downgrade_ee_to_ce/README.md @@ -15,9 +15,9 @@ Kerberos and Atlassian Crowd are only available on the Enterprise Edition, so you should disable these mechanisms before downgrading and you should provide alternative authentication methods to your users. -### Remove Jenkins CI Service entries from the database +### Remove Service Integration entries from the database -The `JenkinsService` class is only available on the Enterprise Edition codebase, +The `JenkinsService` and `GithubService` classes are only available in the Enterprise Edition codebase, so if you downgrade to the Community Edition, you'll come across the following error: @@ -30,20 +30,31 @@ column if you didn't intend it to be used for storing the inheritance class or o use another column for that information.) ``` +or + +``` +Completed 500 Internal Server Error in 497ms (ActiveRecord: 32.2ms) + +ActionView::Template::Error (The single-table inheritance mechanism failed to locate the subclass: 'GithubService'. This +error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this +column if you didn't intend it to be used for storing the inheritance class or overwrite Service.inheritance_column to +use another column for that information.) +``` + All services are created automatically for every project you have, so in order to avoid getting this error, you need to remove all instances of the -`JenkinsService` from your database: +`JenkinsService` and `GithubService` from your database: **Omnibus Installation** ``` -$ sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all" +$ sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService', 'GithubService']).delete_all" ``` **Source Installation** ``` -$ bundle exec rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService']).delete_all" production +$ bundle exec rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService', 'GithubService']).delete_all" production ``` ### Secret variables environment scopes From 51214bf0e179e3c41d373595397b2a2d10fcb379 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 15 May 2018 19:31:35 +0200 Subject: [PATCH 102/145] Add docs for incremental rollouts --- doc/topics/autodevops/img/rollout_enabled.png | Bin 0 -> 19536 bytes .../img/rollout_staging_disabled.png | Bin 0 -> 13837 bytes .../img/rollout_staging_enabled.png | Bin 0 -> 17306 bytes doc/topics/autodevops/img/staging_enabled.png | Bin 0 -> 17929 bytes doc/topics/autodevops/index.md | 52 ++++++++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 doc/topics/autodevops/img/rollout_enabled.png create mode 100644 doc/topics/autodevops/img/rollout_staging_disabled.png create mode 100644 doc/topics/autodevops/img/rollout_staging_enabled.png create mode 100644 doc/topics/autodevops/img/staging_enabled.png diff --git a/doc/topics/autodevops/img/rollout_enabled.png b/doc/topics/autodevops/img/rollout_enabled.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e7d790cf2a422189bc8793bcfa136413b0c034 GIT binary patch literal 19536 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 Qg?D|Ov^j0fIN=%nH~xT-2mk;8 literal 0 HcmV?d00001 diff --git a/doc/topics/autodevops/img/rollout_staging_disabled.png b/doc/topics/autodevops/img/rollout_staging_disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..71e36b440f0539ca6882d204bd424d15fdec7fb7 GIT binary patch literal 13837 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 V;`q0cp?|0)4^ft`kb2|)zX0sQ^C|!U literal 0 HcmV?d00001 diff --git a/doc/topics/autodevops/img/rollout_staging_enabled.png b/doc/topics/autodevops/img/rollout_staging_enabled.png new file mode 100644 index 0000000000000000000000000000000000000000..d0d1d3566277e3b61340825d1a905c78ca2c9984 GIT binary patch literal 17306 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 NJYD@<);T3K0RU+|RI300 literal 0 HcmV?d00001 diff --git a/doc/topics/autodevops/img/staging_enabled.png b/doc/topics/autodevops/img/staging_enabled.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef1a67d641e1bf7fe516bd4872c8da25fb89f99 GIT binary patch literal 17929 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 [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5415) in GitLab 10.8. + +When you have a new version of your app to deploy in production, you may want +to use an incremental rollout to replace just a few pods with the latest code. +This will allow you to first check how the app is behaving, and later manually +increasing the rollout up to 100%. + +If `INCREMENTAL_ROLLOUT_ENABLED` is defined in your project (e.g., set +`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a secret variable), then instead of the +standard `production` job, 4 different +[manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph) +will be created: + +1. `rollout 10%` +1. `rollout 25%` +1. `rollout 50%` +1. `rollout 100%` + +The percentage is based on the `REPLICAS` variable and defines the number of +pods you want to have for your deployment. If you say `10`, and then you run +the `10%` rollout job, there will be `1` new pod + `9` old ones. + +To start a job, click on the play icon next to the job's name. You are not +required to go from `10%` to `100%`, you can jump to whatever job you want. +You can also scale down by running a lower percentage job, just before hitting +`100%`. Once you get to `100%`, you cannot scale down, and you'd have to roll +back by redeploying the old version using the +[rollback button](../../ci/environments.md#rolling-back-changes) in the +environment page. + +Below, you can see how the pipeline will look if the rollout or staging +variables are defined. + +- **Without `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`** + + ![Staging and rollout disabled](img/rollout_staging_disabled.png) + +- **Without `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`** + + ![Staging enabled](img/staging_enabled.png) + +- **With `INCREMENTAL_ROLLOUT_ENABLED` and without `STAGING_ENABLED`** + + ![Rollout enabled](img/rollout_enabled.png) + +- **With `INCREMENTAL_ROLLOUT_ENABLED` and with `STAGING_ENABLED`** + + ![Rollout and staging enabled](img/rollout_staging_enabled.png) + ## Currently supported languages NOTE: **Note:** From 2ad01c5ab079959205c236df7bf8cbb5c1fab573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 19 Apr 2018 18:53:07 +0200 Subject: [PATCH 103/145] Ensure Flipper memoizer is used in Sidekiq's context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, don't use the provided Middleware, which isn't thread-safe, and instantiate a new Flipper instance per thread instead. Signed-off-by: Rémy Coutable --- config/initializers/flipper.rb | 21 --------------------- lib/feature.rb | 17 ++++++++++++++++- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb index c60ad535fd5..80cab7273e5 100644 --- a/config/initializers/flipper.rb +++ b/config/initializers/flipper.rb @@ -1,22 +1 @@ -require 'flipper/adapters/active_record' -require 'flipper/adapters/active_support_cache_store' - -Flipper.configure do |config| - config.default do - adapter = Flipper::Adapters::ActiveRecord.new( - feature_class: Feature::FlipperFeature, gate_class: Feature::FlipperGate) - cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new( - adapter, - Rails.cache, - expires_in: 1.hour) - - Flipper.new(cached_adapter) - end -end - Feature.register_feature_groups - -unless Rails.env.test? - require 'flipper/middleware/memoizer' - Rails.application.config.middleware.use Flipper::Middleware::Memoizer -end diff --git a/lib/feature.rb b/lib/feature.rb index 8e9ba5c530a..6474de6e56d 100644 --- a/lib/feature.rb +++ b/lib/feature.rb @@ -1,3 +1,6 @@ +require 'flipper/adapters/active_record' +require 'flipper/adapters/active_support_cache_store' + class Feature # Classes to override flipper table names class FlipperFeature < Flipper::Adapters::ActiveRecord::Feature @@ -60,7 +63,8 @@ class Feature end def flipper - @flipper ||= Flipper.instance + Thread.current[:flipper] ||= + Flipper.new(flipper_adapter).tap { |flip| flip.memoize = true } end # This method is called from config/initializers/flipper.rb and can be used @@ -68,5 +72,16 @@ class Feature # See https://docs.gitlab.com/ee/development/feature_flags.html#feature-groups def register_feature_groups end + + def flipper_adapter + active_record_adapter = Flipper::Adapters::ActiveRecord.new( + feature_class: FlipperFeature, + gate_class: FlipperGate) + + Flipper::Adapters::ActiveSupportCacheStore.new( + active_record_adapter, + Rails.cache, + expires_in: 1.hour) + end end end From c0e77f7c9cc24104981bb8f6973ceeb9c311e1e2 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 18 May 2018 10:25:59 +0000 Subject: [PATCH 104/145] Resolve "Expand API: Render an arbitrary Markdown document" --- ...-render-an-arbitrary-markdown-document.yml | 5 + doc/api/README.md | 1 + doc/api/markdown.md | 29 +++++ lib/api/api.rb | 1 + lib/api/markdown.rb | 33 ++++++ lib/banzai/filter/reference_filter.rb | 2 +- lib/banzai/pipeline/gfm_pipeline.rb | 4 +- spec/requests/api/markdown_spec.rb | 112 ++++++++++++++++++ 8 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/19861-expand-api-render-an-arbitrary-markdown-document.yml create mode 100644 doc/api/markdown.md create mode 100644 lib/api/markdown.rb create mode 100644 spec/requests/api/markdown_spec.rb diff --git a/changelogs/unreleased/19861-expand-api-render-an-arbitrary-markdown-document.yml b/changelogs/unreleased/19861-expand-api-render-an-arbitrary-markdown-document.yml new file mode 100644 index 00000000000..a97e8a2b5cc --- /dev/null +++ b/changelogs/unreleased/19861-expand-api-render-an-arbitrary-markdown-document.yml @@ -0,0 +1,5 @@ +--- +title: Add API endpoint to render markdown text +merge_request: 18926 +author: "@blackst0ne" +type: added diff --git a/doc/api/README.md b/doc/api/README.md index e777fc63d2b..194907accc7 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -33,6 +33,7 @@ following locations: - [Jobs](jobs.md) - [Keys](keys.md) - [Labels](labels.md) +- [Markdown](markdown.md) - [Merge Requests](merge_requests.md) - [Project milestones](milestones.md) - [Group milestones](group_milestones.md) diff --git a/doc/api/markdown.md b/doc/api/markdown.md new file mode 100644 index 00000000000..f406838e887 --- /dev/null +++ b/doc/api/markdown.md @@ -0,0 +1,29 @@ +# Markdown API + +> [Introduced][ce-18926] in GitLab 11.0. + +Available only in APIv4. + +## Render an arbitrary Markdown document + +``` +POST /api/v4/markdown +``` + +| Attribute | Type | Required | Description | +| --------- | ------- | ------------- | ------------------------------------------ | +| `text` | string | yes | The markdown text to render | +| `gfm` | boolean | no (optional) | Render text using GitLab Flavored Markdown. Default is `false` | +| `project` | string | no (optional) | Use `project` as a context when creating references using GitLab Flavored Markdown. [Authentication](README.html#authentication) is required if a project is not public. | + +```bash +curl --header Content-Type:application/json --data '{"text":"Hello world! :tada:", "gfm":true, "project":"group_example/project_example"}' https://gitlab.example.com/api/v4/markdown +``` + +Response example: + +```json +{ "html": "

Hello world! 🎉

" } +``` + +[ce-18926]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18926 diff --git a/lib/api/api.rb b/lib/api/api.rb index 2fbeaaffcfe..de20b2b8e67 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -140,6 +140,7 @@ module API mount ::API::Keys mount ::API::Labels mount ::API::Lint + mount ::API::Markdown mount ::API::Members mount ::API::MergeRequestDiffs mount ::API::MergeRequests diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb new file mode 100644 index 00000000000..b9ed68aa584 --- /dev/null +++ b/lib/api/markdown.rb @@ -0,0 +1,33 @@ +module API + class Markdown < Grape::API + params do + requires :text, type: String, desc: "The markdown text to render" + optional :gfm, type: Boolean, desc: "Render text using GitLab Flavored Markdown" + optional :project, type: String, desc: "The full path of a project to use as the context when creating references using GitLab Flavored Markdown" + end + resource :markdown do + desc "Render markdown text" do + detail "This feature was introduced in GitLab 11.0." + end + post do + # Explicitly set CommonMark as markdown engine to use. + # Remove this set when https://gitlab.com/gitlab-org/gitlab-ce/issues/43011 is done. + context = { markdown_engine: :common_mark, only_path: false } + + if params[:project] + project = Project.find_by_full_path(params[:project]) + + not_found!("Project") unless can?(current_user, :read_project, project) + + context[:project] = project + else + context[:skip_project_check] = true + end + + context[:pipeline] = params[:gfm] ? :full : :plain_markdown + + { html: Banzai.render(params[:text], context) } + end + end + end +end diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index b9d5ecf70ec..2f023f4f242 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -73,7 +73,7 @@ module Banzai # # Note that while the key might exist, its value could be nil! def validate - needs :project + needs :project unless skip_project_check? end # Iterates over all
and text() nodes in a document. diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 8b2f05fffec..a1f24e8b093 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -42,9 +42,9 @@ module Banzai end def self.transform_context(context) - context.merge( - only_path: true, + context[:only_path] = true unless context.key?(:only_path) + context.merge( # EmojiFilter asset_host: Gitlab::Application.config.asset_host, asset_root: Gitlab.config.gitlab.base_url diff --git a/spec/requests/api/markdown_spec.rb b/spec/requests/api/markdown_spec.rb new file mode 100644 index 00000000000..a55796cf343 --- /dev/null +++ b/spec/requests/api/markdown_spec.rb @@ -0,0 +1,112 @@ +require "spec_helper" + +describe API::Markdown do + RSpec::Matchers.define_negated_matcher :exclude, :include + + describe "POST /markdown" do + let(:user) {} # No-op. It gets overwritten in the contexts below. + + before do + post api("/markdown", user), params + end + + shared_examples "rendered markdown text without GFM" do + it "renders markdown text" do + expect(response).to have_http_status(201) + expect(response.headers["Content-Type"]).to eq("application/json") + expect(json_response).to be_a(Hash) + expect(json_response["html"]).to eq("

#{text}

") + end + end + + shared_examples "404 Project Not Found" do + it "responses with 404 Not Found" do + expect(response).to have_http_status(404) + expect(response.headers["Content-Type"]).to eq("application/json") + expect(json_response).to be_a(Hash) + expect(json_response["message"]).to eq("404 Project Not Found") + end + end + + context "when arguments are invalid" do + context "when text is missing" do + let(:params) { {} } + + it "responses with 400 Bad Request" do + expect(response).to have_http_status(400) + expect(response.headers["Content-Type"]).to eq("application/json") + expect(json_response).to be_a(Hash) + expect(json_response["error"]).to eq("text is missing") + end + end + + context "when project is not found" do + let(:params) { { text: "Hello world!", gfm: true, project: "Dummy project" } } + + it_behaves_like "404 Project Not Found" + end + end + + context "when arguments are valid" do + set(:project) { create(:project) } + set(:issue) { create(:issue, project: project) } + let(:text) { ":tada: Hello world! :100: #{issue.to_reference}" } + + context "when not using gfm" do + context "without project" do + let(:params) { { text: text } } + + it_behaves_like "rendered markdown text without GFM" + end + + context "with project" do + let(:params) { { text: text, project: project.full_path } } + + context "when not authorized" do + it_behaves_like "404 Project Not Found" + end + + context "when authorized" do + let(:user) { project.owner } + + it_behaves_like "rendered markdown text without GFM" + end + end + end + + context "when using gfm" do + context "without project" do + let(:params) { { text: text, gfm: true } } + + it "renders markdown text" do + expect(response).to have_http_status(201) + expect(response.headers["Content-Type"]).to eq("application/json") + expect(json_response).to be_a(Hash) + expect(json_response["html"]).to include("Hello world!") + .and include('data-name="tada"') + .and include('data-name="100"') + .and include("#1") + .and exclude("
") + end + end + + context "with project" do + let(:params) { { text: text, gfm: true, project: project.full_path } } + let(:user) { project.owner } + + it "renders markdown text" do + expect(response).to have_http_status(201) + expect(response.headers["Content-Type"]).to eq("application/json") + expect(json_response).to be_a(Hash) + expect(json_response["html"]).to include("Hello world!") + .and include('data-name="tada"') + .and include('data-name="100"') + .and include("") + end + end + end + end + end +end From 61a5994db6f8b1e98f7ee1460f8aadb2ddb6c713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kim=20=22BKC=22=20Carlb=C3=A4cker?= Date: Fri, 4 May 2018 15:53:01 +0200 Subject: [PATCH 105/145] Migrate RepositoryService#SearchFilesBy{Content,Name} --- GITALY_SERVER_VERSION | 2 +- lib/gitlab/git/repository.rb | 26 +++++++++++++++---- .../gitaly_client/repository_service.rb | 10 +++++++ spec/models/repository_spec.rb | 14 ++++++++-- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 897e21587ed..7bb21aff834 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.100.0 +0.102.0 diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 061865a7acf..f043df7eba5 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1473,10 +1473,19 @@ module Gitlab def search_files_by_content(query, ref) return [] if empty? || query.blank? - offset = 2 - args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) + safe_query = Regexp.escape(query) + ref ||= root_ref - run_git(args).first.scrub.split(/^--\n/) + gitaly_migrate(:search_files_by_content) do |is_enabled| + if is_enabled + gitaly_repository_client.search_files_by_content(ref, safe_query) + else + offset = 2 + args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{safe_query} #{ref}) + + run_git(args).first.scrub.split(/^--\n/) + end + end end def can_be_merged?(source_sha, target_branch) @@ -1491,12 +1500,19 @@ module Gitlab def search_files_by_name(query, ref) safe_query = Regexp.escape(query.sub(%r{^/*}, "")) + ref ||= root_ref return [] if empty? || safe_query.blank? - args = %W(ls-tree -r --name-status --full-tree #{ref || root_ref} -- #{safe_query}) + gitaly_migrate(:search_files_by_name) do |is_enabled| + if is_enabled + gitaly_repository_client.search_files_by_name(ref, safe_query) + else + args = %W(ls-tree -r --name-status --full-tree #{ref} -- #{safe_query}) - run_git(args).first.lines.map(&:strip) + run_git(args).first.lines.map(&:strip) + end + end end def find_commits_by_message(query, ref, path, limit, offset) diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 132a5947f17..ee01f5a5bd9 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -301,6 +301,16 @@ module Gitlab GitalyClient.call(@storage, :repository_service, :get_raw_changes, request) end + + def search_files_by_name(ref, query) + request = Gitaly::SearchFilesByNameRequest.new(repository: @gitaly_repo, ref: ref, query: query) + GitalyClient.call(@storage, :repository_service, :search_files_by_name, request).flat_map(&:files) + end + + def search_files_by_content(ref, query) + request = Gitaly::SearchFilesByContentRequest.new(repository: @gitaly_repo, ref: ref, query: query) + GitalyClient.call(@storage, :repository_service, :search_files_by_content, request).flat_map(&:matches) + end end end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index ac8d9a32d4e..6bc148a1392 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -671,7 +671,7 @@ describe Repository do end end - describe "search_files_by_content" do + shared_examples "search_files_by_content" do let(:results) { repository.search_files_by_content('feature', 'master') } subject { results } @@ -718,7 +718,7 @@ describe Repository do end end - describe "search_files_by_name" do + shared_examples "search_files_by_name" do let(:results) { repository.search_files_by_name('files', 'master') } it 'returns result' do @@ -758,6 +758,16 @@ describe Repository do end end + describe 'with gitaly enabled' do + it_behaves_like 'search_files_by_content' + it_behaves_like 'search_files_by_name' + end + + describe 'with gitaly disabled', :disable_gitaly do + it_behaves_like 'search_files_by_content' + it_behaves_like 'search_files_by_name' + end + describe '#async_remove_remote' do before do masterrev = repository.find_branch('master').dereferenced_target From d9a3f020be570f135c0ce7c7676b4c1ed332ce1d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 18 May 2018 14:05:29 +0200 Subject: [PATCH 106/145] Separate persisted and runtime pipeline variables --- app/models/ci/build.rb | 1 + app/models/ci/pipeline.rb | 2 +- spec/models/ci/build_spec.rb | 8 +++++++ .../ci/create_pipeline_service_spec.rb | 21 ++++++++++++++++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 61c10c427dd..78f054a6527 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -599,6 +599,7 @@ module Ci break variables unless persisted? variables + .concat(pipeline.persisted_variables) .append(key: 'CI_JOB_ID', value: id.to_s) .append(key: 'CI_JOB_TOKEN', value: token, public: false) .append(key: 'CI_BUILD_ID', value: id.to_s) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 7d7349b04bc..53af87a271a 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -530,7 +530,7 @@ module Ci end def predefined_variables - persisted_variables + Gitlab::Ci::Variables::Collection.new .append(key: 'CI_CONFIG_PATH', value: ci_yaml_file_path) .append(key: 'CI_PIPELINE_SOURCE', value: source.to_s) .append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message) diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index dc810489011..d7659a9cb58 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -629,6 +629,14 @@ describe Ci::Build do it { is_expected.to eq('review/host') } end + + context 'when using persisted variables' do + let(:build) do + create(:ci_build, environment: 'review/x$CI_BUILD_ID') + end + + it { is_expected.to eq('review/x') } + end end describe '#starts_environment?' do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 9a0b6efd8a9..be0255a2ccc 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -395,7 +395,26 @@ describe Ci::CreatePipelineService do result = execute_service expect(result).to be_persisted - expect(Environment.find_by(name: "review/master")).not_to be_nil + expect(Environment.find_by(name: "review/master")).to be_present + end + end + + context 'with environment name including persisted variables' do + before do + config = YAML.dump( + deploy: { + environment: { name: "review/id1$CI_PIPELINE_ID/id2$CI_BUILD_ID" }, + script: 'ls' } + ) + + stub_ci_pipeline_yaml_file(config) + end + + it 'skipps persisted variables in environment name' do + result = execute_service + + expect(result).to be_persisted + expect(Environment.find_by(name: "review/id1/id2")).to be_present end end From 5d462e9d9893e7f27d45964eeca0f045e651c2a7 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 18 May 2018 14:12:46 +0200 Subject: [PATCH 107/145] Update docs about unsupported environment name variables --- doc/ci/environments.md | 1 + doc/ci/variables/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/ci/environments.md b/doc/ci/environments.md index 517e25f00f7..3a491f0073c 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -252,6 +252,7 @@ including predefined, secure variables and `.gitlab-ci.yml` [`variables`](yaml/README.md#variables). You however cannot use variables defined under `script` or on the Runner's side. There are other variables that are unsupported in environment name context: +- `CI_PIPELINE_ID` - `CI_JOB_ID` - `CI_JOB_TOKEN` - `CI_BUILD_ID` diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 42367bf13f7..7e795ba973b 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -543,6 +543,7 @@ We do not support variables containing tokens because of security reasons. You can find a full list of unsupported variables below: +- `CI_PIPELINE_ID` - `CI_JOB_ID` - `CI_JOB_TOKEN` - `CI_BUILD_ID` From eb63895f04e85224bd8616588d6fb95da459a614 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 18 May 2018 14:14:08 +0200 Subject: [PATCH 108/145] Add changelog log entry for persisted variables fix --- ...-gb-exclude-persisted-variables-from-environment-name.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/fix-gb-exclude-persisted-variables-from-environment-name.yml diff --git a/changelogs/unreleased/fix-gb-exclude-persisted-variables-from-environment-name.yml b/changelogs/unreleased/fix-gb-exclude-persisted-variables-from-environment-name.yml new file mode 100644 index 00000000000..92426832f30 --- /dev/null +++ b/changelogs/unreleased/fix-gb-exclude-persisted-variables-from-environment-name.yml @@ -0,0 +1,5 @@ +--- +title: Exclude CI_PIPELINE_ID from variables supported in dynamic environment name +merge_request: 19032 +author: +type: fixed From b5d7937e42126040733fead66f8bc3fabfb5fbe2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 18 May 2018 14:19:57 +0200 Subject: [PATCH 109/145] Update pipeline persisted / predefined variables specs --- spec/models/ci/build_spec.rb | 2 +- spec/models/ci/pipeline_spec.rb | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index d7659a9cb58..884fbad6ccf 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1493,6 +1493,7 @@ describe Ci::Build do let(:container_registry_enabled) { false } let(:predefined_variables) do [ + { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }, { key: 'CI_JOB_ID', value: build.id.to_s, public: true }, { key: 'CI_JOB_TOKEN', value: build.token, public: false }, { key: 'CI_BUILD_ID', value: build.id.to_s, public: true }, @@ -1524,7 +1525,6 @@ describe Ci::Build do { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true }, { key: 'CI_PROJECT_URL', value: project.web_url, public: true }, { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true }, - { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }, { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true }, { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true }, { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true }, diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 0e393aa362b..e4f4c62bd22 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -181,10 +181,10 @@ describe Ci::Pipeline, :mailer do context 'when pipeline is persisted' do subject { build_stubbed(:ci_pipeline).persisted_variables } - it 'does not contain some variables' do + it 'does contains persisted variables' do keys = subject.map { |variable| variable[:key] } - expect(keys).to include 'CI_PIPELINE_ID' + expect(keys).to eq %w[CI_PIPELINE_ID] end end end @@ -195,7 +195,11 @@ describe Ci::Pipeline, :mailer do it 'includes all predefined variables in a valid order' do keys = subject.map { |variable| variable[:key] } - expect(keys).to eq %w[CI_PIPELINE_ID CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION] + expect(keys).to eq %w[CI_CONFIG_PATH + CI_PIPELINE_SOURCE + CI_COMMIT_MESSAGE + CI_COMMIT_TITLE + CI_COMMIT_DESCRIPTION] end end From 2ebafdfb2f026580153fd2cf50f4b6b7ab3a0344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Fri, 18 May 2018 15:33:54 +0200 Subject: [PATCH 110/145] Improve cacheable module --- app/models/concerns/redis_cacheable.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index b5425295130..53f022e2b35 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -7,11 +7,11 @@ module RedisCacheable class_methods do def cached_attr_reader(*attributes) attributes.each do |attribute| - define_method(attribute) do - unless self.has_attribute?(attribute) - raise ArgumentError, "`cached_attr_reader` requires the #{self.class.name}\##{attribute} attribute to have a database column" - end + unless self.column_names.include?(attribute.to_s) + raise ArgumentError, "`cached_attr_reader` requires the #{self.name}##{attribute} to be a database attribute" + end + define_method(attribute) do cached_attribute(attribute) || read_attribute(attribute) end end @@ -50,7 +50,9 @@ module RedisCacheable if Gitlab.rails5? self.class.type_for_attribute(attribute).cast(value) else - self.class.column_for_attribute(attribute).type_cast_from_database(value) + ActiveSupport::Deprecation.silence do + self.class.column_for_attribute(attribute).type_cast_from_database(value) + end end end end From 18a8eb96b34db63101c2b1210c1f93342b138a55 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Fri, 11 May 2018 16:44:38 +0200 Subject: [PATCH 111/145] Calculating repository checksums executed by Gitaly OPT_OUT status has been removed, and alternative implementation removed. Also checks if the repository exists before executing the checksum RPC to guard against NotFound errors. Closes gitlab-org/gitaly#1105 --- .../zj-calculate-checksum-mandator.yml | 5 ++ lib/gitlab/git/repository.rb | 44 ++-------- spec/lib/gitlab/git/repository_spec.rb | 80 +++++++------------ 3 files changed, 39 insertions(+), 90 deletions(-) create mode 100644 changelogs/unreleased/zj-calculate-checksum-mandator.yml diff --git a/changelogs/unreleased/zj-calculate-checksum-mandator.yml b/changelogs/unreleased/zj-calculate-checksum-mandator.yml new file mode 100644 index 00000000000..83315a3c5dd --- /dev/null +++ b/changelogs/unreleased/zj-calculate-checksum-mandator.yml @@ -0,0 +1,5 @@ +--- +title: Remove shellout implementation for Repository checksums +merge_request: +author: +type: other diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 25487f53999..f612d9dc597 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1576,14 +1576,12 @@ module Gitlab end def checksum - gitaly_migrate(:calculate_checksum, - status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - if is_enabled - gitaly_repository_client.calculate_checksum - else - calculate_checksum_by_shelling_out - end - end + # The exists? RPC is much cheaper, so we perform this request first + raise NoRepository, "Repository does not exists" unless exists? + + gitaly_repository_client.calculate_checksum + rescue GRPC::NotFound + raise NoRepository # Guard against data races. end private @@ -2498,36 +2496,6 @@ module Gitlab rev_parse_target(ref).oid end - def calculate_checksum_by_shelling_out - raise NoRepository unless exists? - - args = %W(--git-dir=#{path} show-ref --heads --tags) - output, status = run_git(args) - - if status.nil? || !status.zero? - # Non-valid git repositories return 128 as the status code and an error output - raise InvalidRepository if status == 128 && output.to_s.downcase =~ /not a git repository/ - # Empty repositories returns with a non-zero status and an empty output. - raise ChecksumError, output unless output.blank? - - return EMPTY_REPOSITORY_CHECKSUM - end - - refs = output.split("\n") - - result = refs.inject(nil) do |checksum, ref| - value = Digest::SHA1.hexdigest(ref).hex - - if checksum.nil? - value - else - checksum ^ value - end - end - - result.to_s(16) - end - def build_git_cmd(*args) object_directories = alternate_object_directories.join(File::PATH_SEPARATOR) diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index fcb690d8aa3..2b5710ac401 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -2247,66 +2247,42 @@ describe Gitlab::Git::Repository, seed_helper: true do end describe '#checksum' do - shared_examples 'calculating checksum' do - it 'calculates the checksum for non-empty repo' do - expect(repository.checksum).to eq '54f21be4c32c02f6788d72207fa03ad3bce725e4' - end - - it 'returns 0000000000000000000000000000000000000000 for an empty repo' do - FileUtils.rm_rf(File.join(storage_path, 'empty-repo.git')) - - system(git_env, *%W(#{Gitlab.config.git.bin_path} init --bare empty-repo.git), - chdir: storage_path, - out: '/dev/null', - err: '/dev/null') - - empty_repo = described_class.new('default', 'empty-repo.git', '') - - expect(empty_repo.checksum).to eq '0000000000000000000000000000000000000000' - end - - it 'raises Gitlab::Git::Repository::InvalidRepository error for non-valid git repo' do - FileUtils.rm_rf(File.join(storage_path, 'non-valid.git')) - - system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{TEST_REPO_PATH} non-valid.git), - chdir: SEED_STORAGE_PATH, - out: '/dev/null', - err: '/dev/null') - - File.truncate(File.join(storage_path, 'non-valid.git/HEAD'), 0) - - non_valid = described_class.new('default', 'non-valid.git', '') - - expect { non_valid.checksum }.to raise_error(Gitlab::Git::Repository::InvalidRepository) - end - - it 'raises Gitlab::Git::Repository::NoRepository error when there is no repo' do - broken_repo = described_class.new('default', 'a/path.git', '') - - expect { broken_repo.checksum }.to raise_error(Gitlab::Git::Repository::NoRepository) - end + it 'calculates the checksum for non-empty repo' do + expect(repository.checksum).to eq '54f21be4c32c02f6788d72207fa03ad3bce725e4' end - context 'when calculate_checksum Gitaly feature is enabled' do - it_behaves_like 'calculating checksum' + it 'returns 0000000000000000000000000000000000000000 for an empty repo' do + FileUtils.rm_rf(File.join(storage_path, 'empty-repo.git')) + + system(git_env, *%W(#{Gitlab.config.git.bin_path} init --bare empty-repo.git), + chdir: storage_path, + out: '/dev/null', + err: '/dev/null') + + empty_repo = described_class.new('default', 'empty-repo.git', '') + + expect(empty_repo.checksum).to eq '0000000000000000000000000000000000000000' end - context 'when calculate_checksum Gitaly feature is disabled', :disable_gitaly do - it_behaves_like 'calculating checksum' + it 'raises Gitlab::Git::Repository::InvalidRepository error for non-valid git repo' do + FileUtils.rm_rf(File.join(storage_path, 'non-valid.git')) - describe 'when storage is broken', :broken_storage do - it 'raises a storage exception when storage is not available' do - broken_repo = described_class.new('broken', 'a/path.git', '') + system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{TEST_REPO_PATH} non-valid.git), + chdir: SEED_STORAGE_PATH, + out: '/dev/null', + err: '/dev/null') - expect { broken_repo.rugged }.to raise_error(Gitlab::Git::Storage::Inaccessible) - end - end + File.truncate(File.join(storage_path, 'non-valid.git/HEAD'), 0) - it "raises a Gitlab::Git::Repository::Failure error if the `popen` call to git returns a non-zero exit code" do - allow(repository).to receive(:popen).and_return(['output', nil]) + non_valid = described_class.new('default', 'non-valid.git', '') - expect { repository.checksum }.to raise_error Gitlab::Git::Repository::ChecksumError - end + expect { non_valid.checksum }.to raise_error(Gitlab::Git::Repository::InvalidRepository) + end + + it 'raises Gitlab::Git::Repository::NoRepository error when there is no repo' do + broken_repo = described_class.new('default', 'a/path.git', '') + + expect { broken_repo.checksum }.to raise_error(Gitlab::Git::Repository::NoRepository) end end From 73903ae8849c4ec833964ec8ad6b18d04083bd64 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 18 May 2018 14:07:06 +0100 Subject: [PATCH 112/145] Fix a RuntimeError: cannot modify frozen string --- lib/gitlab/git/blob.rb | 20 ++++++++++---------- lib/gitlab/git/path_helper.rb | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb index d78d29b7ac6..156d077a69c 100644 --- a/lib/gitlab/git/blob.rb +++ b/lib/gitlab/git/blob.rb @@ -104,25 +104,22 @@ module Gitlab # file.rb # oid: 4a # # - # Blob.find_entry_by_path(repo, '1a', 'app/file.rb') # => '4a' + # Blob.find_entry_by_path(repo, '1a', 'blog', 'app', 'file.rb') # => '4a' # - def find_entry_by_path(repository, root_id, path) + def find_entry_by_path(repository, root_id, *path_parts) root_tree = repository.lookup(root_id) - # Strip leading slashes - path[%r{^/*}] = '' - path_arr = path.split('/') entry = root_tree.find do |entry| - entry[:name] == path_arr[0] + entry[:name] == path_parts[0] end return nil unless entry - if path_arr.size > 1 + if path_parts.size > 1 return nil unless entry[:type] == :tree - path_arr.shift - find_entry_by_path(repository, entry[:oid], path_arr.join('/')) + path_parts.shift + find_entry_by_path(repository, entry[:oid], *path_parts) else [:blob, :commit].include?(entry[:type]) ? entry : nil end @@ -185,10 +182,13 @@ module Gitlab def find_by_rugged(repository, sha, path, limit:) return unless path + # Strip any leading / characters from the path + path = path.sub(%r{\A/*}, '') + rugged_commit = repository.lookup(sha) root_tree = rugged_commit.tree - blob_entry = find_entry_by_path(repository, root_tree.oid, path) + blob_entry = find_entry_by_path(repository, root_tree.oid, *path.split('/')) return nil unless blob_entry diff --git a/lib/gitlab/git/path_helper.rb b/lib/gitlab/git/path_helper.rb index 155cf52f050..57b82a37d6c 100644 --- a/lib/gitlab/git/path_helper.rb +++ b/lib/gitlab/git/path_helper.rb @@ -6,7 +6,7 @@ module Gitlab class << self def normalize_path(filename) # Strip all leading slashes so that //foo -> foo - filename[%r{^/*}] = '' + filename = filename.sub(%r{\A/*}, '') # Expand relative paths (e.g. foo/../bar) filename = Pathname.new(filename) From 0a581fcfa28696245be297da39b12207a8ae0255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 18 May 2018 13:18:06 +0200 Subject: [PATCH 113/145] Minimize CE/EE difference in Gitlab::Auth::Saml::Config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/auth/saml/config.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/gitlab/auth/saml/config.rb b/lib/gitlab/auth/saml/config.rb index 2760b1a3247..5fa9581f837 100644 --- a/lib/gitlab/auth/saml/config.rb +++ b/lib/gitlab/auth/saml/config.rb @@ -14,6 +14,10 @@ module Gitlab def external_groups options[:external_groups] end + + def admin_groups + options[:admin_groups] + end end end end From 37cd2b9b4dcf4c0145fd1bc2e19c8e903b4fd4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 18 May 2018 13:19:20 +0200 Subject: [PATCH 114/145] Minimize CE/EE difference in Gitlab::Auth::Saml::User MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/auth/saml/user.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/auth/saml/user.rb b/lib/gitlab/auth/saml/user.rb index cb01cd8004c..b8c84c37cd5 100644 --- a/lib/gitlab/auth/saml/user.rb +++ b/lib/gitlab/auth/saml/user.rb @@ -20,10 +20,8 @@ module Gitlab user ||= find_or_build_ldap_user if auto_link_ldap_user? user ||= build_new_user if signup_enabled? - if external_users_enabled? && user - # Check if there is overlap between the user's groups and the external groups - # setting then set user as external or internal. - user.external = !(auth_hash.groups & saml_config.external_groups).empty? + if user + user.external = !(auth_hash.groups & saml_config.external_groups).empty? if external_users_enabled? end user From dfdbf198b34075d0d7c88130ba3a082083e905c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 18 May 2018 14:00:44 +0200 Subject: [PATCH 115/145] Minimize CE/EE difference in Gitlab::Auth::UserAuthFinders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/auth/user_auth_finders.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/auth/user_auth_finders.rb b/lib/gitlab/auth/user_auth_finders.rb index cf02030c577..4dc23f977da 100644 --- a/lib/gitlab/auth/user_auth_finders.rb +++ b/lib/gitlab/auth/user_auth_finders.rb @@ -1,9 +1,5 @@ module Gitlab module Auth - # - # Exceptions - # - AuthenticationError = Class.new(StandardError) MissingTokenError = Class.new(AuthenticationError) TokenNotFoundError = Class.new(AuthenticationError) @@ -61,6 +57,12 @@ module Gitlab private + def route_authentication_setting + return {} unless respond_to?(:route_setting) + + route_setting(:authentication) || {} + end + def access_token strong_memoize(:access_token) do find_oauth_access_token || find_personal_access_token From 8b287679a1900185ecb6354ecdc8ac6d5a1e9ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 18 May 2018 16:26:44 +0200 Subject: [PATCH 116/145] Minimize CE/EE difference in Gitlab::Auth::LDAP::Access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/auth/ldap/access.rb | 41 ++++++++++++++++++------ spec/lib/gitlab/auth/ldap/access_spec.rb | 13 +++++++- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/lib/gitlab/auth/ldap/access.rb b/lib/gitlab/auth/ldap/access.rb index 34286900e72..865185eb5db 100644 --- a/lib/gitlab/auth/ldap/access.rb +++ b/lib/gitlab/auth/ldap/access.rb @@ -6,7 +6,7 @@ module Gitlab module Auth module LDAP class Access - attr_reader :provider, :user + attr_reader :provider, :user, :ldap_identity def self.open(user, &block) Gitlab::Auth::LDAP::Adapter.open(user.ldap_identity.provider) do |adapter| @@ -14,9 +14,12 @@ module Gitlab end end - def self.allowed?(user) + def self.allowed?(user, options = {}) self.open(user) do |access| + # Whether user is allowed, or not, we should update + # permissions to keep things clean if access.allowed? + access.update_user Users::UpdateService.new(user, user: user, last_credential_check_at: Time.now).execute true @@ -29,7 +32,8 @@ module Gitlab def initialize(user, adapter = nil) @adapter = adapter @user = user - @provider = user.ldap_identity.provider + @ldap_identity = user.ldap_identity + @provider = adapter&.provider || ldap_identity&.provider end def allowed? @@ -40,7 +44,7 @@ module Gitlab end # Block user in GitLab if he/she was blocked in AD - if Gitlab::Auth::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter) + if Gitlab::Auth::LDAP::Person.disabled_via_active_directory?(ldap_identity.extern_uid, adapter) block_user(user, 'is disabled in Active Directory') false else @@ -64,27 +68,44 @@ module Gitlab Gitlab::Auth::LDAP::Config.new(provider) end + def find_ldap_user + Gitlab::Auth::LDAP::Person.find_by_dn(ldap_identity.extern_uid, adapter) + end + def ldap_user - @ldap_user ||= Gitlab::Auth::LDAP::Person.find_by_dn(user.ldap_identity.extern_uid, adapter) + return unless provider + + @ldap_user ||= find_ldap_user end def block_user(user, reason) user.ldap_block - Gitlab::AppLogger.info( - "LDAP account \"#{user.ldap_identity.extern_uid}\" #{reason}, " \ - "blocking Gitlab user \"#{user.name}\" (#{user.email})" - ) + if provider + Gitlab::AppLogger.info( + "LDAP account \"#{ldap_identity.extern_uid}\" #{reason}, " \ + "blocking Gitlab user \"#{user.name}\" (#{user.email})" + ) + else + Gitlab::AppLogger.info( + "Account is not provided by LDAP, " \ + "blocking Gitlab user \"#{user.name}\" (#{user.email})" + ) + end end def unblock_user(user, reason) user.activate Gitlab::AppLogger.info( - "LDAP account \"#{user.ldap_identity.extern_uid}\" #{reason}, " \ + "LDAP account \"#{ldap_identity.extern_uid}\" #{reason}, " \ "unblocking Gitlab user \"#{user.name}\" (#{user.email})" ) end + + def update_user + # no-op in CE + end end end end diff --git a/spec/lib/gitlab/auth/ldap/access_spec.rb b/spec/lib/gitlab/auth/ldap/access_spec.rb index 6b251d824f7..eff21985108 100644 --- a/spec/lib/gitlab/auth/ldap/access_spec.rb +++ b/spec/lib/gitlab/auth/ldap/access_spec.rb @@ -8,6 +8,7 @@ describe Gitlab::Auth::LDAP::Access do describe '.allowed?' do it 'updates the users `last_credential_check_at' do + allow(access).to receive(:update_user) expect(access).to receive(:allowed?) { true } expect(described_class).to receive(:open).and_yield(access) @@ -16,12 +17,21 @@ describe Gitlab::Auth::LDAP::Access do end end + describe '#find_ldap_user' do + it 'finds a user by dn first' do + expect(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(:ldap_user) + + access.find_ldap_user + end + end + describe '#allowed?' do subject { access.allowed? } context 'when the user cannot be found' do before do allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(nil) + allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_email).and_return(nil) end it { is_expected.to be_falsey } @@ -54,7 +64,7 @@ describe Gitlab::Auth::LDAP::Access do end end - context 'and has no disabled flag in active diretory' do + context 'and has no disabled flag in active directory' do before do allow(Gitlab::Auth::LDAP::Person).to receive(:disabled_via_active_directory?).and_return(false) end @@ -100,6 +110,7 @@ describe Gitlab::Auth::LDAP::Access do context 'when user cannot be found' do before do allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_dn).and_return(nil) + allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_email).and_return(nil) end it { is_expected.to be_falsey } From 6226d19c711e34ed9fa6f8a61468cac7f10ba7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 18 May 2018 16:27:12 +0200 Subject: [PATCH 117/145] Minimize CE/EE difference in Gitlab::Auth::LDAP::Config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/auth/ldap/config.rb | 18 ++++++++++-- spec/lib/gitlab/auth/ldap/config_spec.rb | 36 +++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb index 77185f52ced..d4415eaa6dc 100644 --- a/lib/gitlab/auth/ldap/config.rb +++ b/lib/gitlab/auth/ldap/config.rb @@ -11,6 +11,8 @@ module Gitlab attr_accessor :provider, :options + InvalidProvider = Class.new(StandardError) + def self.enabled? Gitlab.config.ldap.enabled end @@ -22,6 +24,10 @@ module Gitlab def self.available_servers return [] unless enabled? + _available_servers + end + + def self._available_servers Array.wrap(servers.first) end @@ -34,7 +40,7 @@ module Gitlab end def self.invalid_provider(provider) - raise "Unknown provider (#{provider}). Available providers: #{providers}" + raise InvalidProvider.new("Unknown provider (#{provider}). Available providers: #{providers}") end def initialize(provider) @@ -84,13 +90,17 @@ module Gitlab end def base - options['base'] + @base ||= Person.normalize_dn(options['base']) end def uid options['uid'] end + def label + options['label'] + end + def sync_ssh_keys? sync_ssh_keys.present? end @@ -132,6 +142,10 @@ module Gitlab options['timeout'].to_i end + def external_groups + options['external_groups'] || [] + end + def has_auth? options['password'] || options['bind_dn'] end diff --git a/spec/lib/gitlab/auth/ldap/config_spec.rb b/spec/lib/gitlab/auth/ldap/config_spec.rb index 82587e2ba55..d3ab599d5a0 100644 --- a/spec/lib/gitlab/auth/ldap/config_spec.rb +++ b/spec/lib/gitlab/auth/ldap/config_spec.rb @@ -23,7 +23,7 @@ describe Gitlab::Auth::LDAP::Config do end it 'raises an error if a unknown provider is used' do - expect { described_class.new 'unknown' }.to raise_error(RuntimeError) + expect { described_class.new 'unknown' }.to raise_error(described_class::InvalidProvider) end end @@ -370,4 +370,38 @@ describe Gitlab::Auth::LDAP::Config do }) end end + + describe '#base' do + context 'when the configured base is not normalized' do + it 'returns the normalized base' do + stub_ldap_config(options: { 'base' => 'DC=example, DC= com' }) + + expect(config.base).to eq('dc=example,dc=com') + end + end + + context 'when the configured base is normalized' do + it 'returns the base unaltered' do + stub_ldap_config(options: { 'base' => 'dc=example,dc=com' }) + + expect(config.base).to eq('dc=example,dc=com') + end + end + + context 'when the configured base is malformed' do + it 'returns the base unaltered' do + stub_ldap_config(options: { 'base' => 'invalid,dc=example,dc=com' }) + + expect(config.base).to eq('invalid,dc=example,dc=com') + end + end + + context 'when the configured base is blank' do + it 'returns the base unaltered' do + stub_ldap_config(options: { 'base' => '' }) + + expect(config.base).to eq('') + end + end + end end From d34d6a58fd89f72b63842832f8a6660a01681ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 18 May 2018 16:27:52 +0200 Subject: [PATCH 118/145] Minimize CE/EE difference in Gitlab::Auth::LDAP::User MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/lib/gitlab/auth/ldap/user_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/lib/gitlab/auth/ldap/user_spec.rb b/spec/lib/gitlab/auth/ldap/user_spec.rb index 653c19942ea..44bb9d20e47 100644 --- a/spec/lib/gitlab/auth/ldap/user_spec.rb +++ b/spec/lib/gitlab/auth/ldap/user_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Gitlab::Auth::LDAP::User do + include LdapHelpers + let(:ldap_user) { described_class.new(auth_hash) } let(:gl_user) { ldap_user.gl_user } let(:info) do @@ -177,8 +179,7 @@ describe Gitlab::Auth::LDAP::User do describe 'blocking' do def configure_block(value) - allow_any_instance_of(Gitlab::Auth::LDAP::Config) - .to receive(:block_auto_created_users).and_return(value) + stub_ldap_config(block_auto_created_users: value) end context 'signup' do From 55e2ce762d52e680b45c9b87a238f993485f2866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20=C4=8Cupi=C4=87?= Date: Fri, 18 May 2018 14:34:08 +0000 Subject: [PATCH 119/145] Revert "Improve cacheable module" This reverts commit 2ebafdfb2f026580153fd2cf50f4b6b7ab3a0344 --- app/models/concerns/redis_cacheable.rb | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/models/concerns/redis_cacheable.rb b/app/models/concerns/redis_cacheable.rb index 53f022e2b35..b5425295130 100644 --- a/app/models/concerns/redis_cacheable.rb +++ b/app/models/concerns/redis_cacheable.rb @@ -7,11 +7,11 @@ module RedisCacheable class_methods do def cached_attr_reader(*attributes) attributes.each do |attribute| - unless self.column_names.include?(attribute.to_s) - raise ArgumentError, "`cached_attr_reader` requires the #{self.name}##{attribute} to be a database attribute" - end - define_method(attribute) do + unless self.has_attribute?(attribute) + raise ArgumentError, "`cached_attr_reader` requires the #{self.class.name}\##{attribute} attribute to have a database column" + end + cached_attribute(attribute) || read_attribute(attribute) end end @@ -50,9 +50,7 @@ module RedisCacheable if Gitlab.rails5? self.class.type_for_attribute(attribute).cast(value) else - ActiveSupport::Deprecation.silence do - self.class.column_for_attribute(attribute).type_cast_from_database(value) - end + self.class.column_for_attribute(attribute).type_cast_from_database(value) end end end From f2479b608c042b2c4a7b85e38da92834c5c5ceb2 Mon Sep 17 00:00:00 2001 From: James Ramsay Date: Fri, 18 May 2018 14:27:30 +0000 Subject: [PATCH 120/145] Add web shortcut to docs and shortcut modal --- app/views/help/_shortcuts.html.haml | 11 +++++++++++ changelogs/unreleased/jr-web-ide-shortcuts.yml | 5 +++++ doc/workflow/shortcuts.md | 6 ++++++ 3 files changed, 22 insertions(+) create mode 100644 changelogs/unreleased/jr-web-ide-shortcuts.yml diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 2244d16f0a6..9a3a03a7671 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -231,6 +231,17 @@ %td.shortcut .key y %td Go to file permalink + %tbody + %tr + %th + %th Web IDE + %tr + %td.shortcut + - if browser.platform.mac? + .key ⌘ p + - else + .key ctrl p + %td Go to file .col-lg-4 %table.shortcut-mappings %tbody.hidden-shortcut.network{ style: 'display:none' } diff --git a/changelogs/unreleased/jr-web-ide-shortcuts.yml b/changelogs/unreleased/jr-web-ide-shortcuts.yml new file mode 100644 index 00000000000..a895eab432a --- /dev/null +++ b/changelogs/unreleased/jr-web-ide-shortcuts.yml @@ -0,0 +1,5 @@ +--- +title: Add shortcuts to Web IDE docs and modal +merge_request: 19044 +author: +type: changed diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md index 930f2e73683..c99505e6bdf 100644 --- a/doc/workflow/shortcuts.md +++ b/doc/workflow/shortcuts.md @@ -88,3 +88,9 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?' | Keyboard Shortcut | Description | | ----------------- | ----------- | | e | Edit wiki page| + +## Web IDE + +| Keyboard Shortcut | Description | +| ----------------- | ----------- | +| ⌘ + p | Go to file | From dbe0839396f56e30780350e840a1ded303dfbb81 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Fri, 18 May 2018 10:29:20 -0500 Subject: [PATCH 121/145] Fixes deploy tokens build variables It was using name, instead of username. Fixes documentation as well Closes #46454 --- app/models/ci/build.rb | 2 +- .../unreleased/46454-wrong-value-in-ci-deploy-user.yml | 5 +++++ doc/user/project/deploy_tokens/index.md | 2 +- spec/models/ci/build_spec.rb | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index d9649e30edc..1c42ed4d3e5 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -661,7 +661,7 @@ module Ci Gitlab::Ci::Variables::Collection.new.tap do |variables| break variables unless gitlab_deploy_token - variables.append(key: 'CI_DEPLOY_USER', value: gitlab_deploy_token.name) + variables.append(key: 'CI_DEPLOY_USER', value: gitlab_deploy_token.username) variables.append(key: 'CI_DEPLOY_PASSWORD', value: gitlab_deploy_token.token, public: false) end end diff --git a/changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml b/changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml new file mode 100644 index 00000000000..e610e53f71c --- /dev/null +++ b/changelogs/unreleased/46454-wrong-value-in-ci-deploy-user.yml @@ -0,0 +1,5 @@ +--- +title: Fixes deploy token variables on Ci::Build +merge_request: 19047 +author: +type: fixed diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md index 7a8b3c75690..c09d5aeba8e 100644 --- a/doc/user/project/deploy_tokens/index.md +++ b/doc/user/project/deploy_tokens/index.md @@ -76,7 +76,7 @@ pull images from your Container Registry. > [Introduced][ce-18414] in GitLab 10.8. There's a special case when it comes to Deploy Tokens, if a user creates one -named `gitlab-deploy-token`, the name and token of the Deploy Token will be +named `gitlab-deploy-token`, the username and token of the Deploy Token will be automatically exposed to the CI/CD jobs as environment variables: `CI_DEPLOY_USER` and `CI_DEPLOY_PASSWORD`, respectively. diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 7d8bddbcedb..af5f5047803 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2084,7 +2084,7 @@ describe Ci::Build do let(:deploy_token_variables) do [ - { key: 'CI_DEPLOY_USER', value: deploy_token.name, public: true }, + { key: 'CI_DEPLOY_USER', value: deploy_token.username, public: true }, { key: 'CI_DEPLOY_PASSWORD', value: deploy_token.token, public: false } ] end From 1fb431c6acfa0bdc578a6488147ed5f28aaa96c3 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Fri, 18 May 2018 09:41:25 -0700 Subject: [PATCH 122/145] Use defaults for retry And use a supported syntax for ignoring docs during gem install --- .gitlab-ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc6fd5e2bfe..c0c1f3c8ec3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -307,9 +307,8 @@ cloud-native-image: stage: build allow_failure: true cache: {} - retry: 0 before_script: - - gem install gitlab --no-doc + - gem install gitlab --no-rdoc --no-ri - chmod 755 ./scripts/trigger-build-cloud-native script: - ./scripts/trigger-build-cloud-native From 12292502317ea1ef106c2236d6ab7012bf9d7825 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 18 May 2018 19:29:17 +0200 Subject: [PATCH 123/145] Chmod 644 on offending files --- app/views/projects/forks/new.html.haml | 0 doc/user/admin_area/settings/img/enforce_terms.png | Bin .../admin_area/settings/img/respond_to_terms.png | Bin 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app/views/projects/forks/new.html.haml mode change 100755 => 100644 doc/user/admin_area/settings/img/enforce_terms.png mode change 100755 => 100644 doc/user/admin_area/settings/img/respond_to_terms.png diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml old mode 100755 new mode 100644 diff --git a/doc/user/admin_area/settings/img/enforce_terms.png b/doc/user/admin_area/settings/img/enforce_terms.png old mode 100755 new mode 100644 diff --git a/doc/user/admin_area/settings/img/respond_to_terms.png b/doc/user/admin_area/settings/img/respond_to_terms.png old mode 100755 new mode 100644 From 019f5e2469f21c4127a2c972042839185b26bb3f Mon Sep 17 00:00:00 2001 From: Ahmad Sherif Date: Tue, 20 Mar 2018 20:20:12 +0100 Subject: [PATCH 124/145] Add handling for commit/tags with big messages --- .flayignore | 4 + lib/gitlab/git/commit.rb | 64 ++++++++++++-- lib/gitlab/git/repository.rb | 7 +- lib/gitlab/git/repository_mirroring.rb | 6 +- lib/gitlab/git/tag.rb | 88 ++++++++++++++++++- lib/gitlab/gitaly_client/commit_service.rb | 16 ++++ lib/gitlab/gitaly_client/operation_service.rb | 2 +- lib/gitlab/gitaly_client/ref_service.rb | 18 +++- lib/gitlab/gitaly_client/util.rb | 14 --- spec/factories/gitaly/tag.rb | 9 ++ spec/lib/gitlab/git/commit_spec.rb | 56 +++++++++++- spec/lib/gitlab/git/tag_spec.rb | 52 +++++++++++ .../update_remote_mirror_service_spec.rb | 6 +- 13 files changed, 310 insertions(+), 32 deletions(-) create mode 100644 spec/factories/gitaly/tag.rb diff --git a/.flayignore b/.flayignore index 0c4eee10ffa..7faa6c7bb90 100644 --- a/.flayignore +++ b/.flayignore @@ -10,3 +10,7 @@ lib/gitlab/background_migration/* app/models/project_services/kubernetes_service.rb lib/gitlab/workhorse.rb lib/gitlab/ci/trace/chunked_io.rb +lib/gitlab/gitaly_client/ref_service.rb +lib/gitlab/gitaly_client/commit_service.rb +lib/gitlab/git/commit.rb +lib/gitlab/git/tag.rb diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index d79a4dbeee4..89f761dd515 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -6,6 +6,7 @@ module Gitlab attr_accessor :raw_commit, :head + MAX_COMMIT_MESSAGE_DISPLAY_SIZE = 10.megabytes MIN_SHA_LENGTH = 7 SERIALIZE_KEYS = [ :id, :message, :parent_ids, @@ -63,9 +64,7 @@ module Gitlab if is_enabled repo.gitaly_commit_client.find_commit(commit_id) else - obj = repo.rev_parse_target(commit_id) - - obj.is_a?(Rugged::Commit) ? obj : nil + rugged_find(repo, commit_id) end end @@ -76,6 +75,12 @@ module Gitlab nil end + def rugged_find(repo, commit_id) + obj = repo.rev_parse_target(commit_id) + + obj.is_a?(Rugged::Commit) ? obj : nil + end + # Get last commit for HEAD # # Ex. @@ -297,11 +302,40 @@ module Gitlab nil end end + + def get_message(repository, commit_id) + BatchLoader.for({ repository: repository, commit_id: commit_id }).batch do |items, loader| + items_by_repo = items.group_by { |i| i[:repository] } + + items_by_repo.each do |repo, items| + commit_ids = items.map { |i| i[:commit_id] } + + messages = get_messages(repository, commit_ids) + + messages.each do |commit_sha, message| + loader.call({ repository: repository, commit_id: commit_sha }, message) + end + end + end + end + + def get_messages(repository, commit_ids) + repository.gitaly_migrate(:commit_messages) do |is_enabled| + if is_enabled + repository.gitaly_commit_client.get_commit_messages(commit_ids) + else + commit_ids.map { |id| [id, rugged_find(repository, id).message] }.to_h + end + end + end end def initialize(repository, raw_commit, head = nil) raise "Nil as raw commit passed" unless raw_commit + @repository = repository + @head = head + case raw_commit when Hash init_from_hash(raw_commit) @@ -312,9 +346,6 @@ module Gitlab else raise "Invalid raw commit type: #{raw_commit.class}" end - - @repository = repository - @head = head end def sha @@ -518,7 +549,7 @@ module Gitlab # TODO: Once gitaly "takes over" Rugged consider separating the # subject from the message to make it clearer when there's one # available but not the other. - @message = (commit.body.presence || commit.subject).dup + @message = message_from_gitaly_body @authored_date = Time.at(commit.author.date.seconds).utc @author_name = commit.author.name.dup @author_email = commit.author.email.dup @@ -570,6 +601,25 @@ module Gitlab def refs(repo) repo.refs_hash[id] end + + def message_from_gitaly_body + return @raw_commit.subject.dup if @raw_commit.body_size.zero? + return @raw_commit.body.dup if full_body_fetched_from_gitaly? + + if @raw_commit.body_size > MAX_COMMIT_MESSAGE_DISPLAY_SIZE + "#{@raw_commit.subject}\n\n--commit message is too big".strip + else + fetch_body_from_gitaly + end + end + + def full_body_fetched_from_gitaly? + @raw_commit.body.bytesize == @raw_commit.body_size + end + + def fetch_body_from_gitaly + self.class.get_message(@repository, id) + end end end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 061865a7acf..b9aa4d03d9f 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1948,7 +1948,12 @@ module Gitlab end target_commit = Gitlab::Git::Commit.find(self, ref.target) - Gitlab::Git::Tag.new(self, ref.name, ref.target, target_commit, message) + Gitlab::Git::Tag.new(self, { + name: ref.name, + target: ref.target, + target_commit: target_commit, + message: message + }) end.sort_by(&:name) end diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb index 8a01f92e2af..e35ea5762eb 100644 --- a/lib/gitlab/git/repository_mirroring.rb +++ b/lib/gitlab/git/repository_mirroring.rb @@ -35,7 +35,11 @@ module Gitlab next if name =~ /\^\{\}\Z/ target_commit = Gitlab::Git::Commit.find(self, target) - Gitlab::Git::Tag.new(self, name, target, target_commit) + Gitlab::Git::Tag.new(self, { + name: name, + target: target, + target_commit: target_commit + }) end.compact end diff --git a/lib/gitlab/git/tag.rb b/lib/gitlab/git/tag.rb index 8a8f7b051ed..e44284572fd 100644 --- a/lib/gitlab/git/tag.rb +++ b/lib/gitlab/git/tag.rb @@ -1,17 +1,99 @@ module Gitlab module Git class Tag < Ref - attr_reader :object_sha + extend Gitlab::EncodingHelper + + attr_reader :object_sha, :repository + + MAX_TAG_MESSAGE_DISPLAY_SIZE = 10.megabytes + SERIALIZE_KEYS = %i[name target target_commit message].freeze + + attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator + + class << self + def get_message(repository, tag_id) + BatchLoader.for({ repository: repository, tag_id: tag_id }).batch do |items, loader| + items_by_repo = items.group_by { |i| i[:repository] } + + items_by_repo.each do |repo, items| + tag_ids = items.map { |i| i[:tag_id] } + + messages = get_messages(repository, tag_ids) + + messages.each do |id, message| + loader.call({ repository: repository, tag_id: id }, message) + end + end + end + end + + def get_messages(repository, tag_ids) + repository.gitaly_migrate(:tag_messages) do |is_enabled| + if is_enabled + repository.gitaly_ref_client.get_tag_messages(tag_ids) + else + tag_ids.map do |id| + tag = repository.rugged.lookup(id) + message = tag.is_a?(Rugged::Commit) ? "" : tag.message + + [id, message] + end.to_h + end + end + end + end + + def initialize(repository, raw_tag) + @repository = repository + @raw_tag = raw_tag + + case raw_tag + when Hash + init_from_hash + when Gitaly::Tag + init_from_gitaly + end - def initialize(repository, name, target, target_commit, message = nil) super(repository, name, target, target_commit) + end - @message = message + def init_from_hash + raw_tag = @raw_tag.symbolize_keys + + SERIALIZE_KEYS.each do |key| + send("#{key}=", raw_tag[key]) # rubocop:disable GitlabSecurity/PublicSend + end + end + + def init_from_gitaly + @name = encode!(@raw_tag.name.dup) + @target = @raw_tag.id + @message = message_from_gitaly_tag + + if @raw_tag.target_commit.present? + @target_commit = Gitlab::Git::Commit.decorate(repository, @raw_tag.target_commit) + end end def message encode! @message end + + private + + def message_from_gitaly_tag + return @raw_tag.message.dup if full_message_fetched_from_gitaly? + + if @raw_tag.message_size > MAX_TAG_MESSAGE_DISPLAY_SIZE + '--tag message is too big' + else + self.class.get_message(@repository, target) + end + end + + def full_message_fetched_from_gitaly? + @raw_tag.message.bytesize == @raw_tag.message_size + end end end end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index a36e6c822f9..1f5f88bf792 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -334,6 +334,22 @@ module Gitlab signatures end + def get_commit_messages(commit_ids) + request = Gitaly::GetCommitMessagesRequest.new(repository: @gitaly_repo, commit_ids: commit_ids) + response = GitalyClient.call(@repository.storage, :commit_service, :get_commit_messages, request) + + messages = Hash.new { |h, k| h[k] = ''.b } + current_commit_id = nil + + response.each do |rpc_message| + current_commit_id = rpc_message.commit_id if rpc_message.commit_id.present? + + messages[current_commit_id] << rpc_message.message + end + + messages + end + private def call_commit_diff(request_params, options = {}) diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb index 831cfd1e014..44b0e517bf0 100644 --- a/lib/gitlab/gitaly_client/operation_service.rb +++ b/lib/gitlab/gitaly_client/operation_service.rb @@ -40,7 +40,7 @@ module Gitlab raise Gitlab::Git::Repository::TagExistsError end - Util.gitlab_tag_from_gitaly_tag(@repository, response.tag) + Gitlab::Git::Tag.new(@repository, response.tag) rescue GRPC::FailedPrecondition => e raise Gitlab::Git::Repository::InvalidRef, e end diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb index ba6b577fd17..3ac46be6208 100644 --- a/lib/gitlab/gitaly_client/ref_service.rb +++ b/lib/gitlab/gitaly_client/ref_service.rb @@ -171,6 +171,22 @@ module Gitlab consume_ref_contains_sha_response(stream, :branch_names) end + def get_tag_messages(tag_ids) + request = Gitaly::GetTagMessagesRequest.new(repository: @gitaly_repo, tag_ids: tag_ids) + response = GitalyClient.call(@repository.storage, :ref_service, :get_tag_messages, request) + + messages = Hash.new { |h, k| h[k] = ''.b } + current_tag_id = nil + + response.each do |rpc_message| + current_tag_id = rpc_message.tag_id if rpc_message.tag_id.present? + + messages[current_tag_id] << rpc_message.message + end + + messages + end + private def consume_refs_response(response) @@ -210,7 +226,7 @@ module Gitlab def consume_tags_response(response) response.flat_map do |message| - message.tags.map { |gitaly_tag| Util.gitlab_tag_from_gitaly_tag(@repository, gitaly_tag) } + message.tags.map { |gitaly_tag| Gitlab::Git::Tag.new(@repository, gitaly_tag) } end end diff --git a/lib/gitlab/gitaly_client/util.rb b/lib/gitlab/gitaly_client/util.rb index 405567db94a..9c19c51d412 100644 --- a/lib/gitlab/gitaly_client/util.rb +++ b/lib/gitlab/gitaly_client/util.rb @@ -21,20 +21,6 @@ module Gitlab gitaly_repository.relative_path, gitaly_repository.gl_repository) end - - def gitlab_tag_from_gitaly_tag(repository, gitaly_tag) - if gitaly_tag.target_commit.present? - commit = Gitlab::Git::Commit.decorate(repository, gitaly_tag.target_commit) - end - - Gitlab::Git::Tag.new( - repository, - Gitlab::EncodingHelper.encode!(gitaly_tag.name.dup), - gitaly_tag.id, - commit, - Gitlab::EncodingHelper.encode!(gitaly_tag.message.chomp) - ) - end end end end diff --git a/spec/factories/gitaly/tag.rb b/spec/factories/gitaly/tag.rb new file mode 100644 index 00000000000..e99776d524a --- /dev/null +++ b/spec/factories/gitaly/tag.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :gitaly_tag, class: Gitaly::Tag do + skip_create + + name { 'v3.1.4' } + message { 'Pie release' } + target_commit factory: :gitaly_commit + end +end diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 2e068584c2e..08c6d1e55e9 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -66,7 +66,8 @@ describe Gitlab::Git::Commit, seed_helper: true do describe "Commit info from gitaly commit" do let(:subject) { "My commit".force_encoding('ASCII-8BIT') } let(:body) { subject + "My body".force_encoding('ASCII-8BIT') } - let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body) } + let(:body_size) { body.length } + let(:gitaly_commit) { build(:gitaly_commit, subject: subject, body: body, body_size: body_size) } let(:id) { gitaly_commit.id } let(:committer) { gitaly_commit.committer } let(:author) { gitaly_commit.author } @@ -83,10 +84,30 @@ describe Gitlab::Git::Commit, seed_helper: true do it { expect(commit.committer_email).to eq(committer.email) } it { expect(commit.parent_ids).to eq(gitaly_commit.parent_ids) } - context 'no body' do + context 'body_size != body.size' do let(:body) { "".force_encoding('ASCII-8BIT') } - it { expect(commit.safe_message).to eq(subject) } + context 'zero body_size' do + it { expect(commit.safe_message).to eq(subject) } + end + + context 'body_size less than threshold' do + let(:body_size) { 123 } + + it 'fetches commit message seperately' do + expect(described_class).to receive(:get_message).with(repository, id) + + commit.safe_message + end + end + + context 'body_size greater than threshold' do + let(:body_size) { described_class::MAX_COMMIT_MESSAGE_DISPLAY_SIZE + 1 } + + it 'returns the suject plus a notice about message size' do + expect(commit.safe_message).to eq("My commit\n\n--commit message is too big") + end + end end end @@ -589,6 +610,35 @@ describe Gitlab::Git::Commit, seed_helper: true do it { is_expected.not_to include("feature") } end + describe '.get_message' do + let(:commit_ids) { %w[6d394385cf567f80a8fd85055db1ab4c5295806f cfe32cf61b73a0d5e9f13e774abde7ff789b1660] } + + subject do + commit_ids.map { |id| described_class.get_message(repository, id) } + end + + shared_examples 'getting commit messages' do + it 'gets commit messages' do + expect(subject).to contain_exactly( + "Added contributing guide\n\nSigned-off-by: Dmitriy Zaporozhets \n", + "Add submodule\n\nSigned-off-by: Dmitriy Zaporozhets \n" + ) + end + end + + context 'when Gitaly commit_messages feature is enabled' do + it_behaves_like 'getting commit messages' + + it 'gets messages in one batch', :request_store do + expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1) + end + end + + context 'when Gitaly commit_messages feature is disabled', :disable_gitaly do + it_behaves_like 'getting commit messages' + end + end + def sample_commit_hash { author_email: "dmitriy.zaporozhets@gmail.com", diff --git a/spec/lib/gitlab/git/tag_spec.rb b/spec/lib/gitlab/git/tag_spec.rb index 6c4f538bf01..be2f5bfb819 100644 --- a/spec/lib/gitlab/git/tag_spec.rb +++ b/spec/lib/gitlab/git/tag_spec.rb @@ -32,4 +32,56 @@ describe Gitlab::Git::Tag, seed_helper: true do context 'when Gitaly tags feature is disabled', :skip_gitaly_mock do it_behaves_like 'Gitlab::Git::Repository#tags' end + + describe '.get_message' do + let(:tag_ids) { %w[f4e6814c3e4e7a0de82a9e7cd20c626cc963a2f8 8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b] } + + subject do + tag_ids.map { |id| described_class.get_message(repository, id) } + end + + shared_examples 'getting tag messages' do + it 'gets tag messages' do + expect(subject[0]).to eq("Release\n") + expect(subject[1]).to eq("Version 1.1.0\n") + end + end + + context 'when Gitaly tag_messages feature is enabled' do + it_behaves_like 'getting tag messages' + + it 'gets messages in one batch', :request_store do + expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1) + end + end + + context 'when Gitaly tag_messages feature is disabled', :disable_gitaly do + it_behaves_like 'getting tag messages' + end + end + + describe 'tag into from Gitaly tag' do + context 'message_size != message.size' do + let(:gitaly_tag) { build(:gitaly_tag, message: ''.b, message_size: message_size) } + let(:tag) { described_class.new(repository, gitaly_tag) } + + context 'message_size less than threshold' do + let(:message_size) { 123 } + + it 'fetches tag message seperately' do + expect(described_class).to receive(:get_message).with(repository, gitaly_tag.id) + + tag.message + end + end + + context 'message_size greater than threshold' do + let(:message_size) { described_class::MAX_TAG_MESSAGE_DISPLAY_SIZE + 1 } + + it 'returns a notice about message size' do + expect(tag.message).to eq("--tag message is too big") + end + end + end + end end diff --git a/spec/services/projects/update_remote_mirror_service_spec.rb b/spec/services/projects/update_remote_mirror_service_spec.rb index be09afd9f36..723cb374c37 100644 --- a/spec/services/projects/update_remote_mirror_service_spec.rb +++ b/spec/services/projects/update_remote_mirror_service_spec.rb @@ -343,7 +343,11 @@ describe Projects::UpdateRemoteMirrorService do tag = repository.find_tag(name) target = tag.try(:target) target_commit = tag.try(:dereferenced_target) - tags << Gitlab::Git::Tag.new(repository.raw_repository, name, target, target_commit) + tags << Gitlab::Git::Tag.new(repository.raw_repository, { + name: name, + target: target, + target_commit: target_commit + }) end end From 1b530f96645b738c8e723f6ff8841569ac06aef4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 May 2018 20:14:31 +0000 Subject: [PATCH 125/145] Fix double brackets being linkified in wiki markdown --- ...4-fix-double-brackets-in-wiki-markdown.yml | 5 ++++ lib/banzai/filter/gollum_tags_filter.rb | 3 +++ .../projects/wiki/markdown_preview_spec.rb | 23 +++++++++++++++++++ .../banzai/filter/gollum_tags_filter_spec.rb | 6 +++++ 4 files changed, 37 insertions(+) create mode 100644 changelogs/unreleased/18524-fix-double-brackets-in-wiki-markdown.yml diff --git a/changelogs/unreleased/18524-fix-double-brackets-in-wiki-markdown.yml b/changelogs/unreleased/18524-fix-double-brackets-in-wiki-markdown.yml new file mode 100644 index 00000000000..9287243a7e3 --- /dev/null +++ b/changelogs/unreleased/18524-fix-double-brackets-in-wiki-markdown.yml @@ -0,0 +1,5 @@ +--- +title: Fix double-brackets being linkified in wiki markdown +merge_request: 18524 +author: brewingcode +type: fixed diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb index f2e9a5a1116..4bc82ecb4d6 100644 --- a/lib/banzai/filter/gollum_tags_filter.rb +++ b/lib/banzai/filter/gollum_tags_filter.rb @@ -58,6 +58,9 @@ module Banzai def call doc.search(".//text()").each do |node| + # Do not perform linking inside blocks + next unless node.ancestors('code').empty? + # A Gollum ToC tag is `[[_TOC_]]`, but due to MarkdownFilter running # before this one, it will be converted into `[[TOC]]`, so it # needs special-case handling diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb index 6586ccaa400..e473739a6aa 100644 --- a/spec/features/projects/wiki/markdown_preview_spec.rb +++ b/spec/features/projects/wiki/markdown_preview_spec.rb @@ -155,4 +155,27 @@ feature 'Projects > Wiki > User previews markdown changes', :js do end end end + + it "does not linkify double brackets inside code blocks as expected" do + click_link 'New page' + page.within '#modal-new-wiki' do + fill_in :new_wiki_path, with: 'linkify_test' + click_button 'Create page' + end + + page.within '.wiki-form' do + fill_in :wiki_content, with: <<-HEREDOC + `[[do_not_linkify]]` + ``` + [[also_do_not_linkify]] + ``` + HEREDOC + click_on "Preview" + end + + expect(page).to have_content("do_not_linkify") + + expect(page.html).to include('[[do_not_linkify]]') + expect(page.html).to include('[[also_do_not_linkify]]') + end end diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb index ca76d6f0881..0e178b859c4 100644 --- a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb +++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb @@ -91,6 +91,12 @@ describe Banzai::Filter::GollumTagsFilter do expect(doc.at_css('a').text).to eq 'link-text' expect(doc.at_css('a')['href']).to eq expected_path end + + it "inside back ticks will be exempt from linkification" do + doc = filter('[[link-in-backticks]]', project_wiki: project_wiki) + + expect(doc.at_css('code').text).to eq '[[link-in-backticks]]' + end end context 'table of contents' do From d8a9bfb6b7e17812915fe6767158abaca2ea6a74 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Thu, 5 Apr 2018 23:56:32 +0900 Subject: [PATCH 126/145] Order UsersController#projects.json by updated_at --- app/finders/personal_projects_finder.rb | 2 +- .../unreleased/45065-users-projects-json-sort.yml | 5 +++++ .../users/user_browses_projects_on_user_page_spec.rb | 8 ++++---- spec/finders/personal_projects_finder_spec.rb | 12 +++++++----- 4 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/45065-users-projects-json-sort.yml diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 3ad4bd5f066..5aea0cb8192 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -13,7 +13,7 @@ class PersonalProjectsFinder < UnionFinder def execute(current_user = nil) segments = all_projects(current_user) - find_union(segments, Project).includes(:namespace).order_id_desc + find_union(segments, Project).includes(:namespace).order_updated_desc end private diff --git a/changelogs/unreleased/45065-users-projects-json-sort.yml b/changelogs/unreleased/45065-users-projects-json-sort.yml new file mode 100644 index 00000000000..89a1d7eb36f --- /dev/null +++ b/changelogs/unreleased/45065-users-projects-json-sort.yml @@ -0,0 +1,5 @@ +--- +title: Order UsersController#projects.json by updated_at +merge_request: 18227 +author: Takuya Noguchi +type: other diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb index a70637c8370..7bede0b0d48 100644 --- a/spec/features/users/user_browses_projects_on_user_page_spec.rb +++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb @@ -27,8 +27,8 @@ describe 'Users > User browses projects on user page', :js do end it 'paginates projects', :js do - project = create(:project, namespace: user.namespace) - project2 = create(:project, namespace: user.namespace) + project = create(:project, namespace: user.namespace, updated_at: 2.minutes.since) + project2 = create(:project, namespace: user.namespace, updated_at: 1.minute.since) allow(Project).to receive(:default_per_page).and_return(1) sign_in(user) @@ -41,11 +41,11 @@ describe 'Users > User browses projects on user page', :js do wait_for_requests - expect(page).to have_content(project2.name) + expect(page).to have_content(project.name) click_link('Next') - expect(page).to have_content(project.name) + expect(page).to have_content(project2.name) end context 'when not signed in' do diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb index 5e52898e9c0..00c551a1f65 100644 --- a/spec/finders/personal_projects_finder_spec.rb +++ b/spec/finders/personal_projects_finder_spec.rb @@ -4,14 +4,16 @@ describe PersonalProjectsFinder do let(:source_user) { create(:user) } let(:current_user) { create(:user) } let(:finder) { described_class.new(source_user) } - let!(:public_project) { create(:project, :public, namespace: source_user.namespace) } + let!(:public_project) do + create(:project, :public, namespace: source_user.namespace, updated_at: 1.hour.ago) + end let!(:private_project) do - create(:project, :private, namespace: source_user.namespace, path: 'mepmep') + create(:project, :private, namespace: source_user.namespace, updated_at: 3.hours.ago, path: 'mepmep') end let!(:internal_project) do - create(:project, :internal, namespace: source_user.namespace, path: 'C') + create(:project, :internal, namespace: source_user.namespace, updated_at: 2.hours.ago, path: 'C') end before do @@ -28,7 +30,7 @@ describe PersonalProjectsFinder do subject { finder.execute(current_user) } context 'normal user' do - it { is_expected.to eq([internal_project, private_project, public_project]) } + it { is_expected.to eq([public_project, internal_project, private_project]) } end context 'external' do @@ -36,7 +38,7 @@ describe PersonalProjectsFinder do current_user.update_attributes(external: true) end - it { is_expected.to eq([private_project, public_project]) } + it { is_expected.to eq([public_project, private_project]) } end end end From 100c687cbc49c592afc1c08bfda9e21d97a115b1 Mon Sep 17 00:00:00 2001 From: Sergey Sinev Date: Wed, 25 Apr 2018 20:37:52 +0300 Subject: [PATCH 127/145] Fix error when deleting an empty list of refs Closes #45743 --- ...don-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml | 5 +++++ lib/gitlab/git/repository.rb | 2 +- spec/lib/gitlab/git/repository_spec.rb | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml diff --git a/changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml b/changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml new file mode 100644 index 00000000000..94da4d74300 --- /dev/null +++ b/changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml @@ -0,0 +1,5 @@ +--- +title: Fix error when deleting an empty list of refs +merge_request: +author: +type: fixed diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index b521d69930a..5617d7c1c41 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -2346,7 +2346,7 @@ module Gitlab end def gitaly_delete_refs(*ref_names) - gitaly_ref_client.delete_refs(refs: ref_names) + gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any? end def rugged_remove_remote(remote_name) diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 2b5710ac401..01a51573922 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -600,6 +600,10 @@ describe Gitlab::Git::Repository, seed_helper: true do end end + it 'does not fail when deleting an empty list of refs' do + expect { repo.delete_refs(*[]) }.not_to raise_error + end + it 'raises an error if it failed' do expect { repo.delete_refs('refs\heads\fix') }.to raise_error(Gitlab::Git::Repository::GitError) end From d839b880a256f208ae5a0d877765bc5fcbfcd44d Mon Sep 17 00:00:00 2001 From: Mark Chao Date: Mon, 14 May 2018 10:07:53 +0800 Subject: [PATCH 128/145] Add created_by_me and assigned_to_me scopes Deprecate corresponding dash versions created-by-me and assigned-to-me --- app/finders/issuable_finder.rb | 9 +- app/finders/issues_finder.rb | 2 +- app/finders/merge_requests_finder.rb | 2 +- .../44799-api-naming-issue-scope.yml | 5 + doc/api/issues.md | 9 +- doc/api/merge_requests.md | 9 +- lib/api/issues.rb | 9 +- lib/api/merge_requests.rb | 9 +- spec/finders/issues_finder_spec.rb | 2 +- spec/requests/api/issues_spec.rb | 9 + spec/requests/api/merge_requests_spec.rb | 225 +++++++----------- 11 files changed, 129 insertions(+), 161 deletions(-) create mode 100644 changelogs/unreleased/44799-api-naming-issue-scope.yml diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 7ed9b1fc6d0..c6ef79ce15e 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -6,7 +6,7 @@ # klass - actual class like Issue or MergeRequest # current_user - which user use # params: -# scope: 'created-by-me' or 'assigned-to-me' or 'all' +# scope: 'created_by_me' or 'assigned_to_me' or 'all' # state: 'opened' or 'closed' or 'all' # group_id: integer # project_id: integer @@ -282,9 +282,9 @@ class IssuableFinder return items.none if current_user_related? && !current_user case params[:scope] - when 'created-by-me', 'authored' + when 'created_by_me', 'authored' items.where(author_id: current_user.id) - when 'assigned-to-me' + when 'assigned_to_me' items.assigned_to(current_user) else items @@ -426,6 +426,7 @@ class IssuableFinder end def current_user_related? - params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' + scope = params[:scope] + scope == 'created_by_me' || scope == 'authored' || scope == 'assigned_to_me' end end diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb index 2a27ff0e386..1787b4899cd 100644 --- a/app/finders/issues_finder.rb +++ b/app/finders/issues_finder.rb @@ -5,7 +5,7 @@ # Arguments: # current_user - which user use # params: -# scope: 'created-by-me' or 'assigned-to-me' or 'all' +# scope: 'created_by_me' or 'assigned_to_me' or 'all' # state: 'open' or 'closed' or 'all' # group_id: integer # project_id: integer diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 64dc1e6af0f..e2240e5e0d8 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -5,7 +5,7 @@ # Arguments: # current_user - which user use # params: -# scope: 'created-by-me' or 'assigned-to-me' or 'all' +# scope: 'created_by_me' or 'assigned_to_me' or 'all' # state: 'open', 'closed', 'merged', or 'all' # group_id: integer # project_id: integer diff --git a/changelogs/unreleased/44799-api-naming-issue-scope.yml b/changelogs/unreleased/44799-api-naming-issue-scope.yml new file mode 100644 index 00000000000..75c6ea4cd0d --- /dev/null +++ b/changelogs/unreleased/44799-api-naming-issue-scope.yml @@ -0,0 +1,5 @@ +--- +title: Rename issue scope created-by-me to created_by_me, and assigned-to-me to assigned_to_me +merge_request: 44799 +author: +type: deprecated diff --git a/doc/api/issues.md b/doc/api/issues.md index 7479c1d2f93..d0063e0b8a2 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -38,8 +38,8 @@ GET /issues?my_reaction_emoji=star | `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `milestone` | string | no | The milestone title | -| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` _([Introduced][ce-13004] in GitLab 9.5)_ | -| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`. _([Introduced][ce-13004] in GitLab 9.5)_ | +| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.
_([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | +| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me`. _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ | | `iids[]` | Array[integer] | no | Return only the issues having the given `iid` | @@ -152,7 +152,7 @@ GET /groups/:id/issues?my_reaction_emoji=star | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `iids[]` | Array[integer] | no | Return only the issues having the given `iid` | | `milestone` | string | no | The milestone title | -| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.
_([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | | `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ | @@ -266,7 +266,7 @@ GET /projects/:id/issues?my_reaction_emoji=star | `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `milestone` | string | no | The milestone title | -| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13004] in GitLab 9.5)_ | +| `scope` | string | no | Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`.
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.
_([Introduced][ce-13004] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | | `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ | | `my_reaction_emoji` | string | no | Return issues reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ | @@ -1254,3 +1254,4 @@ Example response: [ce-13004]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13004 [ce-14016]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14016 [ce-17042]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17042 +[ce-18935]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18935 diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index b9a4f661777..cbd51c9870c 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -28,7 +28,7 @@ GET /merge_requests?milestone=release GET /merge_requests?labels=bug,reproduced GET /merge_requests?author_id=5 GET /merge_requests?my_reaction_emoji=star -GET /merge_requests?scope=assigned-to-me +GET /merge_requests?scope=assigned_to_me ``` Parameters: @@ -45,8 +45,8 @@ Parameters: | `created_before` | datetime | no | Return merge requests created on or before the given time | | `updated_after` | datetime | no | Return merge requests updated on or after the given time | | `updated_before` | datetime | no | Return merge requests updated on or before the given time | -| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` | -| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me` | +| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead. | +| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned_to_me` | | `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` | | `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ | | `source_branch` | string | no | Return merge requests with the given source branch | @@ -164,7 +164,7 @@ Parameters: | `created_before` | datetime | no | Return merge requests created on or before the given time | | `updated_after` | datetime | no | Return merge requests updated on or after the given time | | `updated_before` | datetime | no | Return merge requests updated on or before the given time | -| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13060] in GitLab 9.5)_ | +| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`.
For versions before 11.0, use the now deprecated `created-by-me` or `assigned-to-me` scopes instead.
_([Introduced][ce-13060] in GitLab 9.5. [Changed to snake_case][ce-18935] in GitLab 11.0)_ | | `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | | `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ | | `my_reaction_emoji` | string | no | Return merge requests reacted by the authenticated user by the given `emoji` _([Introduced][ce-14016] in GitLab 10.0)_ | @@ -1460,3 +1460,4 @@ Example response: [ce-13060]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13060 [ce-14016]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14016 [ce-15454]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15454 +[ce-18935]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18935 diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 12ff2a1398b..257369ee7b0 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -13,6 +13,7 @@ module API args.delete(:id) args[:milestone_title] = args.delete(:milestone) args[:label_name] = args.delete(:labels) + args[:scope] = args[:scope].underscore if args[:scope] issues = IssuesFinder.new(current_user, args).execute .preload(:assignees, :labels, :notes, :timelogs) @@ -36,8 +37,8 @@ module API optional :updated_before, type: DateTime, desc: 'Return issues updated before the specified time' optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID' optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID' - optional :scope, type: String, values: %w[created-by-me assigned-to-me all], - desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`' + optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], + desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' use :pagination end @@ -66,8 +67,8 @@ module API optional :state, type: String, values: %w[opened closed all], default: 'all', desc: 'Return opened, closed, or all issues' use :issues_params - optional :scope, type: String, values: %w[created-by-me assigned-to-me all], default: 'created-by-me', - desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`' + optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], default: 'created_by_me', + desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`' end get do issues = paginate(find_issues) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index d4cc18f622b..bc4df16e3a8 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -38,6 +38,7 @@ module API args[:milestone_title] = args.delete(:milestone) args[:label_name] = args.delete(:labels) + args[:scope] = args[:scope].underscore if args[:scope] merge_requests = MergeRequestsFinder.new(current_user, args).execute .reorder(args[:order_by] => args[:sort]) @@ -79,8 +80,8 @@ module API optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request' optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID' optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID' - optional :scope, type: String, values: %w[created-by-me assigned-to-me all], - desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`' + optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], + desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`' optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' optional :source_branch, type: String, desc: 'Return merge requests with the given source branch' optional :target_branch, type: String, desc: 'Return merge requests with the given target branch' @@ -95,8 +96,8 @@ module API end params do use :merge_requests_params - optional :scope, type: String, values: %w[created-by-me assigned-to-me all], default: 'created-by-me', - desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`' + optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], default: 'created_by_me', + desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`' end get do authenticate! unless params[:scope] == 'all' diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index 45439640ea3..74e91b02f0f 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -372,7 +372,7 @@ describe IssuesFinder do end context 'personal scope' do - let(:scope) { 'assigned-to-me' } + let(:scope) { 'assigned_to_me' } it 'returns issue assigned to the user' do expect(issues).to contain_exactly(issue1, issue2) diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 90f9c4ad214..6457af77729 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -106,6 +106,15 @@ describe API::Issues do it 'returns issues assigned to me' do issue2 = create(:issue, assignees: [user2], project: project) + get api('/issues', user2), scope: 'assigned_to_me' + + expect_paginated_array_response(size: 1) + expect(first_issue['id']).to eq(issue2.id) + end + + it 'returns issues assigned to me (kebab-case)' do + issue2 = create(:issue, assignees: [user2], project: project) + get api('/issues', user2), scope: 'assigned-to-me' expect_paginated_array_response(size: 1) diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index f64623d7018..1eeeb4f1045 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -34,8 +34,7 @@ describe API::MergeRequests do it 'returns an array of all merge requests' do get api('/merge_requests', user), scope: 'all' - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array + expect_paginated_array_response end it "returns authentication error without any scope" do @@ -50,11 +49,23 @@ describe API::MergeRequests do expect(response).to have_gitlab_http_status(401) end + it "returns authentication error when scope is assigned_to_me" do + get api("/merge_requests"), scope: 'assigned_to_me' + + expect(response).to have_gitlab_http_status(401) + end + it "returns authentication error when scope is created-by-me" do get api("/merge_requests"), scope: 'created-by-me' expect(response).to have_gitlab_http_status(401) end + + it "returns authentication error when scope is created_by_me" do + get api("/merge_requests"), scope: 'created_by_me' + + expect(response).to have_gitlab_http_status(401) + end end context 'when authenticated' do @@ -62,27 +73,14 @@ describe API::MergeRequests do let!(:merge_request2) { create(:merge_request, :simple, author: user, assignee: user, source_project: project2, target_project: project2) } let(:user2) { create(:user) } - it 'returns an array of all merge requests' do - get api('/merge_requests', user), scope: :all - - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.map { |mr| mr['id'] }) - .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request2.id) - end - - it 'does not return unauthorized merge requests' do + it 'returns an array of all merge requests except unauthorized ones' do private_project = create(:project, :private) merge_request3 = create(:merge_request, :simple, source_project: private_project, target_project: private_project, source_branch: 'other-branch') get api('/merge_requests', user), scope: :all - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.map { |mr| mr['id'] }) - .not_to include(merge_request3.id) + expect_response_contain_exactly(merge_request2, merge_request_merged, merge_request_closed, merge_request) + expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request3.id) end it 'returns an array of merge requests created by current user if no scope is given' do @@ -90,10 +88,7 @@ describe API::MergeRequests do get api('/merge_requests', user2) - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request3.id) + expect_response_ordered_exactly(merge_request3) end it 'returns an array of merge requests authored by the given user' do @@ -101,10 +96,7 @@ describe API::MergeRequests do get api('/merge_requests', user), author_id: user2.id, scope: :all - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request3.id) + expect_response_ordered_exactly(merge_request3) end it 'returns an array of merge requests assigned to the given user' do @@ -112,32 +104,39 @@ describe API::MergeRequests do get api('/merge_requests', user), assignee_id: user2.id, scope: :all - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request3.id) + expect_response_ordered_exactly(merge_request3) end it 'returns an array of merge requests assigned to me' do merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch') + get api('/merge_requests', user2), scope: 'assigned_to_me' + + expect_response_ordered_exactly(merge_request3) + end + + it 'returns an array of merge requests assigned to me (kebab-case)' do + merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch') + get api('/merge_requests', user2), scope: 'assigned-to-me' - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request3.id) + expect_response_ordered_exactly(merge_request3) end it 'returns an array of merge requests created by me' do merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + get api('/merge_requests', user2), scope: 'created_by_me' + + expect_response_ordered_exactly(merge_request3) + end + + it 'returns an array of merge requests created by me (kebab-case)' do + merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch') + get api('/merge_requests', user2), scope: 'created-by-me' - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request3.id) + expect_response_ordered_exactly(merge_request3) end it 'returns merge requests reacted by the authenticated user by the given emoji' do @@ -146,19 +145,14 @@ describe API::MergeRequests do get api('/merge_requests', user2), my_reaction_emoji: award_emoji.name, scope: 'all' - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request3.id) + expect_response_ordered_exactly(merge_request3) end context 'source_branch param' do it 'returns merge requests with the given source branch' do get api('/merge_requests', user), source_branch: merge_request_closed.source_branch, state: 'all' - expect(json_response.length).to eq(2) - expect(json_response.map { |mr| mr['id'] }) - .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + expect_response_contain_exactly(merge_request_closed, merge_request_merged) end end @@ -166,9 +160,7 @@ describe API::MergeRequests do it 'returns merge requests with the given target branch' do get api('/merge_requests', user), target_branch: merge_request_closed.target_branch, state: 'all' - expect(json_response.length).to eq(2) - expect(json_response.map { |mr| mr['id'] }) - .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + expect_response_contain_exactly(merge_request_closed, merge_request_merged) end end @@ -177,8 +169,7 @@ describe API::MergeRequests do get api('/merge_requests?created_before=2000-01-02T00:00:00.060Z', user) - expect(json_response.size).to eq(1) - expect(json_response.first['id']).to eq(merge_request2.id) + expect_response_ordered_exactly(merge_request2) end it 'returns merge requests created after a specific date' do @@ -186,8 +177,7 @@ describe API::MergeRequests do get api("/merge_requests?created_after=#{merge_request2.created_at}", user) - expect(json_response.size).to eq(1) - expect(json_response.first['id']).to eq(merge_request2.id) + expect_response_ordered_exactly(merge_request2) end it 'returns merge requests updated before a specific date' do @@ -195,8 +185,7 @@ describe API::MergeRequests do get api('/merge_requests?updated_before=2000-01-02T00:00:00.060Z', user) - expect(json_response.size).to eq(1) - expect(json_response.first['id']).to eq(merge_request2.id) + expect_response_ordered_exactly(merge_request2) end it 'returns merge requests updated after a specific date' do @@ -204,8 +193,7 @@ describe API::MergeRequests do get api("/merge_requests?updated_after=#{merge_request2.updated_at}", user) - expect(json_response.size).to eq(1) - expect(json_response.first['id']).to eq(merge_request2.id) + expect_response_ordered_exactly(merge_request2) end context 'search params' do @@ -216,15 +204,13 @@ describe API::MergeRequests do it 'returns merge requests matching given search string for title' do get api("/merge_requests", user), search: merge_request.title - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request.id) + expect_response_ordered_exactly(merge_request) end it 'returns merge requests for project matching given search string for description' do get api("/merge_requests", user), project_id: project.id, search: merge_request.description - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request.id) + expect_response_ordered_exactly(merge_request) end end end @@ -235,8 +221,7 @@ describe API::MergeRequests do it 'returns merge requests for public projects' do get api("/projects/#{project.id}/merge_requests") - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array + expect_paginated_array_response end it "returns 404 for non public projects" do @@ -265,10 +250,7 @@ describe API::MergeRequests do it "returns an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect_response_ordered_exactly(merge_request_merged, merge_request_closed, merge_request) expect(json_response.last['title']).to eq(merge_request.title) expect(json_response.last).to have_key('web_url') expect(json_response.last['sha']).to eq(merge_request.diff_head_sha) @@ -286,11 +268,8 @@ describe API::MergeRequests do it "returns an array of all merge_requests using simple mode" do get api("/projects/#{project.id}/merge_requests?view=simple", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers + expect_response_ordered_exactly(merge_request_merged, merge_request_closed, merge_request) expect(json_response.last.keys).to match_array(%w(id iid title web_url created_at description project_id state updated_at)) - expect(json_response).to be_an Array - expect(json_response.length).to eq(3) expect(json_response.last['iid']).to eq(merge_request.iid) expect(json_response.last['title']).to eq(merge_request.title) expect(json_response.last).to have_key('web_url') @@ -302,51 +281,36 @@ describe API::MergeRequests do it "returns an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests?state", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(3) + expect_response_ordered_exactly(merge_request_merged, merge_request_closed, merge_request) expect(json_response.last['title']).to eq(merge_request.title) end it "returns an array of open merge_requests" do get api("/projects/#{project.id}/merge_requests?state=opened", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) + expect_response_ordered_exactly(merge_request) expect(json_response.last['title']).to eq(merge_request.title) end it "returns an array of closed merge_requests" do get api("/projects/#{project.id}/merge_requests?state=closed", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) + expect_response_ordered_exactly(merge_request_closed) expect(json_response.first['title']).to eq(merge_request_closed.title) end it "returns an array of merged merge_requests" do get api("/projects/#{project.id}/merge_requests?state=merged", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) + expect_response_ordered_exactly(merge_request_merged) expect(json_response.first['title']).to eq(merge_request_merged.title) end it 'returns merge_request by "iids" array' do get api("/projects/#{project.id}/merge_requests", user), iids: [merge_request.iid, merge_request_closed.iid] - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(2) + expect_response_ordered_exactly(merge_request_closed, merge_request) expect(json_response.first['title']).to eq merge_request_closed.title - expect(json_response.first['id']).to eq merge_request_closed.id end it 'matches V4 response schema' do @@ -359,16 +323,14 @@ describe API::MergeRequests do it 'returns an empty array if no issue matches milestone' do get api("/projects/#{project.id}/merge_requests", user), milestone: '1.0.0' - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array + expect_paginated_array_response expect(json_response.length).to eq(0) end it 'returns an empty array if milestone does not exist' do get api("/projects/#{project.id}/merge_requests", user), milestone: 'foo' - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array + expect_paginated_array_response expect(json_response.length).to eq(0) end @@ -382,17 +344,13 @@ describe API::MergeRequests do it 'returns an array of merge requests matching state in milestone' do get api("/projects/#{project.id}/merge_requests", user), milestone: '0.9', state: 'closed' - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(merge_request_closed.id) + expect_response_ordered_exactly(merge_request_closed) end it 'returns an array of labeled merge requests' do get api("/projects/#{project.id}/merge_requests?labels=#{label.title}", user) - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array + expect_paginated_array_response expect(json_response.length).to eq(1) expect(json_response.first['labels']).to eq([label2.title, label.title]) end @@ -400,16 +358,14 @@ describe API::MergeRequests do it 'returns an array of labeled merge requests where all labels match' do get api("/projects/#{project.id}/merge_requests?labels=#{label.title},foo,bar", user) - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array + expect_paginated_array_response expect(json_response.length).to eq(0) end it 'returns an empty array if no merge request matches labels' do get api("/projects/#{project.id}/merge_requests?labels=foo,bar", user) - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array + expect_paginated_array_response expect(json_response.length).to eq(0) end @@ -427,13 +383,12 @@ describe API::MergeRequests do get api("/projects/#{project.id}/merge_requests?labels=#{bug_label.title}&milestone=#{milestone1.title}&state=merged", user) - expect(response).to have_gitlab_http_status(200) - expect(json_response).to be_an Array - expect(json_response.length).to eq(1) - expect(json_response.first['id']).to eq(mr2.id) + expect_response_ordered_exactly(mr2) end context "with ordering" do + let(:merge_requests) { [merge_request_merged, merge_request_closed, merge_request] } + before do @mr_later = mr_with_later_created_and_updated_at_time @mr_earlier = mr_with_earlier_created_and_updated_at_time @@ -442,45 +397,25 @@ describe API::MergeRequests do it "returns an array of merge_requests in ascending order" do get api("/projects/#{project.id}/merge_requests?sort=asc", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(3) - response_dates = json_response.map { |merge_request| merge_request['created_at'] } - expect(response_dates).to eq(response_dates.sort) + expect_response_ordered_exactly(*merge_requests.sort_by { |mr| mr['created_at'] }) end it "returns an array of merge_requests in descending order" do get api("/projects/#{project.id}/merge_requests?sort=desc", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(3) - response_dates = json_response.map { |merge_request| merge_request['created_at'] } - expect(response_dates).to eq(response_dates.sort.reverse) + expect_response_ordered_exactly(*merge_requests.sort_by { |mr| mr['created_at'] }.reverse) end it "returns an array of merge_requests ordered by updated_at" do get api("/projects/#{project.id}/merge_requests?order_by=updated_at", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(3) - response_dates = json_response.map { |merge_request| merge_request['updated_at'] } - expect(response_dates).to eq(response_dates.sort.reverse) + expect_response_ordered_exactly(*merge_requests.sort_by { |mr| mr['updated_at'] }.reverse) end it "returns an array of merge_requests ordered by created_at" do get api("/projects/#{project.id}/merge_requests?order_by=created_at&sort=asc", user) - expect(response).to have_gitlab_http_status(200) - expect(response).to include_pagination_headers - expect(json_response).to be_an Array - expect(json_response.length).to eq(3) - response_dates = json_response.map { |merge_request| merge_request['created_at'] } - expect(response_dates).to eq(response_dates.sort) + expect_response_ordered_exactly(*merge_requests.sort_by { |mr| mr['created_at'] }) end end @@ -488,9 +423,7 @@ describe API::MergeRequests do it 'returns merge requests with the given source branch' do get api('/merge_requests', user), source_branch: merge_request_closed.source_branch, state: 'all' - expect(json_response.length).to eq(2) - expect(json_response.map { |mr| mr['id'] }) - .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + expect_response_contain_exactly(merge_request_closed, merge_request_merged) end end @@ -498,9 +431,7 @@ describe API::MergeRequests do it 'returns merge requests with the given target branch' do get api('/merge_requests', user), target_branch: merge_request_closed.target_branch, state: 'all' - expect(json_response.length).to eq(2) - expect(json_response.map { |mr| mr['id'] }) - .to contain_exactly(merge_request_closed.id, merge_request_merged.id) + expect_response_contain_exactly(merge_request_closed, merge_request_merged) end end end @@ -1341,4 +1272,22 @@ describe API::MergeRequests do merge_request_closed.save merge_request_closed end + + def expect_response_contain_exactly(*items) + expect_paginated_array_response + expect(json_response.length).to eq(items.size) + expect(json_response.map { |element| element['id'] }).to contain_exactly(*items.map(&:id)) + end + + def expect_response_ordered_exactly(*items) + expect_paginated_array_response + expect(json_response.length).to eq(items.size) + expect(json_response.map { |element| element['id'] }).to eq(items.map(&:id)) + end + + def expect_paginated_array_response + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + end end From c1e89492375b696b25ff63257c3cbe58ce86cc18 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Wed, 16 May 2018 21:35:24 +0900 Subject: [PATCH 129/145] Rescue Kubeclient::HttpError when generating prometheus_client --- app/models/clusters/applications/prometheus.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb index 7b25d8c4089..c702c4ee807 100644 --- a/app/models/clusters/applications/prometheus.rb +++ b/app/models/clusters/applications/prometheus.rb @@ -49,6 +49,11 @@ module Clusters # ensures headers containing auth data are appended to original k8s client options options = kube_client.rest_client.options.merge(headers: kube_client.headers) RestClient::Resource.new(proxy_url, options) + rescue Kubeclient::HttpError + # If users have mistakenly set parameters or removed the depended clusters, + # `proxy_url` could raise an exception because gitlab can not communicate with the cluster. + # Since `PrometheusAdapter#can_query?` is eargely loaded on environement pages in gitlab, + # we need to silence the exceptions end private From f1b43c1e4724a3e945a089fe60bb2f9a50052241 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Thu, 17 May 2018 15:47:48 +0900 Subject: [PATCH 130/145] Add spec for unathrozied proxy_url --- .../projects/environments/environments_spec.rb | 16 ++++++++++++++++ .../clusters/applications/prometheus_spec.rb | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index 5248a783db4..c9be5f6ce54 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -42,6 +42,22 @@ feature 'Environments page', :js do expect(page).to have_content('You don\'t have any environments right now') end end + + context 'when cluster is not reachable' do + let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } + let!(:application_prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } + + before do + allow_any_instance_of(Kubeclient::Client).to receive(:proxy_url).with(anything, anything, anything, anything).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) + end + + it 'should show one environment without error' do + visit_environments(project, scope: 'available') + + expect(page).to have_css('.environments-container') + expect(page.all('.environment-name').length).to eq(1) + end + end end describe 'with one stopped environment' do diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index aeca6ee903a..52404b0e247 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -85,6 +85,16 @@ describe Clusters::Applications::Prometheus do it 'copies options and headers from kube client to proxy client' do expect(subject.prometheus_client.options).to eq(kube_client.rest_client.options.merge(headers: kube_client.headers)) end + + context 'when cluster is not reachable' do + before do + allow(kube_client).to receive(:proxy_url).with(anything, anything, anything, anything).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) + end + + it 'returns nil' do + expect(subject.prometheus_client).to be_nil + end + end end end From c5bce1dc90e936eb1a1c09a66fa09471cbcf3861 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Thu, 17 May 2018 15:50:43 +0900 Subject: [PATCH 131/145] Add changelog --- .../unreleased/fix-kube_client-proxy_url-exception.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/fix-kube_client-proxy_url-exception.yml diff --git a/changelogs/unreleased/fix-kube_client-proxy_url-exception.yml b/changelogs/unreleased/fix-kube_client-proxy_url-exception.yml new file mode 100644 index 00000000000..1f64ab9f30f --- /dev/null +++ b/changelogs/unreleased/fix-kube_client-proxy_url-exception.yml @@ -0,0 +1,5 @@ +--- +title: Fix corrupted environment pages with unathorized proxy url +merge_request: 18989 +author: +type: fixed From 197d3e69d4e93067ab356db9df6df325e05fe4ef Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 21 May 2018 14:52:26 +0900 Subject: [PATCH 132/145] Remove unnecessary `with` from `allow` --- spec/features/projects/environments/environments_spec.rb | 2 +- spec/models/clusters/applications/prometheus_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb index c9be5f6ce54..f9defa22d35 100644 --- a/spec/features/projects/environments/environments_spec.rb +++ b/spec/features/projects/environments/environments_spec.rb @@ -48,7 +48,7 @@ feature 'Environments page', :js do let!(:application_prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } before do - allow_any_instance_of(Kubeclient::Client).to receive(:proxy_url).with(anything, anything, anything, anything).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) + allow_any_instance_of(Kubeclient::Client).to receive(:proxy_url).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) end it 'should show one environment without error' do diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index 52404b0e247..407e2fc598a 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -88,7 +88,7 @@ describe Clusters::Applications::Prometheus do context 'when cluster is not reachable' do before do - allow(kube_client).to receive(:proxy_url).with(anything, anything, anything, anything).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) + allow(kube_client).to receive(:proxy_url).and_raise(Kubeclient::HttpError.new(401, 'Unauthorized', nil)) end it 'returns nil' do From cd7702e5cd4ffcb1989d5fe758e3971a8952c5c0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 21 May 2018 10:07:17 +0200 Subject: [PATCH 133/145] Fix Rubocop offense in create pipeline services :cop: --- spec/services/ci/create_pipeline_service_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index be0255a2ccc..2b88fcc9a96 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -404,7 +404,8 @@ describe Ci::CreatePipelineService do config = YAML.dump( deploy: { environment: { name: "review/id1$CI_PIPELINE_ID/id2$CI_BUILD_ID" }, - script: 'ls' } + script: 'ls' + } ) stub_ci_pipeline_yaml_file(config) From bc535d4f885e41ec7069325bb198767770fefbb8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 21 May 2018 16:41:28 +0800 Subject: [PATCH 134/145] Generate the key while running the tests --- .../specs/features/project/deploy_key_clone_spec.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb index bf8fa230244..442ac312b4d 100644 --- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb +++ b/qa/qa/specs/features/project/deploy_key_clone_spec.rb @@ -33,13 +33,15 @@ module QA end keys = [ - Runtime::Key::RSA.new(8192), - Runtime::Key::ECDSA.new(521), - Runtime::Key::ED25519.new + [Runtime::Key::RSA, 8192], + [Runtime::Key::ECDSA, 521], + [Runtime::Key::ED25519] ] - keys.each do |key| - scenario "user sets up a deploy key with #{key.name}(#{key.bits}) to clone code using pipelines" do + keys.each do |(key_class, bits)| + scenario "user sets up a deploy key with #{key_class}(#{bits}) to clone code using pipelines" do + key = key_class.new(*bits) + login Factory::Resource::DeployKey.fabricate! do |resource| From bc57a62a5c8c4c1e08cdf5c4a1656357d885026d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 18 May 2018 15:32:07 +0200 Subject: [PATCH 135/145] Improve the single-script jobs CI config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .gitlab-ci.yml | 59 ++++++++++++++++++++-------------------- scripts/prepare_build.sh | 2 +- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 84d8e69b84e..7e65bb204c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -126,6 +126,23 @@ stages: <<: *dedicated-no-docs-pull-cache-job <<: *except-docs-and-qa +.single-script-job: &single-script-job + image: ruby:2.4-alpine + before_script: [] + stage: build + cache: {} + dependencies: [] + variables: &single-script-job-variables + GIT_STRATEGY: none + before_script: + # We need to download the script rather than clone the repo since the + # package-and-qa job will not be able to run when the branch gets + # deleted (when merging the MR). + - export SCRIPT_NAME="${SCRIPT_NAME:-$CI_JOB_NAME}" + - apk add --update openssl + - wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/$SCRIPT_NAME + - chmod 755 $SCRIPT_NAME + .rake-exec: &rake-exec <<: *dedicated-no-docs-no-db-pull-cache-job script: @@ -207,19 +224,10 @@ stages: .review-docs: &review-docs <<: *dedicated-runner <<: *except-qa - image: ruby:2.4-alpine - before_script: - - gem install gitlab --no-doc - # We need to download the script rather than clone the repo since the - # review-docs-cleanup job will not be able to run when the branch gets - # deleted (when merging the MR). - - apk add --update openssl - - wget https://gitlab.com/gitlab-org/gitlab-ce/raw/master/scripts/trigger-build-docs - - chmod 755 trigger-build-docs - cache: {} - dependencies: [] + <<: *single-script-job variables: - GIT_STRATEGY: none + <<: *single-script-job-variables + SCRIPT_NAME: trigger-build-docs when: manual only: - branches @@ -253,23 +261,14 @@ stages: # Trigger a package build in omnibus-gitlab repository # package-and-qa: - image: ruby:2.4-alpine - before_script: [] - stage: build - cache: {} - when: manual + <<: *single-script-job variables: - GIT_STRATEGY: none + <<: *single-script-job-variables + SCRIPT_NAME: trigger-build-omnibus retry: 0 - before_script: - # We need to download the script rather than clone the repo since the - # package-and-qa job will not be able to run when the branch gets - # deleted (when merging the MR). - - apk add --update openssl - - wget https://gitlab.com/$CI_PROJECT_PATH/raw/$CI_COMMIT_SHA/scripts/trigger-build-omnibus - - chmod 755 trigger-build-omnibus script: - - ./trigger-build-omnibus + - ./$SCRIPT_NAME + when: manual only: - //@gitlab-org/gitlab-ce - //@gitlab-org/gitlab-ee @@ -286,7 +285,8 @@ review-docs-deploy: url: http://$DOCS_GITLAB_REPO_SUFFIX-$CI_COMMIT_REF_SLUG.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX on_stop: review-docs-cleanup script: - - ./trigger-build-docs deploy + - gem install gitlab --no-ri --no-rdoc + - ./$SCRIPT_NAME deploy # Cleanup remote environment of gitlab-docs review-docs-cleanup: @@ -296,7 +296,8 @@ review-docs-cleanup: name: review-docs/$CI_COMMIT_REF_NAME action: stop script: - - ./trigger-build-docs cleanup + - gem install gitlab --no-ri --no-rdoc + - ./SCRIPT_NAME cleanup # Retrieve knapsack and rspec_flaky reports retrieve-tests-metadata: @@ -325,7 +326,7 @@ update-tests-metadata: - rspec_flaky/ policy: push script: - - retry gem install fog-aws mime-types activesupport + - retry gem install fog-aws mime-types activesupport --no-ri --no-rdoc - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json - FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH} diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index d8bcc9f8191..75a3cea0448 100644 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -11,7 +11,7 @@ fi # Only install knapsack after bundle install! Otherwise oddly some native # gems could not be found under some circumstance. No idea why, hours wasted. -retry gem install knapsack +retry gem install knapsack --no-ri --no-rdoc cp config/gitlab.yml.example config/gitlab.yml sed -i 's/bin_path: \/usr\/bin\/git/bin_path: \/usr\/local\/bin\/git/' config/gitlab.yml From 3540254e3b990431f2a4e9fcfef99daa5b0f1e13 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 21 May 2018 11:08:17 +0100 Subject: [PATCH 136/145] Update CHANGELOG.md for 10.7.4 [ci skip] --- CHANGELOG.md | 7 +++++++ ...n-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) delete mode 100644 changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 29047c3ad65..86f12a75b7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.7.4 (2018-05-21) + +### Fixed (1 change) + +- Fix error when deleting an empty list of refs. + + ## 10.7.3 (2018-05-02) ### Fixed (8 changes) diff --git a/changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml b/changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml deleted file mode 100644 index 94da4d74300..00000000000 --- a/changelogs/unreleased/xeodon-gitlab-ce-fix-45743-master-fix-gitaly-delete-refs.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix error when deleting an empty list of refs -merge_request: -author: -type: fixed From 93349b4440add0da70f87026ae434be5c10f1ad1 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Tue, 15 May 2018 22:34:29 +0900 Subject: [PATCH 137/145] Append trace only if the job is running --- lib/api/runner.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/runner.rb b/lib/api/runner.rb index 649feba1036..a4df98cadac 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -165,7 +165,7 @@ module API body_start = content_range[0].to_i body_end = body_start + body_data.bytesize - stream_size = job.trace.append(body_data, body_start) + stream_size = job.trace.append(body_data, body_start) if job.running? unless stream_size == body_end break error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{stream_size}" }) end From 5af5815d62f3430c52bb489c590eaa32402b18fa Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 18 May 2018 16:05:11 +0900 Subject: [PATCH 138/145] Add the same gurad clause with artifacts uplaoding --- lib/api/runner.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/api/runner.rb b/lib/api/runner.rb index a4df98cadac..a7f1cb1131f 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -149,6 +149,7 @@ module API end patch '/:id/trace' do job = authenticate_job! + forbidden!('Job is not running') unless job.running? error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range') content_range = request.headers['Content-Range'] @@ -165,7 +166,7 @@ module API body_start = content_range[0].to_i body_end = body_start + body_data.bytesize - stream_size = job.trace.append(body_data, body_start) if job.running? + stream_size = job.trace.append(body_data, body_start) unless stream_size == body_end break error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{stream_size}" }) end From e4ebee4246ec389ec52e0d9453db338ce2993084 Mon Sep 17 00:00:00 2001 From: Joshua Lambert Date: Mon, 21 May 2018 10:48:59 +0000 Subject: [PATCH 139/145] Remove experiemental notes from Prometheus docs --- doc/administration/monitoring/prometheus/gitlab_metrics.md | 4 ++-- doc/administration/monitoring/prometheus/index.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index 69600cad25c..411a0fae93f 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -1,7 +1,7 @@ # GitLab Prometheus metrics >**Note:** -Available since [Omnibus GitLab 9.3][29118]. Currently experimental. For +Available since [Omnibus GitLab 9.3][29118]. For installations from source you'll have to configure it yourself. To enable the GitLab Prometheus metrics: @@ -24,7 +24,7 @@ server, because the embedded server configuration is overwritten once every ## Metrics available -In this experimental phase, only a few metrics are available: +The following metrics are available: | Metric | Type | Since | Description | |:--------------------------------- |:--------- |:----- |:----------- | diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md index 3d24812c66a..f47add48345 100644 --- a/doc/administration/monitoring/prometheus/index.md +++ b/doc/administration/monitoring/prometheus/index.md @@ -120,7 +120,7 @@ To disable the monitoring of Kubernetes: ## GitLab Prometheus metrics -> Introduced as an experimental feature in GitLab 9.3. +> Introduced in GitLab 9.3. GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other exporters, this endpoint requires authentication as it is available on the same URL and port as user traffic. From b053c483e792e00c50ba57e461e3a09ebdb8f34d Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 21 May 2018 21:11:00 +0900 Subject: [PATCH 140/145] Add test for the cancelled jobs --- spec/requests/api/runner_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index da392c5ab81..efb9bddde44 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -918,6 +918,22 @@ describe API::Runner, :clean_gitlab_redis_shared_state do expect(job.reload.trace.raw).to eq 'BUILD TRACE appended appended' end + context 'when job is cancelled' do + before do + job.cancel + end + + context 'when trace is patched' do + before do + patch_the_trace + end + + it 'returns Forbidden ' do + expect(response.status).to eq(403) + end + end + end + context 'when redis data are flushed' do before do redis_shared_state_cleanup! From c94b607ae6150808a063fc6b2916bda0dc4850ab Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Mon, 21 May 2018 21:42:01 +0900 Subject: [PATCH 141/145] Add changelog --- .../unreleased/create-live-trace-only-if-job-is-complete.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/create-live-trace-only-if-job-is-complete.yml diff --git a/changelogs/unreleased/create-live-trace-only-if-job-is-complete.yml b/changelogs/unreleased/create-live-trace-only-if-job-is-complete.yml new file mode 100644 index 00000000000..f32c70cf884 --- /dev/null +++ b/changelogs/unreleased/create-live-trace-only-if-job-is-complete.yml @@ -0,0 +1,5 @@ +--- +title: Forbid to patch traces for finished jobs +merge_request: 18969 +author: +type: fixed From 1c23c3ff3e71e3b7ff6e8b282bab095b58331ddd Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Mon, 21 May 2018 11:04:05 -0500 Subject: [PATCH 142/145] Update CHANGELOG.md for 10.8.0 [ci skip] --- CHANGELOG.md | 176 ++++++++++++++++++ .../10244-add-project-ci-cd-settings.yml | 5 - .../unreleased/16957-issue-due-email.yml | 5 - .../unreleased/21677-run-pipeline-word.yml | 5 - .../25010-collapsed-sidebar-tooltips.yml | 5 - ...ed-information-on-project-members-page.yml | 5 - ...-fix-template-selector-menu-visibility.yml | 6 - .../33697-pipelines-json-endpoint.yml | 5 - .../33697-remove-ujs-action-big-graph.yml | 5 - ...34262-show-current-labels-when-editing.yml | 5 - ...ile-project-templates-with-auto-devops.yml | 5 - .../36983-osw-heading-labels-color-fix.yml | 5 - ...estimate-system-notes-can-be-confusing.yml | 5 - .../unreleased/40487-axios-pipelines.yml | 4 - ...lculate-artifact-size-more-efficiently.yml | 5 - ...ploykeys-table-more-clearly-structured.yml | 5 - .../41748-vertical-misalignment-login-box.yml | 5 - ...owner-to-enable-runners-from-subgroups.yml | 5 - ...-port-push-mirroring-to-ce-ce-port-v-2.yml | 5 - ...ivergence-graph-on-branches-for-mobile.yml | 5 - .../42803-show-new-branch-mr-button.yml | 5 - .../42889-avoid-return-inside-block.yml | 5 - ...36-improve-ns-factory-avoid-duplicates.yml | 6 - ...dex-executes-more-than-100-sql-queries.yml | 5 - .../unreleased/43404-pipelines-commit.yml | 5 - ...-make-auto-devops-settings-first-class.yml | 5 - .../unreleased/43469-gcp-account-offer.yml | 5 - .../43557-osw-present-merge-sha-commit.yml | 5 - changelogs/unreleased/43567-replace-gke.yml | 5 - changelogs/unreleased/43617-mailsig.yml | 5 - ...xecuting-a-manual-pipeline-from-the-ui.yml | 5 - changelogs/unreleased/44224-remove-gl.yml | 5 - changelogs/unreleased/44296-commit-path.yml | 6 - .../44447-expose-deploy-token-to-ci-cd.yml | 5 - ...4541-fix-file-tree-commit-status-cache.yml | 5 - .../44582-clear-pipeline-status-cache.yml | 5 - changelogs/unreleased/44697-prevue.yml | 5 - .../44833-ide-clean-up-status-bar.yml | 5 - ...4834-ide-remove-branch-from-bottom-bar.yml | 5 - changelogs/unreleased/44879.yml | 5 - ...4985-fix-protected-branch-delete-modal.yml | 5 - .../unreleased/45159-fix-illustration.yml | 5 - ...97-update-faraday_middleware-to-0-12-2.yml | 5 - .../unreleased/45398-fix-rss-button.yml | 5 - ...ng-viewer-undefined-method-html_escape.yml | 5 - ...-user-or-delete-user-and-contributions.yml | 6 - .../unreleased/45481-sane-pages-artifacts.yml | 6 - ...rs-invitations-scheduled-before-commit.yml | 5 - ...6-fix-create-project-for-user-endpoint.yml | 5 - .../45666-project-ci-lint-links.yml | 5 - ...1-replace-actionview-time_ago_in_words.yml | 5 - ...oken-due-to-the-addition-of-a-ci-table.yml | 5 - .../46210-terms-acceptance-dropdown-menu.yml | 5 - .../46286-fix-ingress-rbac-default-value.yml | 5 - ...6303_copy_button_fix_embedded_snippets.yml | 5 - ...kubernetes-popover-illustration-skewed.yml | 5 - ...950-unassign-slash-command-preview-fix.yml | 5 - ...cksum-git-commanderror-exit-status-128.yml | 6 - ...-t-connect-to-geo-tracking-database-ce.yml | 5 - .../8088_embedded_snippets_support.yml | 5 - ...259-atomic-internal-ids-for-all-models.yml | 5 - changelogs/unreleased/accessible-text.yml | 6 - .../unreleased/add-copy-metadata-command.yml | 5 - ...git-commit-message-predefined-variable.yml | 5 - ...icon-padding-for-pipeline-environments.yml | 5 - .../add-padding-to-profile-description.yml | 5 - ...lign-project-avatar-on-small-viewports.yml | 5 - .../ash-mckenzie-include-sha-with-version.yml | 5 - ...0ne-add-missing-changelog-type-to-docs.yml | 5 - ...inach-project-builds-artifacts-feature.yml | 5 - ...inach-project-commits-branches-feature.yml | 5 - ...inach-project-commits-comments-feature.yml | 5 - ...nach-project-issues-milestones-feature.yml | 5 - ...project-source-markdown-render-feature.yml | 5 - ...d-card-title-and-issueable-header-text.yml | 5 - changelogs/unreleased/bvl-enforce-terms.yml | 5 - .../bvl-restrict-api-git-for-terms.yml | 6 - .../bvl-shared-groups-on-group-page.yml | 5 - .../unreleased/bw-add-console-message.yml | 5 - ...ont-for-tables-inside-diff-discussions.yml | 5 - ...ecation-warning-for-dynamic-milestones.yml | 5 - ...dm-webhook-catch-blocked-url-exception.yml | 6 - ...ploy-policy-for-staging-and-production.yml | 6 - .../dz-add-2fa-filter-admin-api.yml | 5 - ...ture-add-language-in-repository-to-api.yml | 5 - .../unreleased/feature-add_target_to_tags.yml | 5 - .../feature-display-active-sessions.yml | 5 - .../unreleased/feature-runner-per-group.yml | 5 - ...-groups-user-is-member-of-in-dashboard.yml | 5 - ...fix-gb-add-pipeline-builds-foreign-key.yml | 5 - ...sistent-protected-branch-pill-baseline.yml | 5 - .../unreleased/fix-metrics-content-types.yml | 5 - .../fix-project-mirror-data-schema.yml | 6 - .../fix-shortcut-close-screen-with-key.yml | 5 - .../fix-wiki-find-page-invalid-encoding.yml | 5 - ...om-hooks-not-triggered-by-UI-wiki-edit.yml | 5 - .../fj-45057-improve-ssrf-documentation.yml | 5 - .../fj-change-gollum-gems-to-custom-ones.yml | 5 - .../unreleased/fl-pipelines-details-axios.yml | 5 - .../unreleased/helm-add-alpine-mirrors.yml | 5 - changelogs/unreleased/ide-file-finder.yml | 5 - .../unreleased/ide-improve-commit-panel.yml | 5 - .../improve-commit-message-body-rendering.yml | 5 - .../improve-jobs-queuing-time-metric.yml | 5 - .../improve-quick-actions-summary-preview.yml | 5 - ...ncrease-new-issue-metadata-form-margin.yml | 5 - ...re-no-project-import-options-available.yml | 5 - changelogs/unreleased/issue_43660.yml | 5 - .../jivl-refactor-activity-calendar.yml | 5 - .../unreleased/jprovazn-commit-notes-api.yml | 5 - .../unreleased/jprovazn-generic-error.yml | 6 - .../jr-33320-lfs-settings-interface.yml | 5 - .../unreleased/jr-46209-web-ide-copy.yml | 5 - .../jramsay-44880-filter-pipelines-by-sha.yml | 5 - .../label-links-on-project-transfer.yml | 5 - .../live-trace-v2-efficient-destroy-all.yml | 5 - changelogs/unreleased/live-trace-v2.yml | 5 - .../move-board-blank-state-vue-component.yml | 5 - .../move-estimate-only-pane-vue-component.yml | 5 - .../move-help-state-vue-component.yml | 5 - ...-notification-service-calls-to-sidekiq.yml | 5 - .../move-pipeline-failed-vue-component.yml | 5 - ...tracking-spent-only-pane-vue-component.yml | 5 - ...ed-highlighted-content-for-discussions.yml | 5 - ...e-gb-improve-pipeline-creation-service.yml | 5 - ...ghost-user-or-gitlab-support-bot-in-ui.yml | 5 - ...oject-export-with-lfs-file-locks-fails.yml | 5 - ...mr-widget-ready-to-merge-vue-component.yml | 5 - ...actor-move-mr-widget-wip-vue-component.yml | 5 - ...or-move-no-tracking-pane-vue-component.yml | 5 - ...ve-sidebar-time-tracking-vue-component.yml | 5 - .../rename-overview-project-sidenav.yml | 5 - .../restore-label-underline-color.yml | 5 - ...estore-size-and-position-for-fork-icon.yml | 5 - .../revert-discussion-counter-height.yml | 5 - .../security-45689-fix-archive-cache-bug.yml | 5 - .../unreleased/security_issue_42029.yml | 5 - changelogs/unreleased/sh-bump-lograge.yml | 5 - .../show-group-id-in-group-settings.yml | 5 - .../show-runners-description-on-jobs-page.yml | 5 - .../unreleased/tc-repo-verify-mails.yml | 5 - .../unreleased/tz-upgrade-underscore.yml | 5 - ...scussions-vue-component-i18n-and-tests.yml | 5 - ...-environment-item-action-buttons-icons.yml | 5 - ...ate-timeline-icon-for-description-edit.yml | 5 - .../winh-dashboard-any-milestone.yml | 5 - .../winh-dropdown-entry-unlocking.yml | 5 - .../winh-new-mergerequest-branch-picker.yml | 5 - .../zj-branch-containing-sha-opt-out.yml | 5 - .../unreleased/zj-find-license-opt-out.yml | 5 - changelogs/unreleased/zj-fork-opt-out.yml | 5 - .../zj-namespace-service-mandatory.yml | 5 - .../unreleased/zj-ref-exists-opt-out.yml | 5 - .../unreleased/zj-repo-checksum-opt-out.yml | 5 - .../zj-repository-exist-mandatory.yml | 5 - .../zj-tag-containing-sha-opt-out.yml | 5 - 156 files changed, 176 insertions(+), 786 deletions(-) delete mode 100644 changelogs/unreleased/10244-add-project-ci-cd-settings.yml delete mode 100644 changelogs/unreleased/16957-issue-due-email.yml delete mode 100644 changelogs/unreleased/21677-run-pipeline-word.yml delete mode 100644 changelogs/unreleased/25010-collapsed-sidebar-tooltips.yml delete mode 100644 changelogs/unreleased/30739-fix-joined-information-on-project-members-page.yml delete mode 100644 changelogs/unreleased/32617-fix-template-selector-menu-visibility.yml delete mode 100644 changelogs/unreleased/33697-pipelines-json-endpoint.yml delete mode 100644 changelogs/unreleased/33697-remove-ujs-action-big-graph.yml delete mode 100644 changelogs/unreleased/34262-show-current-labels-when-editing.yml delete mode 100644 changelogs/unreleased/36762-reconcile-project-templates-with-auto-devops.yml delete mode 100644 changelogs/unreleased/36983-osw-heading-labels-color-fix.yml delete mode 100644 changelogs/unreleased/40402-time-estimate-system-notes-can-be-confusing.yml delete mode 100644 changelogs/unreleased/40487-axios-pipelines.yml delete mode 100644 changelogs/unreleased/41059-calculate-artifact-size-more-efficiently.yml delete mode 100644 changelogs/unreleased/41082-make-deploykeys-table-more-clearly-structured.yml delete mode 100644 changelogs/unreleased/41748-vertical-misalignment-login-box.yml delete mode 100644 changelogs/unreleased/41981-allow-group-owner-to-enable-runners-from-subgroups.yml delete mode 100644 changelogs/unreleased/42099-port-push-mirroring-to-ce-ce-port-v-2.yml delete mode 100644 changelogs/unreleased/42543-hide-divergence-graph-on-branches-for-mobile.yml delete mode 100644 changelogs/unreleased/42803-show-new-branch-mr-button.yml delete mode 100644 changelogs/unreleased/42889-avoid-return-inside-block.yml delete mode 100644 changelogs/unreleased/42936-improve-ns-factory-avoid-duplicates.yml delete mode 100644 changelogs/unreleased/43111-controller-projects-mergerequestscontroller-index-executes-more-than-100-sql-queries.yml delete mode 100644 changelogs/unreleased/43404-pipelines-commit.yml delete mode 100644 changelogs/unreleased/43466-make-auto-devops-settings-first-class.yml delete mode 100644 changelogs/unreleased/43469-gcp-account-offer.yml delete mode 100644 changelogs/unreleased/43557-osw-present-merge-sha-commit.yml delete mode 100644 changelogs/unreleased/43567-replace-gke.yml delete mode 100644 changelogs/unreleased/43617-mailsig.yml delete mode 100644 changelogs/unreleased/44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui.yml delete mode 100644 changelogs/unreleased/44224-remove-gl.yml delete mode 100644 changelogs/unreleased/44296-commit-path.yml delete mode 100644 changelogs/unreleased/44447-expose-deploy-token-to-ci-cd.yml delete mode 100644 changelogs/unreleased/44541-fix-file-tree-commit-status-cache.yml delete mode 100644 changelogs/unreleased/44582-clear-pipeline-status-cache.yml delete mode 100644 changelogs/unreleased/44697-prevue.yml delete mode 100644 changelogs/unreleased/44833-ide-clean-up-status-bar.yml delete mode 100644 changelogs/unreleased/44834-ide-remove-branch-from-bottom-bar.yml delete mode 100644 changelogs/unreleased/44879.yml delete mode 100644 changelogs/unreleased/44985-fix-protected-branch-delete-modal.yml delete mode 100644 changelogs/unreleased/45159-fix-illustration.yml delete mode 100644 changelogs/unreleased/45397-update-faraday_middleware-to-0-12-2.yml delete mode 100644 changelogs/unreleased/45398-fix-rss-button.yml delete mode 100644 changelogs/unreleased/45436-markdown-is-not-rendering-error-loading-viewer-undefined-method-html_escape.yml delete mode 100644 changelogs/unreleased/45451-user-deletion-modal-with-same-info-for-delete-user-or-delete-user-and-contributions.yml delete mode 100644 changelogs/unreleased/45481-sane-pages-artifacts.yml delete mode 100644 changelogs/unreleased/45572-members-invitations-scheduled-before-commit.yml delete mode 100644 changelogs/unreleased/45576-fix-create-project-for-user-endpoint.yml delete mode 100644 changelogs/unreleased/45666-project-ci-lint-links.yml delete mode 100644 changelogs/unreleased/45761-replace-actionview-time_ago_in_words.yml delete mode 100644 changelogs/unreleased/46049-import-export-import-is-broken-due-to-the-addition-of-a-ci-table.yml delete mode 100644 changelogs/unreleased/46210-terms-acceptance-dropdown-menu.yml delete mode 100644 changelogs/unreleased/46286-fix-ingress-rbac-default-value.yml delete mode 100644 changelogs/unreleased/46303_copy_button_fix_embedded_snippets.yml delete mode 100644 changelogs/unreleased/46345-kubernetes-popover-illustration-skewed.yml delete mode 100644 changelogs/unreleased/4950-unassign-slash-command-preview-fix.yml delete mode 100644 changelogs/unreleased/5750-backport-checksum-git-commanderror-exit-status-128.yml delete mode 100644 changelogs/unreleased/5794-we-should-failover-gracefully-when-we-can-t-connect-to-geo-tracking-database-ce.yml delete mode 100644 changelogs/unreleased/8088_embedded_snippets_support.yml delete mode 100644 changelogs/unreleased/ab-44259-atomic-internal-ids-for-all-models.yml delete mode 100755 changelogs/unreleased/accessible-text.yml delete mode 100644 changelogs/unreleased/add-copy-metadata-command.yml delete mode 100644 changelogs/unreleased/add-git-commit-message-predefined-variable.yml delete mode 100644 changelogs/unreleased/add-loading-icon-padding-for-pipeline-environments.yml delete mode 100644 changelogs/unreleased/add-padding-to-profile-description.yml delete mode 100644 changelogs/unreleased/align-project-avatar-on-small-viewports.yml delete mode 100644 changelogs/unreleased/ash-mckenzie-include-sha-with-version.yml delete mode 100644 changelogs/unreleased/blackst0ne-add-missing-changelog-type-to-docs.yml delete mode 100644 changelogs/unreleased/blackst0ne-replace-spinach-project-builds-artifacts-feature.yml delete mode 100644 changelogs/unreleased/blackst0ne-replace-spinach-project-commits-branches-feature.yml delete mode 100644 changelogs/unreleased/blackst0ne-replace-spinach-project-commits-comments-feature.yml delete mode 100644 changelogs/unreleased/blackst0ne-replace-spinach-project-issues-milestones-feature.yml delete mode 100644 changelogs/unreleased/blackst0ne-replace-spinach-project-source-markdown-render-feature.yml delete mode 100644 changelogs/unreleased/break-issue-title-for-board-card-title-and-issueable-header-text.yml delete mode 100644 changelogs/unreleased/bvl-enforce-terms.yml delete mode 100644 changelogs/unreleased/bvl-restrict-api-git-for-terms.yml delete mode 100644 changelogs/unreleased/bvl-shared-groups-on-group-page.yml delete mode 100644 changelogs/unreleased/bw-add-console-message.yml delete mode 100644 changelogs/unreleased/change-font-for-tables-inside-diff-discussions.yml delete mode 100644 changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml delete mode 100644 changelogs/unreleased/dm-webhook-catch-blocked-url-exception.yml delete mode 100644 changelogs/unreleased/docs-use-variables-deploy-policy-for-staging-and-production.yml delete mode 100644 changelogs/unreleased/dz-add-2fa-filter-admin-api.yml delete mode 100644 changelogs/unreleased/feature-add-language-in-repository-to-api.yml delete mode 100644 changelogs/unreleased/feature-add_target_to_tags.yml delete mode 100644 changelogs/unreleased/feature-display-active-sessions.yml delete mode 100644 changelogs/unreleased/feature-runner-per-group.yml delete mode 100644 changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml delete mode 100644 changelogs/unreleased/fix-gb-add-pipeline-builds-foreign-key.yml delete mode 100644 changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml delete mode 100644 changelogs/unreleased/fix-metrics-content-types.yml delete mode 100644 changelogs/unreleased/fix-project-mirror-data-schema.yml delete mode 100644 changelogs/unreleased/fix-shortcut-close-screen-with-key.yml delete mode 100644 changelogs/unreleased/fix-wiki-find-page-invalid-encoding.yml delete mode 100644 changelogs/unreleased/fj-42354-custom-hooks-not-triggered-by-UI-wiki-edit.yml delete mode 100644 changelogs/unreleased/fj-45057-improve-ssrf-documentation.yml delete mode 100644 changelogs/unreleased/fj-change-gollum-gems-to-custom-ones.yml delete mode 100644 changelogs/unreleased/fl-pipelines-details-axios.yml delete mode 100644 changelogs/unreleased/helm-add-alpine-mirrors.yml delete mode 100644 changelogs/unreleased/ide-file-finder.yml delete mode 100644 changelogs/unreleased/ide-improve-commit-panel.yml delete mode 100644 changelogs/unreleased/improve-commit-message-body-rendering.yml delete mode 100644 changelogs/unreleased/improve-jobs-queuing-time-metric.yml delete mode 100644 changelogs/unreleased/improve-quick-actions-summary-preview.yml delete mode 100644 changelogs/unreleased/increase-new-issue-metadata-form-margin.yml delete mode 100644 changelogs/unreleased/inform-the-user-when-there-are-no-project-import-options-available.yml delete mode 100644 changelogs/unreleased/issue_43660.yml delete mode 100644 changelogs/unreleased/jivl-refactor-activity-calendar.yml delete mode 100644 changelogs/unreleased/jprovazn-commit-notes-api.yml delete mode 100644 changelogs/unreleased/jprovazn-generic-error.yml delete mode 100644 changelogs/unreleased/jr-33320-lfs-settings-interface.yml delete mode 100644 changelogs/unreleased/jr-46209-web-ide-copy.yml delete mode 100644 changelogs/unreleased/jramsay-44880-filter-pipelines-by-sha.yml delete mode 100644 changelogs/unreleased/label-links-on-project-transfer.yml delete mode 100644 changelogs/unreleased/live-trace-v2-efficient-destroy-all.yml delete mode 100644 changelogs/unreleased/live-trace-v2.yml delete mode 100644 changelogs/unreleased/move-board-blank-state-vue-component.yml delete mode 100644 changelogs/unreleased/move-estimate-only-pane-vue-component.yml delete mode 100644 changelogs/unreleased/move-help-state-vue-component.yml delete mode 100644 changelogs/unreleased/move-notification-service-calls-to-sidekiq.yml delete mode 100644 changelogs/unreleased/move-pipeline-failed-vue-component.yml delete mode 100644 changelogs/unreleased/move-time-tracking-spent-only-pane-vue-component.yml delete mode 100644 changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml delete mode 100644 changelogs/unreleased/performance-gb-improve-pipeline-creation-service.yml delete mode 100644 changelogs/unreleased/rd-44635-error-500-when-clicking-ghost-user-or-gitlab-support-bot-in-ui.yml delete mode 100644 changelogs/unreleased/rd-45502-uploading-project-export-with-lfs-file-locks-fails.yml delete mode 100644 changelogs/unreleased/refactor-move-mr-widget-ready-to-merge-vue-component.yml delete mode 100644 changelogs/unreleased/refactor-move-mr-widget-wip-vue-component.yml delete mode 100644 changelogs/unreleased/refactor-move-no-tracking-pane-vue-component.yml delete mode 100644 changelogs/unreleased/refactor-move-sidebar-time-tracking-vue-component.yml delete mode 100644 changelogs/unreleased/rename-overview-project-sidenav.yml delete mode 100644 changelogs/unreleased/restore-label-underline-color.yml delete mode 100644 changelogs/unreleased/restore-size-and-position-for-fork-icon.yml delete mode 100644 changelogs/unreleased/revert-discussion-counter-height.yml delete mode 100644 changelogs/unreleased/security-45689-fix-archive-cache-bug.yml delete mode 100644 changelogs/unreleased/security_issue_42029.yml delete mode 100644 changelogs/unreleased/sh-bump-lograge.yml delete mode 100644 changelogs/unreleased/show-group-id-in-group-settings.yml delete mode 100644 changelogs/unreleased/show-runners-description-on-jobs-page.yml delete mode 100644 changelogs/unreleased/tc-repo-verify-mails.yml delete mode 100644 changelogs/unreleased/tz-upgrade-underscore.yml delete mode 100644 changelogs/unreleased/unresolved-discussions-vue-component-i18n-and-tests.yml delete mode 100644 changelogs/unreleased/update-environment-item-action-buttons-icons.yml delete mode 100644 changelogs/unreleased/update-timeline-icon-for-description-edit.yml delete mode 100644 changelogs/unreleased/winh-dashboard-any-milestone.yml delete mode 100644 changelogs/unreleased/winh-dropdown-entry-unlocking.yml delete mode 100644 changelogs/unreleased/winh-new-mergerequest-branch-picker.yml delete mode 100644 changelogs/unreleased/zj-branch-containing-sha-opt-out.yml delete mode 100644 changelogs/unreleased/zj-find-license-opt-out.yml delete mode 100644 changelogs/unreleased/zj-fork-opt-out.yml delete mode 100644 changelogs/unreleased/zj-namespace-service-mandatory.yml delete mode 100644 changelogs/unreleased/zj-ref-exists-opt-out.yml delete mode 100644 changelogs/unreleased/zj-repo-checksum-opt-out.yml delete mode 100644 changelogs/unreleased/zj-repository-exist-mandatory.yml delete mode 100644 changelogs/unreleased/zj-tag-containing-sha-opt-out.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 86f12a75b7a..0c90ab1e8bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,182 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 10.8.0 (2018-05-22) + +### Security (3 changes, 1 of them is from the community) + +- Update faraday_middlewar to 0.12.2. !18397 (Takuya Noguchi) +- Serve archive requests with the correct file in all cases. +- Sanitizes user name to avoid XSS attacks. + +### Fixed (47 changes, 11 of them are from the community) + +- Refactor CSS to eliminate vertical misalignment of login nav. !16275 (Takuya Noguchi) +- Fix pipeline status in branch/tag tree page. !17995 +- Allow group owner to enable runners from subgroups (#41981). !18009 +- Fix template selector menu visibility when toggling preview mode in file edit view. !18118 (Fabian Schneider) +- Fix confirmation modal for deleting a protected branch. !18176 (Paul Bonaud @PaulRbR) +- Triggering custom hooks by Wiki UI edit. !18251 +- Now `rake cache:clear` will also clear pipeline status cache. !18257 +- Fix `joined` information on project members page. !18290 (Fabian Schneider) +- Fix missing namespace for some internal users. !18357 +- Show shared projects on group page. !18390 +- Restore label underline color. !18407 (George Tsiolis) +- Fix undefined `html_escape` method during markdown rendering. !18418 +- Fix unassign slash command preview. !18447 +- Correct text and functionality for delete user / delete user and contributions modal. !18463 (Marc Schwede) +- Fix discussions API setting created_at for notable in a group or notable in a project in a group with owners. !18464 +- Don't include lfs_file_locks data in export bundle. !18495 +- Reset milestone filter when clicking "Any Milestone" in dashboard. !18531 +- Ensure member notifications are sent after the member actual creation/update in the DB. !18538 +- Update links to /ci/lint with ones to project ci/lint. !18539 (Takuya Noguchi) +- Fix tabs container styles to make RSS button clickable. !18559 +- Raise NoRepository error for non-valid repositories when calculating repository checksum. !18594 +- Don't automatically remove artifacts for pages jobs after pages:deploy has run. !18628 +- Increase new issue metadata form margin. !18630 (George Tsiolis) +- Add loading icon padding for pipeline environments. !18631 (George Tsiolis) +- ShaAttribute no longer stops startup if database is missing. !18726 +- Fix close keyboard shortcuts dialog using the keyboard shortcut. !18783 (Lars Greiss) +- Fixes database inconsistencies between Community and Enterprise Edition on import state. !18811 +- Add database foreign key constraint between pipelines and build. !18822 +- Fix finding wiki pages when they have invalidly-encoded content. !18856 +- Fix outdated Web IDE welcome copy. !18861 +- fixed copy to blipboard button in embed bar of snippets. !18923 (haseebeqx) +- Disables RBAC on nginx-ingress. !18947 +- Correct skewed Kubernetes popover illustration. !18949 +- Resolve Import/Export ci_cd_settings error updating the project. !46049 +- Fix project creation for user endpoint when jobs_enabled parameter supplied. +- 46210 Display logo and user dropdown on mobile for terms page and fix styling. +- Adds illustration for when job log was erased. +- Ensure web hook 'blocked URL' errors are stored in web hook logs and properly surfaced to the user. +- Make toggle markdown preview shortcut only toggle selected field. +- Verifiy if pipeline has commit idetails and render information in MR widget when branch is deleted. +- Fixed inconsistent protected branch pill baseline. +- Fix setting Gitlab metrics content types. +- Display only generic message on merge error to avoid exposing any potentially sensitive or user unfriendly backend messages. +- Fix label links update on project transfer. +- Breaks commit not found message in pipelines table. +- Adjust issue boards list header label text color. +- Prevent pipeline actions in dropdown to redirct to a new page. + +### Changed (35 changes, 15 of them are from the community) + +- Improve tooltips in collapsed right sidebar. !17714 +- Partition job_queue_duration_seconds with jobs_running_for_project. !17730 +- For group dashboard, we no longer show groups which the visitor is not a member of (this applies to admins and auditors). !17884 (Roger Rüttimann) +- Use RFC 3676 mail signature delimiters. !17979 (Enrico Scholz) +- Add sha filter to pipelines list API. !18125 +- New CI Job live-trace architecture. !18169 +- Make project deploy keys table more clearly structured. !18279 +- Remove green background from unlock button in admin area. !18288 +- Renamed Overview to Project in the contextual navigation at a project level. !18295 (Constance Okoghenun) +- Load branches on new merge request page asynchronously. !18315 +- Create settings section for autodevops. !18321 +- Add a comma to the time estimate system notes. !18326 +- Enable specifying variables when executing a manual pipeline. !18440 +- Fix size and position for fork icon. !18449 (George Tsiolis) +- Refactored activity calendar. !18469 (Enrico Scholz) +- Small improvements to repository checks. !18484 +- Add 2FA filter to users API for admins only. !18503 +- Align project avatar on small viewports. !18513 (George Tsiolis) +- Show group and project LFS settings in the interface to Owners and Masters. !18562 +- Update environment item action buttons icons. !18632 (George Tsiolis) +- Update timeline icon for description edit. !18633 (George Tsiolis) +- Revert discussion counter height. !18656 (George Tsiolis) +- Improve quick actions summary preview. !18659 (George Tsiolis) +- Change font for tables inside diff discussions. !18660 (George Tsiolis) +- Add padding to profile description. !18663 (George Tsiolis) +- Break issue title for board card title and issuable header text. !18674 (George Tsiolis) +- Adds push mirrors to GitLab Community Edition. !18715 +- Inform the user when there are no project import options available. !18716 (George Tsiolis) +- Improve commit message body rendering and fix responsive compare panels. !18725 (Constance Okoghenun) +- Reconcile project templates with Auto DevOps. !18737 +- Remove branch name from the status bar of WebIDE. +- Clean up WebIDE status bar and add useful info. +- Improve interaction on WebIDE commit panel. +- Keep current labels visible when editing them in the sidebar. +- Use VueJS for rendering pipeline stages. + +### Performance (26 changes, 11 of them are from the community) + +- Move WorkInProgress vue component. !17536 (George Tsiolis) +- Move ReadyToMerge vue component. !17545 (George Tsiolis) +- Move BoardBlankState vue component. !17666 (George Tsiolis) +- Improve DB performance of calculating total artifacts size. !17839 +- Add i18n and update specs for UnresolvedDiscussions vue component. !17866 (George Tsiolis) +- Introduce new ProjectCiCdSetting model with group_runners_enabled. !18144 +- Move PipelineFailed vue component. !18277 (George Tsiolis) +- Move TimeTrackingEstimateOnlyPane vue component. !18318 (George Tsiolis) +- Move TimeTrackingHelpState vue component. !18319 (George Tsiolis) +- Reduce queries on merge requests list page for merge requests from forks. !18561 +- Destroy build_chunks efficiently with FastDestroyAll module. !18575 +- Improve performance of a service responsible for creating a pipeline. !18582 +- Replace time_ago_in_words with JS-based one. !18607 (Takuya Noguchi) +- Move TimeTrackingNoTrackingPane vue component. !18676 (George Tsiolis) +- Move SidebarTimeTracking vue component. !18677 (George Tsiolis) +- Move TimeTrackingSpentOnlyPane vue component. !18710 (George Tsiolis) +- Detecting tags containing a commit uses Gitaly by default. +- Increase cluster applications installer availability using alpine linux mirrors. +- Compute notification recipients in background jobs. +- Use persisted diff data instead fetching Git on discussions. +- Detecting branchnames containing a commit uses Gitaly by default. +- Detect repository license on Gitaly by default. +- Finish NamespaceService migration to Gitaly. +- Check if a ref exists is done by Gitaly by default. +- Compute Gitlab::Git::Repository#checksum on Gitaly by default. +- Repository#exists? is always executed through Gitaly. + +### Added (22 changes, 10 of them are from the community) + +- Allow group masters to configure runners for groups. !9646 (Alexis Reigel) +- Adds Embedded Snippets Support. !15695 (haseebeqx) +- Add Copy metadata quick action. !16473 (Mateusz Bajorski) +- Show Runner's description on job's page. !17321 +- Add deprecation message to dynamic milestone pages. !17505 +- Show new branch/mr button even when branch exists. !17712 (Jacopo Beschi @jacopo-beschi) +- API: add languages of project GET /projects/:id/languages. !17770 (Roger Rüttimann) +- Display active sessions and allow the user to revoke any of it. !17867 (Alexis Reigel) +- Add cron job to email users on issue due date. !17985 (Stuart Nelson) +- Rubocop rule to avoid returning from a block. !18000 (Jacopo Beschi @jacopo-beschi) +- Add the signature verfication badge to the compare view. !18245 (Marc Shaw) +- Expose Deploy Token data as environment varialbes on CI/CD jobs. !18414 +- Show group id in group settings. !18482 (George Tsiolis) +- Allow admins to enforce accepting Terms of Service on an instance. !18570 +- Add CI_COMMIT_MESSAGE, CI_COMMIT_TITLE and CI_COMMIT_DESCRIPTION predefined variables. !18672 +- Add GCP signup offer to cluster index / create pages. !18684 +- Output some useful information when running the rails console. !18697 +- Display merge commit SHA in merge widget after merge. !18722 +- git SHA is now displayed alongside the GitLab version on the Admin Dashboard. +- Expose the target commit ID through the tag API. +- Added fuzzy file finder to web IDE. +- Add discussion API for merge requests and commits. + +### Other (22 changes, 8 of them are from the community) + +- Replace the `project/issues/milestones.feature` spinach test with an rspec analog. !18300 (@blackst0ne) +- Replace the `project/commits/branches.feature` spinach test with an rspec analog. !18302 (@blackst0ne) +- Replacing gollum libraries for gitlab custom libs. !18343 +- Replace the `project/commits/comments.feature` spinach test with an rspec analog. !18356 (@blackst0ne) +- Replace "Click" with "Select" to be more inclusive of people with accessibility requirements. !18386 (Mark Lapierre) +- Remove ahead/behind graphs on project branches on mobile. !18415 (Takuya Noguchi) +- Replace the `project/source/markdown_render.feature` spinach test with an rspec analog. !18525 (@blackst0ne) +- Add missing changelog type to docs. !18526 (@blackst0ne) +- Added Webhook SSRF prevention to documentation. !18532 +- Upgrade underscore.js to 1.9.0. !18578 +- Add documentation about how to use variables to define deploy policies for staging/production environments. !18675 +- Replace the `project/builds/artifacts.feature` spinach test with an rspec analog. !18729 (@blackst0ne) +- Block access to the API & git for users that did not accept enforced Terms of Service. !18816 +- Transition to atomic internal ids for all models. !44259 +- Removes modal boards store and mixins from global scope. +- Replace GKE acronym with Google Kubernetes Engine. +- Replace vue resource with axios for pipelines details page. +- Enable prometheus monitoring by default. +- Replace vue resource with axios in pipelines table. +- Bump lograge to 0.10.0 and remove monkey patch. +- Improves wording in new pipeline page. +- Gitaly handles repository forks by default. + + ## 10.7.4 (2018-05-21) ### Fixed (1 change) diff --git a/changelogs/unreleased/10244-add-project-ci-cd-settings.yml b/changelogs/unreleased/10244-add-project-ci-cd-settings.yml deleted file mode 100644 index 89f9a0fe03c..00000000000 --- a/changelogs/unreleased/10244-add-project-ci-cd-settings.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Introduce new ProjectCiCdSetting model with group_runners_enabled -merge_request: 18144 -author: -type: performance diff --git a/changelogs/unreleased/16957-issue-due-email.yml b/changelogs/unreleased/16957-issue-due-email.yml deleted file mode 100644 index 83944ca4f73..00000000000 --- a/changelogs/unreleased/16957-issue-due-email.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add cron job to email users on issue due date -merge_request: 17985 -author: Stuart Nelson -type: added diff --git a/changelogs/unreleased/21677-run-pipeline-word.yml b/changelogs/unreleased/21677-run-pipeline-word.yml deleted file mode 100644 index 9cc280244e4..00000000000 --- a/changelogs/unreleased/21677-run-pipeline-word.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improves wording in new pipeline page -merge_request: -author: -type: other diff --git a/changelogs/unreleased/25010-collapsed-sidebar-tooltips.yml b/changelogs/unreleased/25010-collapsed-sidebar-tooltips.yml deleted file mode 100644 index 1226fb4eefd..00000000000 --- a/changelogs/unreleased/25010-collapsed-sidebar-tooltips.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve tooltips in collapsed right sidebar -merge_request: 17714 -author: -type: changed diff --git a/changelogs/unreleased/30739-fix-joined-information-on-project-members-page.yml b/changelogs/unreleased/30739-fix-joined-information-on-project-members-page.yml deleted file mode 100644 index f2d5b503661..00000000000 --- a/changelogs/unreleased/30739-fix-joined-information-on-project-members-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix `joined` information on project members page -merge_request: 18290 -author: Fabian Schneider -type: fixed diff --git a/changelogs/unreleased/32617-fix-template-selector-menu-visibility.yml b/changelogs/unreleased/32617-fix-template-selector-menu-visibility.yml deleted file mode 100644 index c73be5a901e..00000000000 --- a/changelogs/unreleased/32617-fix-template-selector-menu-visibility.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix template selector menu visibility when toggling preview mode in file edit - view -merge_request: 18118 -author: Fabian Schneider -type: fixed diff --git a/changelogs/unreleased/33697-pipelines-json-endpoint.yml b/changelogs/unreleased/33697-pipelines-json-endpoint.yml deleted file mode 100644 index d44e2729415..00000000000 --- a/changelogs/unreleased/33697-pipelines-json-endpoint.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use VueJS for rendering pipeline stages -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/33697-remove-ujs-action-big-graph.yml b/changelogs/unreleased/33697-remove-ujs-action-big-graph.yml deleted file mode 100644 index 43ce52c1209..00000000000 --- a/changelogs/unreleased/33697-remove-ujs-action-big-graph.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Prevent pipeline actions in dropdown to redirct to a new page -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/34262-show-current-labels-when-editing.yml b/changelogs/unreleased/34262-show-current-labels-when-editing.yml deleted file mode 100644 index d3b15b9ddd1..00000000000 --- a/changelogs/unreleased/34262-show-current-labels-when-editing.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Keep current labels visible when editing them in the sidebar -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/36762-reconcile-project-templates-with-auto-devops.yml b/changelogs/unreleased/36762-reconcile-project-templates-with-auto-devops.yml deleted file mode 100644 index 8169b18f875..00000000000 --- a/changelogs/unreleased/36762-reconcile-project-templates-with-auto-devops.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reconcile project templates with Auto DevOps -merge_request: 18737 -author: -type: changed diff --git a/changelogs/unreleased/36983-osw-heading-labels-color-fix.yml b/changelogs/unreleased/36983-osw-heading-labels-color-fix.yml deleted file mode 100644 index 082e0544dea..00000000000 --- a/changelogs/unreleased/36983-osw-heading-labels-color-fix.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adjust issue boards list header label text color -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/40402-time-estimate-system-notes-can-be-confusing.yml b/changelogs/unreleased/40402-time-estimate-system-notes-can-be-confusing.yml deleted file mode 100644 index e47577f9058..00000000000 --- a/changelogs/unreleased/40402-time-estimate-system-notes-can-be-confusing.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add a comma to the time estimate system notes -merge_request: 18326 -author: -type: changed diff --git a/changelogs/unreleased/40487-axios-pipelines.yml b/changelogs/unreleased/40487-axios-pipelines.yml deleted file mode 100644 index 437d5e87e1a..00000000000 --- a/changelogs/unreleased/40487-axios-pipelines.yml +++ /dev/null @@ -1,4 +0,0 @@ -title: Replace vue resource with axios in pipelines table -merge_request: -author: -type: other \ No newline at end of file diff --git a/changelogs/unreleased/41059-calculate-artifact-size-more-efficiently.yml b/changelogs/unreleased/41059-calculate-artifact-size-more-efficiently.yml deleted file mode 100644 index e3f94bbf081..00000000000 --- a/changelogs/unreleased/41059-calculate-artifact-size-more-efficiently.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve DB performance of calculating total artifacts size -merge_request: 17839 -author: -type: performance diff --git a/changelogs/unreleased/41082-make-deploykeys-table-more-clearly-structured.yml b/changelogs/unreleased/41082-make-deploykeys-table-more-clearly-structured.yml deleted file mode 100644 index 23704c2b37b..00000000000 --- a/changelogs/unreleased/41082-make-deploykeys-table-more-clearly-structured.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make project deploy keys table more clearly structured -merge_request: 18279 -author: -type: changed diff --git a/changelogs/unreleased/41748-vertical-misalignment-login-box.yml b/changelogs/unreleased/41748-vertical-misalignment-login-box.yml deleted file mode 100644 index 77a97400323..00000000000 --- a/changelogs/unreleased/41748-vertical-misalignment-login-box.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Refactor CSS to eliminate vertical misalignment of login nav -merge_request: 16275 -author: Takuya Noguchi -type: fixed diff --git a/changelogs/unreleased/41981-allow-group-owner-to-enable-runners-from-subgroups.yml b/changelogs/unreleased/41981-allow-group-owner-to-enable-runners-from-subgroups.yml deleted file mode 100644 index 30481e7af84..00000000000 --- a/changelogs/unreleased/41981-allow-group-owner-to-enable-runners-from-subgroups.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Allow group owner to enable runners from subgroups (#41981)' -merge_request: 18009 -author: -type: fixed diff --git a/changelogs/unreleased/42099-port-push-mirroring-to-ce-ce-port-v-2.yml b/changelogs/unreleased/42099-port-push-mirroring-to-ce-ce-port-v-2.yml deleted file mode 100644 index f23521ea416..00000000000 --- a/changelogs/unreleased/42099-port-push-mirroring-to-ce-ce-port-v-2.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds push mirrors to GitLab Community Edition -merge_request: 18715 -author: -type: changed diff --git a/changelogs/unreleased/42543-hide-divergence-graph-on-branches-for-mobile.yml b/changelogs/unreleased/42543-hide-divergence-graph-on-branches-for-mobile.yml deleted file mode 100644 index 7452a264bfd..00000000000 --- a/changelogs/unreleased/42543-hide-divergence-graph-on-branches-for-mobile.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove ahead/behind graphs on project branches on mobile -merge_request: 18415 -author: Takuya Noguchi -type: other diff --git a/changelogs/unreleased/42803-show-new-branch-mr-button.yml b/changelogs/unreleased/42803-show-new-branch-mr-button.yml deleted file mode 100644 index d689ff7f001..00000000000 --- a/changelogs/unreleased/42803-show-new-branch-mr-button.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Show new branch/mr button even when branch exists -merge_request: 17712 -author: Jacopo Beschi @jacopo-beschi -type: added diff --git a/changelogs/unreleased/42889-avoid-return-inside-block.yml b/changelogs/unreleased/42889-avoid-return-inside-block.yml deleted file mode 100644 index e3e1341ddcc..00000000000 --- a/changelogs/unreleased/42889-avoid-return-inside-block.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Rubocop rule to avoid returning from a block -merge_request: 18000 -author: Jacopo Beschi @jacopo-beschi -type: added diff --git a/changelogs/unreleased/42936-improve-ns-factory-avoid-duplicates.yml b/changelogs/unreleased/42936-improve-ns-factory-avoid-duplicates.yml deleted file mode 100644 index 54b523b65a1..00000000000 --- a/changelogs/unreleased/42936-improve-ns-factory-avoid-duplicates.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fix discussions API setting created_at for notable in a group or notable in - a project in a group with owners -merge_request: 18464 -author: -type: fixed diff --git a/changelogs/unreleased/43111-controller-projects-mergerequestscontroller-index-executes-more-than-100-sql-queries.yml b/changelogs/unreleased/43111-controller-projects-mergerequestscontroller-index-executes-more-than-100-sql-queries.yml deleted file mode 100644 index 120e319acfb..00000000000 --- a/changelogs/unreleased/43111-controller-projects-mergerequestscontroller-index-executes-more-than-100-sql-queries.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reduce queries on merge requests list page for merge requests from forks -merge_request: 18561 -author: -type: performance diff --git a/changelogs/unreleased/43404-pipelines-commit.yml b/changelogs/unreleased/43404-pipelines-commit.yml deleted file mode 100644 index 0b9a4a6451f..00000000000 --- a/changelogs/unreleased/43404-pipelines-commit.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Breaks commit not found message in pipelines table -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/43466-make-auto-devops-settings-first-class.yml b/changelogs/unreleased/43466-make-auto-devops-settings-first-class.yml deleted file mode 100644 index f5c5415159c..00000000000 --- a/changelogs/unreleased/43466-make-auto-devops-settings-first-class.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Create settings section for autodevops -merge_request: 18321 -author: -type: changed diff --git a/changelogs/unreleased/43469-gcp-account-offer.yml b/changelogs/unreleased/43469-gcp-account-offer.yml deleted file mode 100644 index 323a4b81731..00000000000 --- a/changelogs/unreleased/43469-gcp-account-offer.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add GCP signup offer to cluster index / create pages -merge_request: 18684 -author: -type: added diff --git a/changelogs/unreleased/43557-osw-present-merge-sha-commit.yml b/changelogs/unreleased/43557-osw-present-merge-sha-commit.yml deleted file mode 100644 index a7128f7481e..00000000000 --- a/changelogs/unreleased/43557-osw-present-merge-sha-commit.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Display merge commit SHA in merge widget after merge -merge_request: 18722 -author: -type: added diff --git a/changelogs/unreleased/43567-replace-gke.yml b/changelogs/unreleased/43567-replace-gke.yml deleted file mode 100644 index 8ec79fc3d4d..00000000000 --- a/changelogs/unreleased/43567-replace-gke.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace GKE acronym with Google Kubernetes Engine -merge_request: -author: -type: other diff --git a/changelogs/unreleased/43617-mailsig.yml b/changelogs/unreleased/43617-mailsig.yml deleted file mode 100644 index 2c7568e32ca..00000000000 --- a/changelogs/unreleased/43617-mailsig.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use RFC 3676 mail signature delimiters -merge_request: 17979 -author: Enrico Scholz -type: changed diff --git a/changelogs/unreleased/44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui.yml b/changelogs/unreleased/44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui.yml deleted file mode 100644 index 8854eeb5fba..00000000000 --- a/changelogs/unreleased/44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enable specifying variables when executing a manual pipeline -merge_request: 18440 -author: -type: changed diff --git a/changelogs/unreleased/44224-remove-gl.yml b/changelogs/unreleased/44224-remove-gl.yml deleted file mode 100644 index 1c792883f09..00000000000 --- a/changelogs/unreleased/44224-remove-gl.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Removes modal boards store and mixins from global scope -merge_request: -author: -type: other diff --git a/changelogs/unreleased/44296-commit-path.yml b/changelogs/unreleased/44296-commit-path.yml deleted file mode 100644 index b658178f8dc..00000000000 --- a/changelogs/unreleased/44296-commit-path.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Verifiy if pipeline has commit idetails and render information in MR widget - when branch is deleted -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/44447-expose-deploy-token-to-ci-cd.yml b/changelogs/unreleased/44447-expose-deploy-token-to-ci-cd.yml deleted file mode 100644 index d01b797b1ff..00000000000 --- a/changelogs/unreleased/44447-expose-deploy-token-to-ci-cd.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Expose Deploy Token data as environment varialbes on CI/CD jobs -merge_request: 18414 -author: -type: added diff --git a/changelogs/unreleased/44541-fix-file-tree-commit-status-cache.yml b/changelogs/unreleased/44541-fix-file-tree-commit-status-cache.yml deleted file mode 100644 index ff734fe0c05..00000000000 --- a/changelogs/unreleased/44541-fix-file-tree-commit-status-cache.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix pipeline status in branch/tag tree page -merge_request: 17995 -author: -type: fixed diff --git a/changelogs/unreleased/44582-clear-pipeline-status-cache.yml b/changelogs/unreleased/44582-clear-pipeline-status-cache.yml deleted file mode 100644 index 1777f2ffaab..00000000000 --- a/changelogs/unreleased/44582-clear-pipeline-status-cache.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Now `rake cache:clear` will also clear pipeline status cache -merge_request: 18257 -author: -type: fixed diff --git a/changelogs/unreleased/44697-prevue.yml b/changelogs/unreleased/44697-prevue.yml deleted file mode 100644 index 9fdce5869ae..00000000000 --- a/changelogs/unreleased/44697-prevue.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Make toggle markdown preview shortcut only toggle selected field -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/44833-ide-clean-up-status-bar.yml b/changelogs/unreleased/44833-ide-clean-up-status-bar.yml deleted file mode 100644 index 4c827e57195..00000000000 --- a/changelogs/unreleased/44833-ide-clean-up-status-bar.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Clean up WebIDE status bar and add useful info -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/44834-ide-remove-branch-from-bottom-bar.yml b/changelogs/unreleased/44834-ide-remove-branch-from-bottom-bar.yml deleted file mode 100644 index d3f838ad362..00000000000 --- a/changelogs/unreleased/44834-ide-remove-branch-from-bottom-bar.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove branch name from the status bar of WebIDE -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/44879.yml b/changelogs/unreleased/44879.yml deleted file mode 100644 index b51e057bb7b..00000000000 --- a/changelogs/unreleased/44879.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add the signature verfication badge to the compare view -merge_request: 18245 -author: Marc Shaw -type: added diff --git a/changelogs/unreleased/44985-fix-protected-branch-delete-modal.yml b/changelogs/unreleased/44985-fix-protected-branch-delete-modal.yml deleted file mode 100644 index 4af2af2a561..00000000000 --- a/changelogs/unreleased/44985-fix-protected-branch-delete-modal.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix confirmation modal for deleting a protected branch -merge_request: 18176 -author: Paul Bonaud @PaulRbR -type: fixed diff --git a/changelogs/unreleased/45159-fix-illustration.yml b/changelogs/unreleased/45159-fix-illustration.yml deleted file mode 100644 index 3b9cb45b916..00000000000 --- a/changelogs/unreleased/45159-fix-illustration.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds illustration for when job log was erased -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/45397-update-faraday_middleware-to-0-12-2.yml b/changelogs/unreleased/45397-update-faraday_middleware-to-0-12-2.yml deleted file mode 100644 index 3370ec3feba..00000000000 --- a/changelogs/unreleased/45397-update-faraday_middleware-to-0-12-2.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update faraday_middlewar to 0.12.2 -merge_request: 18397 -author: Takuya Noguchi -type: security diff --git a/changelogs/unreleased/45398-fix-rss-button.yml b/changelogs/unreleased/45398-fix-rss-button.yml deleted file mode 100644 index 2c8ee6f0564..00000000000 --- a/changelogs/unreleased/45398-fix-rss-button.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix tabs container styles to make RSS button clickable -merge_request: 18559 -author: -type: fixed diff --git a/changelogs/unreleased/45436-markdown-is-not-rendering-error-loading-viewer-undefined-method-html_escape.yml b/changelogs/unreleased/45436-markdown-is-not-rendering-error-loading-viewer-undefined-method-html_escape.yml deleted file mode 100644 index 0f1d111ca58..00000000000 --- a/changelogs/unreleased/45436-markdown-is-not-rendering-error-loading-viewer-undefined-method-html_escape.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix undefined `html_escape` method during markdown rendering -merge_request: 18418 -author: -type: fixed diff --git a/changelogs/unreleased/45451-user-deletion-modal-with-same-info-for-delete-user-or-delete-user-and-contributions.yml b/changelogs/unreleased/45451-user-deletion-modal-with-same-info-for-delete-user-or-delete-user-and-contributions.yml deleted file mode 100644 index 707a18745c8..00000000000 --- a/changelogs/unreleased/45451-user-deletion-modal-with-same-info-for-delete-user-or-delete-user-and-contributions.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Correct text and functionality for delete user / delete user and contributions - modal. -merge_request: 18463 -author: Marc Schwede -type: fixed diff --git a/changelogs/unreleased/45481-sane-pages-artifacts.yml b/changelogs/unreleased/45481-sane-pages-artifacts.yml deleted file mode 100644 index b9c68b70012..00000000000 --- a/changelogs/unreleased/45481-sane-pages-artifacts.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Don't automatically remove artifacts for pages jobs after pages:deploy has - run -merge_request: 18628 -author: -type: fixed diff --git a/changelogs/unreleased/45572-members-invitations-scheduled-before-commit.yml b/changelogs/unreleased/45572-members-invitations-scheduled-before-commit.yml deleted file mode 100644 index 7cdea436d47..00000000000 --- a/changelogs/unreleased/45572-members-invitations-scheduled-before-commit.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Ensure member notifications are sent after the member actual creation/update in the DB -merge_request: 18538 -author: -type: fixed diff --git a/changelogs/unreleased/45576-fix-create-project-for-user-endpoint.yml b/changelogs/unreleased/45576-fix-create-project-for-user-endpoint.yml deleted file mode 100644 index 12631c75b44..00000000000 --- a/changelogs/unreleased/45576-fix-create-project-for-user-endpoint.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix project creation for user endpoint when jobs_enabled parameter supplied -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/45666-project-ci-lint-links.yml b/changelogs/unreleased/45666-project-ci-lint-links.yml deleted file mode 100644 index dbf803c0921..00000000000 --- a/changelogs/unreleased/45666-project-ci-lint-links.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update links to /ci/lint with ones to project ci/lint -merge_request: 18539 -author: Takuya Noguchi -type: fixed diff --git a/changelogs/unreleased/45761-replace-actionview-time_ago_in_words.yml b/changelogs/unreleased/45761-replace-actionview-time_ago_in_words.yml deleted file mode 100644 index adf4db90407..00000000000 --- a/changelogs/unreleased/45761-replace-actionview-time_ago_in_words.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace time_ago_in_words with JS-based one -merge_request: 18607 -author: Takuya Noguchi -type: performance diff --git a/changelogs/unreleased/46049-import-export-import-is-broken-due-to-the-addition-of-a-ci-table.yml b/changelogs/unreleased/46049-import-export-import-is-broken-due-to-the-addition-of-a-ci-table.yml deleted file mode 100644 index 77e4bb50082..00000000000 --- a/changelogs/unreleased/46049-import-export-import-is-broken-due-to-the-addition-of-a-ci-table.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Resolve Import/Export ci_cd_settings error updating the project -merge_request: 46049 -author: -type: fixed diff --git a/changelogs/unreleased/46210-terms-acceptance-dropdown-menu.yml b/changelogs/unreleased/46210-terms-acceptance-dropdown-menu.yml deleted file mode 100644 index 8a7c549e356..00000000000 --- a/changelogs/unreleased/46210-terms-acceptance-dropdown-menu.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 46210 Display logo and user dropdown on mobile for terms page and fix styling -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/46286-fix-ingress-rbac-default-value.yml b/changelogs/unreleased/46286-fix-ingress-rbac-default-value.yml deleted file mode 100644 index e9cd8977394..00000000000 --- a/changelogs/unreleased/46286-fix-ingress-rbac-default-value.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Disables RBAC on nginx-ingress -merge_request: 18947 -author: -type: fixed diff --git a/changelogs/unreleased/46303_copy_button_fix_embedded_snippets.yml b/changelogs/unreleased/46303_copy_button_fix_embedded_snippets.yml deleted file mode 100644 index c8cdf3672b3..00000000000 --- a/changelogs/unreleased/46303_copy_button_fix_embedded_snippets.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: fixed copy to blipboard button in embed bar of snippets -merge_request: 18923 -author: haseebeqx -type: fixed diff --git a/changelogs/unreleased/46345-kubernetes-popover-illustration-skewed.yml b/changelogs/unreleased/46345-kubernetes-popover-illustration-skewed.yml deleted file mode 100644 index a0e6b39fef6..00000000000 --- a/changelogs/unreleased/46345-kubernetes-popover-illustration-skewed.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Correct skewed Kubernetes popover illustration -merge_request: 18949 -author: -type: fixed diff --git a/changelogs/unreleased/4950-unassign-slash-command-preview-fix.yml b/changelogs/unreleased/4950-unassign-slash-command-preview-fix.yml deleted file mode 100644 index 0b8c14ae699..00000000000 --- a/changelogs/unreleased/4950-unassign-slash-command-preview-fix.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix unassign slash command preview -merge_request: 18447 -author: -type: fixed diff --git a/changelogs/unreleased/5750-backport-checksum-git-commanderror-exit-status-128.yml b/changelogs/unreleased/5750-backport-checksum-git-commanderror-exit-status-128.yml deleted file mode 100644 index d778b44c110..00000000000 --- a/changelogs/unreleased/5750-backport-checksum-git-commanderror-exit-status-128.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Raise NoRepository error for non-valid repositories when calculating repository - checksum -merge_request: 18594 -author: -type: fixed diff --git a/changelogs/unreleased/5794-we-should-failover-gracefully-when-we-can-t-connect-to-geo-tracking-database-ce.yml b/changelogs/unreleased/5794-we-should-failover-gracefully-when-we-can-t-connect-to-geo-tracking-database-ce.yml deleted file mode 100644 index 4db0ff4f3a0..00000000000 --- a/changelogs/unreleased/5794-we-should-failover-gracefully-when-we-can-t-connect-to-geo-tracking-database-ce.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: ShaAttribute no longer stops startup if database is missing -merge_request: 18726 -author: -type: fixed diff --git a/changelogs/unreleased/8088_embedded_snippets_support.yml b/changelogs/unreleased/8088_embedded_snippets_support.yml deleted file mode 100644 index 7bd77a69dbd..00000000000 --- a/changelogs/unreleased/8088_embedded_snippets_support.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Adds Embedded Snippets Support -merge_request: 15695 -author: haseebeqx -type: added diff --git a/changelogs/unreleased/ab-44259-atomic-internal-ids-for-all-models.yml b/changelogs/unreleased/ab-44259-atomic-internal-ids-for-all-models.yml deleted file mode 100644 index e154f7dbc85..00000000000 --- a/changelogs/unreleased/ab-44259-atomic-internal-ids-for-all-models.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Transition to atomic internal ids for all models. -merge_request: 44259 -author: -type: other diff --git a/changelogs/unreleased/accessible-text.yml b/changelogs/unreleased/accessible-text.yml deleted file mode 100755 index d39d5a9eb2c..00000000000 --- a/changelogs/unreleased/accessible-text.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Replace "Click" with "Select" to be more inclusive of people with accessibility - requirements -merge_request: 18386 -author: Mark Lapierre -type: other diff --git a/changelogs/unreleased/add-copy-metadata-command.yml b/changelogs/unreleased/add-copy-metadata-command.yml deleted file mode 100644 index 3bf25ae6ce0..00000000000 --- a/changelogs/unreleased/add-copy-metadata-command.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add Copy metadata quick action -merge_request: 16473 -author: Mateusz Bajorski -type: added diff --git a/changelogs/unreleased/add-git-commit-message-predefined-variable.yml b/changelogs/unreleased/add-git-commit-message-predefined-variable.yml deleted file mode 100644 index 183fe69936e..00000000000 --- a/changelogs/unreleased/add-git-commit-message-predefined-variable.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add CI_COMMIT_MESSAGE, CI_COMMIT_TITLE and CI_COMMIT_DESCRIPTION predefined variables -merge_request: 18672 -author: -type: added diff --git a/changelogs/unreleased/add-loading-icon-padding-for-pipeline-environments.yml b/changelogs/unreleased/add-loading-icon-padding-for-pipeline-environments.yml deleted file mode 100644 index a6304418517..00000000000 --- a/changelogs/unreleased/add-loading-icon-padding-for-pipeline-environments.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add loading icon padding for pipeline environments -merge_request: 18631 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/add-padding-to-profile-description.yml b/changelogs/unreleased/add-padding-to-profile-description.yml deleted file mode 100644 index 4628a10eb3f..00000000000 --- a/changelogs/unreleased/add-padding-to-profile-description.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add padding to profile description -merge_request: 18663 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/align-project-avatar-on-small-viewports.yml b/changelogs/unreleased/align-project-avatar-on-small-viewports.yml deleted file mode 100644 index 320e7fbc294..00000000000 --- a/changelogs/unreleased/align-project-avatar-on-small-viewports.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Align project avatar on small viewports -merge_request: 18513 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/ash-mckenzie-include-sha-with-version.yml b/changelogs/unreleased/ash-mckenzie-include-sha-with-version.yml deleted file mode 100644 index b49c48e0fe1..00000000000 --- a/changelogs/unreleased/ash-mckenzie-include-sha-with-version.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: git SHA is now displayed alongside the GitLab version on the Admin Dashboard -merge_request: -author: -type: added diff --git a/changelogs/unreleased/blackst0ne-add-missing-changelog-type-to-docs.yml b/changelogs/unreleased/blackst0ne-add-missing-changelog-type-to-docs.yml deleted file mode 100644 index f8790fa45aa..00000000000 --- a/changelogs/unreleased/blackst0ne-add-missing-changelog-type-to-docs.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add missing changelog type to docs -merge_request: 18526 -author: "@blackst0ne" -type: other diff --git a/changelogs/unreleased/blackst0ne-replace-spinach-project-builds-artifacts-feature.yml b/changelogs/unreleased/blackst0ne-replace-spinach-project-builds-artifacts-feature.yml deleted file mode 100644 index 98c56cf2b57..00000000000 --- a/changelogs/unreleased/blackst0ne-replace-spinach-project-builds-artifacts-feature.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'Replace the `project/builds/artifacts.feature` spinach test with an rspec analog' -merge_request: 18729 -author: '@blackst0ne' -type: other diff --git a/changelogs/unreleased/blackst0ne-replace-spinach-project-commits-branches-feature.yml b/changelogs/unreleased/blackst0ne-replace-spinach-project-commits-branches-feature.yml deleted file mode 100644 index bcfba4ae70d..00000000000 --- a/changelogs/unreleased/blackst0ne-replace-spinach-project-commits-branches-feature.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "Replace the `project/commits/branches.feature` spinach test with an rspec analog" -merge_request: 18302 -author: "@blackst0ne" -type: other diff --git a/changelogs/unreleased/blackst0ne-replace-spinach-project-commits-comments-feature.yml b/changelogs/unreleased/blackst0ne-replace-spinach-project-commits-comments-feature.yml deleted file mode 100644 index e7077f27555..00000000000 --- a/changelogs/unreleased/blackst0ne-replace-spinach-project-commits-comments-feature.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace the `project/commits/comments.feature` spinach test with an rspec analog -merge_request: 18356 -author: "@blackst0ne" -type: other diff --git a/changelogs/unreleased/blackst0ne-replace-spinach-project-issues-milestones-feature.yml b/changelogs/unreleased/blackst0ne-replace-spinach-project-issues-milestones-feature.yml deleted file mode 100644 index 0dcac0a80eb..00000000000 --- a/changelogs/unreleased/blackst0ne-replace-spinach-project-issues-milestones-feature.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace the `project/issues/milestones.feature` spinach test with an rspec analog -merge_request: 18300 -author: "@blackst0ne" -type: other diff --git a/changelogs/unreleased/blackst0ne-replace-spinach-project-source-markdown-render-feature.yml b/changelogs/unreleased/blackst0ne-replace-spinach-project-source-markdown-render-feature.yml deleted file mode 100644 index 657ed782880..00000000000 --- a/changelogs/unreleased/blackst0ne-replace-spinach-project-source-markdown-render-feature.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace the `project/source/markdown_render.feature` spinach test with an rspec analog -merge_request: 18525 -author: "@blackst0ne" -type: other diff --git a/changelogs/unreleased/break-issue-title-for-board-card-title-and-issueable-header-text.yml b/changelogs/unreleased/break-issue-title-for-board-card-title-and-issueable-header-text.yml deleted file mode 100644 index 7acde223962..00000000000 --- a/changelogs/unreleased/break-issue-title-for-board-card-title-and-issueable-header-text.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Break issue title for board card title and issuable header text -merge_request: 18674 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/bvl-enforce-terms.yml b/changelogs/unreleased/bvl-enforce-terms.yml deleted file mode 100644 index 1bb1ecdf623..00000000000 --- a/changelogs/unreleased/bvl-enforce-terms.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow admins to enforce accepting Terms of Service on an instance -merge_request: 18570 -author: -type: added diff --git a/changelogs/unreleased/bvl-restrict-api-git-for-terms.yml b/changelogs/unreleased/bvl-restrict-api-git-for-terms.yml deleted file mode 100644 index 49cd04b065b..00000000000 --- a/changelogs/unreleased/bvl-restrict-api-git-for-terms.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Block access to the API & git for users that did not accept enforced Terms - of Service -merge_request: 18816 -author: -type: other diff --git a/changelogs/unreleased/bvl-shared-groups-on-group-page.yml b/changelogs/unreleased/bvl-shared-groups-on-group-page.yml deleted file mode 100644 index 6c0703fd138..00000000000 --- a/changelogs/unreleased/bvl-shared-groups-on-group-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Show shared projects on group page -merge_request: 18390 -author: -type: fixed diff --git a/changelogs/unreleased/bw-add-console-message.yml b/changelogs/unreleased/bw-add-console-message.yml deleted file mode 100644 index 7994f7caced..00000000000 --- a/changelogs/unreleased/bw-add-console-message.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Output some useful information when running the rails console -merge_request: 18697 -author: -type: added diff --git a/changelogs/unreleased/change-font-for-tables-inside-diff-discussions.yml b/changelogs/unreleased/change-font-for-tables-inside-diff-discussions.yml deleted file mode 100644 index f2810fab208..00000000000 --- a/changelogs/unreleased/change-font-for-tables-inside-diff-discussions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Change font for tables inside diff discussions -merge_request: 18660 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml b/changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml deleted file mode 100644 index 3e1ac7b795d..00000000000 --- a/changelogs/unreleased/deprecation-warning-for-dynamic-milestones.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add deprecation message to dynamic milestone pages -merge_request: 17505 -author: -type: added diff --git a/changelogs/unreleased/dm-webhook-catch-blocked-url-exception.yml b/changelogs/unreleased/dm-webhook-catch-blocked-url-exception.yml deleted file mode 100644 index c4f8f7acca6..00000000000 --- a/changelogs/unreleased/dm-webhook-catch-blocked-url-exception.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Ensure web hook 'blocked URL' errors are stored in web hook logs and properly - surfaced to the user -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/docs-use-variables-deploy-policy-for-staging-and-production.yml b/changelogs/unreleased/docs-use-variables-deploy-policy-for-staging-and-production.yml deleted file mode 100644 index aa23a89a175..00000000000 --- a/changelogs/unreleased/docs-use-variables-deploy-policy-for-staging-and-production.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Add documentation about how to use variables to define deploy policies for - staging/production environments -merge_request: 18675 -author: -type: other diff --git a/changelogs/unreleased/dz-add-2fa-filter-admin-api.yml b/changelogs/unreleased/dz-add-2fa-filter-admin-api.yml deleted file mode 100644 index df479e69380..00000000000 --- a/changelogs/unreleased/dz-add-2fa-filter-admin-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add 2FA filter to users API for admins only -merge_request: 18503 -author: -type: changed diff --git a/changelogs/unreleased/feature-add-language-in-repository-to-api.yml b/changelogs/unreleased/feature-add-language-in-repository-to-api.yml deleted file mode 100644 index bd9bd377212..00000000000 --- a/changelogs/unreleased/feature-add-language-in-repository-to-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: 'API: add languages of project GET /projects/:id/languages' -merge_request: 17770 -author: Roger Rüttimann -type: added diff --git a/changelogs/unreleased/feature-add_target_to_tags.yml b/changelogs/unreleased/feature-add_target_to_tags.yml deleted file mode 100644 index 75816005e1f..00000000000 --- a/changelogs/unreleased/feature-add_target_to_tags.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Expose the target commit ID through the tag API -merge_request: -author: -type: added diff --git a/changelogs/unreleased/feature-display-active-sessions.yml b/changelogs/unreleased/feature-display-active-sessions.yml deleted file mode 100644 index 14cfa66953e..00000000000 --- a/changelogs/unreleased/feature-display-active-sessions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Display active sessions and allow the user to revoke any of it -merge_request: 17867 -author: Alexis Reigel -type: added diff --git a/changelogs/unreleased/feature-runner-per-group.yml b/changelogs/unreleased/feature-runner-per-group.yml deleted file mode 100644 index 162a5fae0a4..00000000000 --- a/changelogs/unreleased/feature-runner-per-group.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Allow group masters to configure runners for groups -merge_request: 9646 -author: Alexis Reigel -type: added diff --git a/changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml b/changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml deleted file mode 100644 index 6e2273ed9af..00000000000 --- a/changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: For group dashboard, we no longer show groups which the visitor is not a member of (this applies to admins and auditors) -merge_request: 17884 -author: Roger Rüttimann -type: changed diff --git a/changelogs/unreleased/fix-gb-add-pipeline-builds-foreign-key.yml b/changelogs/unreleased/fix-gb-add-pipeline-builds-foreign-key.yml deleted file mode 100644 index bded7bb7cc4..00000000000 --- a/changelogs/unreleased/fix-gb-add-pipeline-builds-foreign-key.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add database foreign key constraint between pipelines and build -merge_request: 18822 -author: -type: fixed diff --git a/changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml b/changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml deleted file mode 100644 index fb6dffaf226..00000000000 --- a/changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fixed inconsistent protected branch pill baseline -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fix-metrics-content-types.yml b/changelogs/unreleased/fix-metrics-content-types.yml deleted file mode 100644 index a418dccffc3..00000000000 --- a/changelogs/unreleased/fix-metrics-content-types.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix setting Gitlab metrics content types -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/fix-project-mirror-data-schema.yml b/changelogs/unreleased/fix-project-mirror-data-schema.yml deleted file mode 100644 index 107f1fe3b9c..00000000000 --- a/changelogs/unreleased/fix-project-mirror-data-schema.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Fixes database inconsistencies between Community and Enterprise Edition on - import state -merge_request: 18811 -author: -type: fixed diff --git a/changelogs/unreleased/fix-shortcut-close-screen-with-key.yml b/changelogs/unreleased/fix-shortcut-close-screen-with-key.yml deleted file mode 100644 index 9cbc856a075..00000000000 --- a/changelogs/unreleased/fix-shortcut-close-screen-with-key.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix close keyboard shortcuts dialog using the keyboard shortcut -merge_request: 18783 -author: Lars Greiss -type: fixed diff --git a/changelogs/unreleased/fix-wiki-find-page-invalid-encoding.yml b/changelogs/unreleased/fix-wiki-find-page-invalid-encoding.yml deleted file mode 100644 index f003bef8671..00000000000 --- a/changelogs/unreleased/fix-wiki-find-page-invalid-encoding.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix finding wiki pages when they have invalidly-encoded content -merge_request: 18856 -author: -type: fixed diff --git a/changelogs/unreleased/fj-42354-custom-hooks-not-triggered-by-UI-wiki-edit.yml b/changelogs/unreleased/fj-42354-custom-hooks-not-triggered-by-UI-wiki-edit.yml deleted file mode 100644 index 9fe458aba4a..00000000000 --- a/changelogs/unreleased/fj-42354-custom-hooks-not-triggered-by-UI-wiki-edit.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Triggering custom hooks by Wiki UI edit -merge_request: 18251 -author: -type: fixed diff --git a/changelogs/unreleased/fj-45057-improve-ssrf-documentation.yml b/changelogs/unreleased/fj-45057-improve-ssrf-documentation.yml deleted file mode 100644 index b923f442b26..00000000000 --- a/changelogs/unreleased/fj-45057-improve-ssrf-documentation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Added Webhook SSRF prevention to documentation -merge_request: 18532 -author: -type: other diff --git a/changelogs/unreleased/fj-change-gollum-gems-to-custom-ones.yml b/changelogs/unreleased/fj-change-gollum-gems-to-custom-ones.yml deleted file mode 100644 index 53883e8d907..00000000000 --- a/changelogs/unreleased/fj-change-gollum-gems-to-custom-ones.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replacing gollum libraries for gitlab custom libs -merge_request: 18343 -author: -type: other diff --git a/changelogs/unreleased/fl-pipelines-details-axios.yml b/changelogs/unreleased/fl-pipelines-details-axios.yml deleted file mode 100644 index 0b72e54cba3..00000000000 --- a/changelogs/unreleased/fl-pipelines-details-axios.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Replace vue resource with axios for pipelines details page -merge_request: -author: -type: other diff --git a/changelogs/unreleased/helm-add-alpine-mirrors.yml b/changelogs/unreleased/helm-add-alpine-mirrors.yml deleted file mode 100644 index 656c4f911d0..00000000000 --- a/changelogs/unreleased/helm-add-alpine-mirrors.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Increase cluster applications installer availability using alpine linux mirrors -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/ide-file-finder.yml b/changelogs/unreleased/ide-file-finder.yml deleted file mode 100644 index 252dd3a30c4..00000000000 --- a/changelogs/unreleased/ide-file-finder.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Added fuzzy file finder to web IDE -merge_request: -author: -type: added diff --git a/changelogs/unreleased/ide-improve-commit-panel.yml b/changelogs/unreleased/ide-improve-commit-panel.yml deleted file mode 100644 index f6237db3039..00000000000 --- a/changelogs/unreleased/ide-improve-commit-panel.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve interaction on WebIDE commit panel -merge_request: -author: -type: changed diff --git a/changelogs/unreleased/improve-commit-message-body-rendering.yml b/changelogs/unreleased/improve-commit-message-body-rendering.yml deleted file mode 100644 index 3fb9b03725e..00000000000 --- a/changelogs/unreleased/improve-commit-message-body-rendering.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve commit message body rendering and fix responsive compare panels -merge_request: 18725 -author: Constance Okoghenun -type: changed diff --git a/changelogs/unreleased/improve-jobs-queuing-time-metric.yml b/changelogs/unreleased/improve-jobs-queuing-time-metric.yml deleted file mode 100644 index cee8b8523fd..00000000000 --- a/changelogs/unreleased/improve-jobs-queuing-time-metric.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Partition job_queue_duration_seconds with jobs_running_for_project -merge_request: 17730 -author: -type: changed diff --git a/changelogs/unreleased/improve-quick-actions-summary-preview.yml b/changelogs/unreleased/improve-quick-actions-summary-preview.yml deleted file mode 100644 index bc75c169ad7..00000000000 --- a/changelogs/unreleased/improve-quick-actions-summary-preview.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve quick actions summary preview -merge_request: 18659 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/increase-new-issue-metadata-form-margin.yml b/changelogs/unreleased/increase-new-issue-metadata-form-margin.yml deleted file mode 100644 index a7196f67969..00000000000 --- a/changelogs/unreleased/increase-new-issue-metadata-form-margin.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Increase new issue metadata form margin -merge_request: 18630 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/inform-the-user-when-there-are-no-project-import-options-available.yml b/changelogs/unreleased/inform-the-user-when-there-are-no-project-import-options-available.yml deleted file mode 100644 index c14f21fc644..00000000000 --- a/changelogs/unreleased/inform-the-user-when-there-are-no-project-import-options-available.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Inform the user when there are no project import options available -merge_request: 18716 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/issue_43660.yml b/changelogs/unreleased/issue_43660.yml deleted file mode 100644 index d83c0ebcbb5..00000000000 --- a/changelogs/unreleased/issue_43660.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Enable prometheus monitoring by default -merge_request: -author: -type: other diff --git a/changelogs/unreleased/jivl-refactor-activity-calendar.yml b/changelogs/unreleased/jivl-refactor-activity-calendar.yml deleted file mode 100644 index 0702ede4af9..00000000000 --- a/changelogs/unreleased/jivl-refactor-activity-calendar.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Refactored activity calendar -merge_request: 18469 -author: Enrico Scholz -type: changed diff --git a/changelogs/unreleased/jprovazn-commit-notes-api.yml b/changelogs/unreleased/jprovazn-commit-notes-api.yml deleted file mode 100644 index 4665d800ccf..00000000000 --- a/changelogs/unreleased/jprovazn-commit-notes-api.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add discussion API for merge requests and commits -merge_request: -author: -type: added diff --git a/changelogs/unreleased/jprovazn-generic-error.yml b/changelogs/unreleased/jprovazn-generic-error.yml deleted file mode 100644 index ced3b84fe02..00000000000 --- a/changelogs/unreleased/jprovazn-generic-error.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Display only generic message on merge error to avoid exposing any potentially - sensitive or user unfriendly backend messages. -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/jr-33320-lfs-settings-interface.yml b/changelogs/unreleased/jr-33320-lfs-settings-interface.yml deleted file mode 100644 index b39308f5474..00000000000 --- a/changelogs/unreleased/jr-33320-lfs-settings-interface.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Show group and project LFS settings in the interface to Owners and Masters -merge_request: 18562 -author: -type: changed diff --git a/changelogs/unreleased/jr-46209-web-ide-copy.yml b/changelogs/unreleased/jr-46209-web-ide-copy.yml deleted file mode 100644 index 87ccae6ced0..00000000000 --- a/changelogs/unreleased/jr-46209-web-ide-copy.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix outdated Web IDE welcome copy -merge_request: 18861 -author: -type: fixed diff --git a/changelogs/unreleased/jramsay-44880-filter-pipelines-by-sha.yml b/changelogs/unreleased/jramsay-44880-filter-pipelines-by-sha.yml deleted file mode 100644 index 3654aa28ff4..00000000000 --- a/changelogs/unreleased/jramsay-44880-filter-pipelines-by-sha.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add sha filter to pipelines list API -merge_request: 18125 -author: -type: changed diff --git a/changelogs/unreleased/label-links-on-project-transfer.yml b/changelogs/unreleased/label-links-on-project-transfer.yml deleted file mode 100644 index fabedb77cb3..00000000000 --- a/changelogs/unreleased/label-links-on-project-transfer.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix label links update on project transfer -merge_request: -author: -type: fixed diff --git a/changelogs/unreleased/live-trace-v2-efficient-destroy-all.yml b/changelogs/unreleased/live-trace-v2-efficient-destroy-all.yml deleted file mode 100644 index ab22739b73d..00000000000 --- a/changelogs/unreleased/live-trace-v2-efficient-destroy-all.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Destroy build_chunks efficiently with FastDestroyAll module -merge_request: 18575 -author: -type: performance diff --git a/changelogs/unreleased/live-trace-v2.yml b/changelogs/unreleased/live-trace-v2.yml deleted file mode 100644 index 875a66bc565..00000000000 --- a/changelogs/unreleased/live-trace-v2.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: New CI Job live-trace architecture -merge_request: 18169 -author: -type: changed diff --git a/changelogs/unreleased/move-board-blank-state-vue-component.yml b/changelogs/unreleased/move-board-blank-state-vue-component.yml deleted file mode 100644 index 0a278a8c009..00000000000 --- a/changelogs/unreleased/move-board-blank-state-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move BoardBlankState vue component -merge_request: 17666 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/move-estimate-only-pane-vue-component.yml b/changelogs/unreleased/move-estimate-only-pane-vue-component.yml deleted file mode 100644 index b6c538f70b3..00000000000 --- a/changelogs/unreleased/move-estimate-only-pane-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move TimeTrackingEstimateOnlyPane vue component -merge_request: 18318 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/move-help-state-vue-component.yml b/changelogs/unreleased/move-help-state-vue-component.yml deleted file mode 100644 index 6108368cde0..00000000000 --- a/changelogs/unreleased/move-help-state-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move TimeTrackingHelpState vue component -merge_request: 18319 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/move-notification-service-calls-to-sidekiq.yml b/changelogs/unreleased/move-notification-service-calls-to-sidekiq.yml deleted file mode 100644 index b2517884d3c..00000000000 --- a/changelogs/unreleased/move-notification-service-calls-to-sidekiq.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Compute notification recipients in background jobs -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/move-pipeline-failed-vue-component.yml b/changelogs/unreleased/move-pipeline-failed-vue-component.yml deleted file mode 100644 index 38d42134876..00000000000 --- a/changelogs/unreleased/move-pipeline-failed-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move PipelineFailed vue component -merge_request: 18277 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/move-time-tracking-spent-only-pane-vue-component.yml b/changelogs/unreleased/move-time-tracking-spent-only-pane-vue-component.yml deleted file mode 100644 index d2db0df5a04..00000000000 --- a/changelogs/unreleased/move-time-tracking-spent-only-pane-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move TimeTrackingSpentOnlyPane vue component -merge_request: 18710 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml b/changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml deleted file mode 100644 index 03a11a3038a..00000000000 --- a/changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Use persisted diff data instead fetching Git on discussions -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/performance-gb-improve-pipeline-creation-service.yml b/changelogs/unreleased/performance-gb-improve-pipeline-creation-service.yml deleted file mode 100644 index bd308f37bec..00000000000 --- a/changelogs/unreleased/performance-gb-improve-pipeline-creation-service.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Improve performance of a service responsible for creating a pipeline -merge_request: 18582 -author: -type: performance diff --git a/changelogs/unreleased/rd-44635-error-500-when-clicking-ghost-user-or-gitlab-support-bot-in-ui.yml b/changelogs/unreleased/rd-44635-error-500-when-clicking-ghost-user-or-gitlab-support-bot-in-ui.yml deleted file mode 100644 index a08a75ceda6..00000000000 --- a/changelogs/unreleased/rd-44635-error-500-when-clicking-ghost-user-or-gitlab-support-bot-in-ui.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix missing namespace for some internal users -merge_request: 18357 -author: -type: fixed diff --git a/changelogs/unreleased/rd-45502-uploading-project-export-with-lfs-file-locks-fails.yml b/changelogs/unreleased/rd-45502-uploading-project-export-with-lfs-file-locks-fails.yml deleted file mode 100644 index e3266dda629..00000000000 --- a/changelogs/unreleased/rd-45502-uploading-project-export-with-lfs-file-locks-fails.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Don't include lfs_file_locks data in export bundle -merge_request: 18495 -author: -type: fixed diff --git a/changelogs/unreleased/refactor-move-mr-widget-ready-to-merge-vue-component.yml b/changelogs/unreleased/refactor-move-mr-widget-ready-to-merge-vue-component.yml deleted file mode 100644 index 90192fae030..00000000000 --- a/changelogs/unreleased/refactor-move-mr-widget-ready-to-merge-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move ReadyToMerge vue component -merge_request: 17545 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/refactor-move-mr-widget-wip-vue-component.yml b/changelogs/unreleased/refactor-move-mr-widget-wip-vue-component.yml deleted file mode 100644 index 0f045431aae..00000000000 --- a/changelogs/unreleased/refactor-move-mr-widget-wip-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move WorkInProgress vue component -merge_request: 17536 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/refactor-move-no-tracking-pane-vue-component.yml b/changelogs/unreleased/refactor-move-no-tracking-pane-vue-component.yml deleted file mode 100644 index 4bb088a1e58..00000000000 --- a/changelogs/unreleased/refactor-move-no-tracking-pane-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move TimeTrackingNoTrackingPane vue component -merge_request: 18676 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/refactor-move-sidebar-time-tracking-vue-component.yml b/changelogs/unreleased/refactor-move-sidebar-time-tracking-vue-component.yml deleted file mode 100644 index 4f578bfcf26..00000000000 --- a/changelogs/unreleased/refactor-move-sidebar-time-tracking-vue-component.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Move SidebarTimeTracking vue component -merge_request: 18677 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/rename-overview-project-sidenav.yml b/changelogs/unreleased/rename-overview-project-sidenav.yml deleted file mode 100644 index 3632ef25c00..00000000000 --- a/changelogs/unreleased/rename-overview-project-sidenav.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Renamed Overview to Project in the contextual navigation at a project level -merge_request: 18295 -author: Constance Okoghenun -type: changed diff --git a/changelogs/unreleased/restore-label-underline-color.yml b/changelogs/unreleased/restore-label-underline-color.yml deleted file mode 100644 index 2e24ece5c36..00000000000 --- a/changelogs/unreleased/restore-label-underline-color.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Restore label underline color -merge_request: 18407 -author: George Tsiolis -type: fixed diff --git a/changelogs/unreleased/restore-size-and-position-for-fork-icon.yml b/changelogs/unreleased/restore-size-and-position-for-fork-icon.yml deleted file mode 100644 index dd8dad0b17d..00000000000 --- a/changelogs/unreleased/restore-size-and-position-for-fork-icon.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix size and position for fork icon -merge_request: 18449 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/revert-discussion-counter-height.yml b/changelogs/unreleased/revert-discussion-counter-height.yml deleted file mode 100644 index 331ff997009..00000000000 --- a/changelogs/unreleased/revert-discussion-counter-height.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Revert discussion counter height -merge_request: 18656 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/security-45689-fix-archive-cache-bug.yml b/changelogs/unreleased/security-45689-fix-archive-cache-bug.yml deleted file mode 100644 index 0103a7fc430..00000000000 --- a/changelogs/unreleased/security-45689-fix-archive-cache-bug.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Serve archive requests with the correct file in all cases -merge_request: -author: -type: security diff --git a/changelogs/unreleased/security_issue_42029.yml b/changelogs/unreleased/security_issue_42029.yml deleted file mode 100644 index 0772e33f930..00000000000 --- a/changelogs/unreleased/security_issue_42029.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Sanitizes user name to avoid XSS attacks -merge_request: -author: -type: security diff --git a/changelogs/unreleased/sh-bump-lograge.yml b/changelogs/unreleased/sh-bump-lograge.yml deleted file mode 100644 index 65b15153a35..00000000000 --- a/changelogs/unreleased/sh-bump-lograge.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Bump lograge to 0.10.0 and remove monkey patch -merge_request: -author: -type: other diff --git a/changelogs/unreleased/show-group-id-in-group-settings.yml b/changelogs/unreleased/show-group-id-in-group-settings.yml deleted file mode 100644 index b975fe8c71d..00000000000 --- a/changelogs/unreleased/show-group-id-in-group-settings.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Show group id in group settings -merge_request: 18482 -author: George Tsiolis -type: added diff --git a/changelogs/unreleased/show-runners-description-on-jobs-page.yml b/changelogs/unreleased/show-runners-description-on-jobs-page.yml deleted file mode 100644 index d9414a3d021..00000000000 --- a/changelogs/unreleased/show-runners-description-on-jobs-page.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Show Runner's description on job's page -merge_request: 17321 -author: -type: added diff --git a/changelogs/unreleased/tc-repo-verify-mails.yml b/changelogs/unreleased/tc-repo-verify-mails.yml deleted file mode 100644 index b4d3c4b1596..00000000000 --- a/changelogs/unreleased/tc-repo-verify-mails.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Small improvements to repository checks -merge_request: 18484 -author: -type: changed diff --git a/changelogs/unreleased/tz-upgrade-underscore.yml b/changelogs/unreleased/tz-upgrade-underscore.yml deleted file mode 100644 index 5dfd8154ecd..00000000000 --- a/changelogs/unreleased/tz-upgrade-underscore.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Upgrade underscore.js to 1.9.0 -merge_request: 18578 -author: -type: other diff --git a/changelogs/unreleased/unresolved-discussions-vue-component-i18n-and-tests.yml b/changelogs/unreleased/unresolved-discussions-vue-component-i18n-and-tests.yml deleted file mode 100644 index d99a9c93c0b..00000000000 --- a/changelogs/unreleased/unresolved-discussions-vue-component-i18n-and-tests.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Add i18n and update specs for UnresolvedDiscussions vue component -merge_request: 17866 -author: George Tsiolis -type: performance diff --git a/changelogs/unreleased/update-environment-item-action-buttons-icons.yml b/changelogs/unreleased/update-environment-item-action-buttons-icons.yml deleted file mode 100644 index 7f06022be3e..00000000000 --- a/changelogs/unreleased/update-environment-item-action-buttons-icons.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update environment item action buttons icons -merge_request: 18632 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/update-timeline-icon-for-description-edit.yml b/changelogs/unreleased/update-timeline-icon-for-description-edit.yml deleted file mode 100644 index 560db00e503..00000000000 --- a/changelogs/unreleased/update-timeline-icon-for-description-edit.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Update timeline icon for description edit -merge_request: 18633 -author: George Tsiolis -type: changed diff --git a/changelogs/unreleased/winh-dashboard-any-milestone.yml b/changelogs/unreleased/winh-dashboard-any-milestone.yml deleted file mode 100644 index 49eecd3da2b..00000000000 --- a/changelogs/unreleased/winh-dashboard-any-milestone.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reset milestone filter when clicking "Any Milestone" in dashboard -merge_request: 18531 -author: -type: fixed diff --git a/changelogs/unreleased/winh-dropdown-entry-unlocking.yml b/changelogs/unreleased/winh-dropdown-entry-unlocking.yml deleted file mode 100644 index fc669af1f57..00000000000 --- a/changelogs/unreleased/winh-dropdown-entry-unlocking.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Remove green background from unlock button in admin area -merge_request: 18288 -author: -type: changed diff --git a/changelogs/unreleased/winh-new-mergerequest-branch-picker.yml b/changelogs/unreleased/winh-new-mergerequest-branch-picker.yml deleted file mode 100644 index 401ecd09ef2..00000000000 --- a/changelogs/unreleased/winh-new-mergerequest-branch-picker.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Load branches on new merge request page asynchronously -merge_request: 18315 -author: -type: changed diff --git a/changelogs/unreleased/zj-branch-containing-sha-opt-out.yml b/changelogs/unreleased/zj-branch-containing-sha-opt-out.yml deleted file mode 100644 index 3d11ee588ae..00000000000 --- a/changelogs/unreleased/zj-branch-containing-sha-opt-out.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Detecting branchnames containing a commit uses Gitaly by default -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-find-license-opt-out.yml b/changelogs/unreleased/zj-find-license-opt-out.yml deleted file mode 100644 index be2656601a9..00000000000 --- a/changelogs/unreleased/zj-find-license-opt-out.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Detect repository license on Gitaly by default -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-fork-opt-out.yml b/changelogs/unreleased/zj-fork-opt-out.yml deleted file mode 100644 index 56bf6b8b0f6..00000000000 --- a/changelogs/unreleased/zj-fork-opt-out.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Gitaly handles repository forks by default -merge_request: -author: -type: other diff --git a/changelogs/unreleased/zj-namespace-service-mandatory.yml b/changelogs/unreleased/zj-namespace-service-mandatory.yml deleted file mode 100644 index d890741c51b..00000000000 --- a/changelogs/unreleased/zj-namespace-service-mandatory.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Finish NamespaceService migration to Gitaly -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-ref-exists-opt-out.yml b/changelogs/unreleased/zj-ref-exists-opt-out.yml deleted file mode 100644 index cdffecb0d0a..00000000000 --- a/changelogs/unreleased/zj-ref-exists-opt-out.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Check if a ref exists is done by Gitaly by default -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-repo-checksum-opt-out.yml b/changelogs/unreleased/zj-repo-checksum-opt-out.yml deleted file mode 100644 index 98dfedf7475..00000000000 --- a/changelogs/unreleased/zj-repo-checksum-opt-out.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Compute Gitlab::Git::Repository#checksum on Gitaly by default -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-repository-exist-mandatory.yml b/changelogs/unreleased/zj-repository-exist-mandatory.yml deleted file mode 100644 index 7d83446e90f..00000000000 --- a/changelogs/unreleased/zj-repository-exist-mandatory.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Repository#exists? is always executed through Gitaly -merge_request: -author: -type: performance diff --git a/changelogs/unreleased/zj-tag-containing-sha-opt-out.yml b/changelogs/unreleased/zj-tag-containing-sha-opt-out.yml deleted file mode 100644 index 4774c7811d1..00000000000 --- a/changelogs/unreleased/zj-tag-containing-sha-opt-out.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Detecting tags containing a commit uses Gitaly by default -merge_request: -author: -type: performance From 87d2c77b7db4d907a6f09b153c59a391ecc6cca7 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Mon, 21 May 2018 11:09:08 -0500 Subject: [PATCH 143/145] Update VERSION to 10.9.0-pre --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 60919325d67..981d3d21691 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.8.0-pre +10.9.0-pre From 3503dfe2b8577fe74e77be26029458fd16dace26 Mon Sep 17 00:00:00 2001 From: Constance Okoghenun Date: Mon, 21 May 2018 16:19:55 +0000 Subject: [PATCH 144/145] Renamed 'Overview' to 'Project' in collapsed contextual navigation at a project level --- app/views/layouts/nav/sidebar/_project.html.haml | 2 +- changelogs/unreleased/collapsed-contextual-nav-update.yml | 6 ++++++ spec/features/projects/actve_tabs_spec.rb | 4 ++-- spec/features/projects/user_sees_sidebar_spec.rb | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/collapsed-contextual-nav-update.yml diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 1cdb57bdfe8..f3af15d771d 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -19,7 +19,7 @@ = nav_link(path: 'projects#show', html_options: { class: "fly-out-top-item" } ) do = link_to project_path(@project) do %strong.fly-out-top-item-name - = _('Overview') + = _('Project') %li.divider.fly-out-top-item = nav_link(path: 'projects#show') do = link_to project_path(@project), title: _('Project details'), class: 'shortcuts-project' do diff --git a/changelogs/unreleased/collapsed-contextual-nav-update.yml b/changelogs/unreleased/collapsed-contextual-nav-update.yml new file mode 100644 index 00000000000..31a32a9e1e9 --- /dev/null +++ b/changelogs/unreleased/collapsed-contextual-nav-update.yml @@ -0,0 +1,6 @@ +--- +title: Renamed 'Overview' to 'Project' in collapsed contextual navigation at a project + level +merge_request: 18996 +author: Constance Okoghenun +type: fixed diff --git a/spec/features/projects/actve_tabs_spec.rb b/spec/features/projects/actve_tabs_spec.rb index 0bda68b83e7..ce5606b63ae 100644 --- a/spec/features/projects/actve_tabs_spec.rb +++ b/spec/features/projects/actve_tabs_spec.rb @@ -35,7 +35,7 @@ describe 'Project active tab' do visit project_path(project) end - it_behaves_like 'page has active tab', 'Overview' + it_behaves_like 'page has active tab', 'Project' it_behaves_like 'page has active sub tab', 'Details' context 'on project Home/Activity' do @@ -43,7 +43,7 @@ describe 'Project active tab' do click_tab('Activity') end - it_behaves_like 'page has active tab', 'Overview' + it_behaves_like 'page has active tab', 'Project' it_behaves_like 'page has active sub tab', 'Activity' end end diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb index cf80517b934..8a9255db9e8 100644 --- a/spec/features/projects/user_sees_sidebar_spec.rb +++ b/spec/features/projects/user_sees_sidebar_spec.rb @@ -37,7 +37,7 @@ describe 'Projects > User sees sidebar' do visit project_path(project) within('.nav-sidebar') do - expect(page).to have_content 'Overview' + expect(page).to have_content 'Project' expect(page).to have_content 'Issues' expect(page).to have_content 'Wiki' From c6f72ac9a88521257991aa9a0cc6d558126f5bb9 Mon Sep 17 00:00:00 2001 From: Mayra Cabrera Date: Mon, 21 May 2018 11:25:29 -0500 Subject: [PATCH 145/145] Update VERSION to 11.0.0-pre --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 981d3d21691..8ca9077d87b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.9.0-pre +11.0.0-pre