Commit ae949093 authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master-ce' into drop-default-value-status-deployments

parents 0f793c6f 8a581d53
...@@ -463,19 +463,19 @@ danger-review: ...@@ -463,19 +463,19 @@ danger-review:
rspec-pg: rspec-pg:
<<: *rspec-metadata-pg <<: *rspec-metadata-pg
parallel: 30 parallel: 50
rspec-mysql: rspec-mysql:
<<: *rspec-metadata-mysql <<: *rspec-metadata-mysql
parallel: 30 parallel: 50
rspec-pg-rails4: rspec-pg-rails4:
<<: *rspec-metadata-pg-rails4 <<: *rspec-metadata-pg-rails4
parallel: 30 parallel: 50
rspec-mysql-rails4: rspec-mysql-rails4:
<<: *rspec-metadata-mysql-rails4 <<: *rspec-metadata-mysql-rails4
parallel: 30 parallel: 50
static-analysis: static-analysis:
<<: *dedicated-no-docs-no-db-pull-cache-job <<: *dedicated-no-docs-no-db-pull-cache-job
......
...@@ -4,10 +4,9 @@ def rails5? ...@@ -4,10 +4,9 @@ def rails5?
end end
gem_versions = {} gem_versions = {}
gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2' gem_versions['activerecord_sane_schema_dumper'] = rails5? ? '1.0' : '0.2'
gem_versions['default_value_for'] = rails5? ? '~> 3.0.5' : '~> 3.0.0' gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10'
gem_versions['rails'] = rails5? ? '5.0.7' : '4.2.10' gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
gem_versions['rails-i18n'] = rails5? ? '~> 5.1' : '~> 4.0.9'
# --- The end of special code for migrating to Rails 5.0 --- # --- The end of special code for migrating to Rails 5.0 ---
source 'https://rubygems.org' source 'https://rubygems.org'
...@@ -15,13 +14,20 @@ source 'https://rubygems.org' ...@@ -15,13 +14,20 @@ source 'https://rubygems.org'
gem 'rails', gem_versions['rails'] gem 'rails', gem_versions['rails']
gem 'rails-deprecated_sanitizer', '~> 1.0.3' gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Improves copy-on-write performance for MRI
gem 'nakayoshi_fork', '~> 0.0.4'
# Responders respond_to and respond_with # Responders respond_to and respond_with
gem 'responders', '~> 2.0' gem 'responders', '~> 2.0'
gem 'sprockets', '~> 3.7.0' gem 'sprockets', '~> 3.7.0'
# Default values for AR models # Default values for AR models
gem 'default_value_for', gem_versions['default_value_for'] if rails5?
gem 'gitlab-default_value_for', '~> 3.1.1', require: 'default_value_for'
else
gem 'default_value_for', '~> 3.0.0'
end
# Supported DBs # Supported DBs
gem 'mysql2', '~> 0.4.10', group: :mysql gem 'mysql2', '~> 0.4.10', group: :mysql
...@@ -133,7 +139,7 @@ gem 'rdoc', '~> 6.0' ...@@ -133,7 +139,7 @@ gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.6' gem 'asciidoctor', '~> 1.5.8'
gem 'asciidoctor-plantuml', '0.0.8' gem 'asciidoctor-plantuml', '0.0.8'
gem 'rouge', '~> 3.1' gem 'rouge', '~> 3.1'
gem 'truncato', '~> 0.7.9' gem 'truncato', '~> 0.7.9'
...@@ -223,7 +229,7 @@ gem 'slack-notifier', '~> 1.5.1' ...@@ -223,7 +229,7 @@ gem 'slack-notifier', '~> 1.5.1'
gem 'hangouts-chat', '~> 0.0.5' gem 'hangouts-chat', '~> 0.0.5'
# Asana integration # Asana integration
gem 'asana', '~> 0.6.0' gem 'asana', '~> 0.8.1'
# FogBugz integration # FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
...@@ -383,7 +389,7 @@ group :test do ...@@ -383,7 +389,7 @@ group :test do
gem 'rails-controller-testing' if rails5? # Rails5 only gem. gem 'rails-controller-testing' if rails5? # Rails5 only gem.
gem 'test_after_commit', '~> 1.1' unless rails5? # Remove this gem when migrated to rails 5.0. It's been integrated to rails 5.0. gem 'test_after_commit', '~> 1.1' unless rails5? # Remove this gem when migrated to rails 5.0. It's been integrated to rails 5.0.
gem 'sham_rack', '~> 1.3.6' gem 'sham_rack', '~> 1.3.6'
gem 'concurrent-ruby', '~> 1.0.5' gem 'concurrent-ruby', '~> 1.1'
gem 'test-prof', '~> 0.2.5' gem 'test-prof', '~> 0.2.5'
gem 'rspec_junit_formatter' gem 'rspec_junit_formatter'
end end
......
...@@ -53,12 +53,12 @@ GEM ...@@ -53,12 +53,12 @@ GEM
aes_key_wrap (1.0.1) aes_key_wrap (1.0.1)
akismet (2.0.0) akismet (2.0.0)
arel (7.1.4) arel (7.1.4)
asana (0.6.0) asana (0.8.1)
faraday (~> 0.9) faraday (~> 0.9)
faraday_middleware (~> 0.9) faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.6.2) asciidoctor (1.5.8)
asciidoctor-plantuml (0.0.8) asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5) asciidoctor (~> 1.5)
ast (2.4.0) ast (2.4.0)
...@@ -128,9 +128,9 @@ GEM ...@@ -128,9 +128,9 @@ GEM
concord (0.1.5) concord (0.1.5)
adamantium (~> 0.2.0) adamantium (~> 0.2.0)
equalizer (~> 0.0.9) equalizer (~> 0.0.9)
concurrent-ruby (1.0.5) concurrent-ruby (1.1.3)
concurrent-ruby-ext (1.0.5) concurrent-ruby-ext (1.1.3)
concurrent-ruby (= 1.0.5) concurrent-ruby (= 1.1.3)
connection_pool (2.2.2) connection_pool (2.2.2)
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
...@@ -146,8 +146,6 @@ GEM ...@@ -146,8 +146,6 @@ GEM
html-pipeline html-pipeline
declarative (0.0.10) declarative (0.0.10)
declarative-option (0.1.0) declarative-option (0.1.0)
default_value_for (3.0.5)
activerecord (>= 3.2.0, < 5.2)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0) device_detector (1.0.0)
...@@ -277,6 +275,8 @@ GEM ...@@ -277,6 +275,8 @@ GEM
gitaly-proto (0.123.0) gitaly-proto (0.123.0)
grpc (~> 1.0) grpc (~> 1.0)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-default_value_for (3.1.1)
activerecord (>= 3.2.0, < 6.0)
gitlab-markup (1.6.5) gitlab-markup (1.6.5)
gitlab-sidekiq-fetcher (0.3.0) gitlab-sidekiq-fetcher (0.3.0)
sidekiq (~> 5) sidekiq (~> 5)
...@@ -379,7 +379,7 @@ GEM ...@@ -379,7 +379,7 @@ GEM
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpclient (2.8.3) httpclient (2.8.3)
i18n (1.1.0) i18n (1.1.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
icalendar (2.4.1) icalendar (2.4.1)
ice_nine (0.11.2) ice_nine (0.11.2)
...@@ -444,7 +444,7 @@ GEM ...@@ -444,7 +444,7 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.2.2) loofah (2.2.3)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0) mail (2.7.0)
...@@ -453,7 +453,7 @@ GEM ...@@ -453,7 +453,7 @@ GEM
memoist (0.16.0) memoist (0.16.0)
memoizable (0.4.2) memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
method_source (0.9.0) method_source (0.9.2)
mime-types (3.2.2) mime-types (3.2.2)
mime-types-data (~> 3.2015) mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812) mime-types-data (3.2018.0812)
...@@ -470,11 +470,12 @@ GEM ...@@ -470,11 +470,12 @@ GEM
mustermann-grape (1.0.0) mustermann-grape (1.0.0)
mustermann (~> 1.0.0) mustermann (~> 1.0.0)
mysql2 (0.4.10) mysql2 (0.4.10)
nakayoshi_fork (0.0.4)
net-ldap (0.16.0) net-ldap (0.16.0)
net-ssh (5.0.1) net-ssh (5.0.1)
netrc (0.11.0) netrc (0.11.0)
nio4r (2.3.1) nio4r (2.3.1)
nokogiri (1.8.4) nokogiri (1.8.5)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0) nokogumbo (1.5.0)
nokogiri nokogiri
...@@ -602,7 +603,7 @@ GEM ...@@ -602,7 +603,7 @@ GEM
get_process_mem (~> 0.2) get_process_mem (~> 0.2)
puma (>= 2.7, < 4) puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
rack (2.0.5) rack (2.0.6)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (4.4.1) rack-attack (4.4.1)
...@@ -766,8 +767,8 @@ GEM ...@@ -766,8 +767,8 @@ GEM
ruby-progressbar (1.9.0) ruby-progressbar (1.9.0)
ruby-saml (1.7.2) ruby-saml (1.7.2)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
ruby_parser (3.9.0) ruby_parser (3.11.0)
sexp_processor (~> 4.1) sexp_processor (~> 4.9)
rubyntlm (0.6.2) rubyntlm (0.6.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.2) rubyzip (1.2.2)
...@@ -807,7 +808,7 @@ GEM ...@@ -807,7 +808,7 @@ GEM
sentry-raven (2.7.2) sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0) faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9) settingslogic (2.0.9)
sexp_processor (4.9.0) sexp_processor (4.11.0)
sham_rack (1.3.6) sham_rack (1.3.6)
rack rack
shoulda-matchers (3.1.2) shoulda-matchers (3.1.2)
...@@ -941,8 +942,8 @@ DEPENDENCIES ...@@ -941,8 +942,8 @@ DEPENDENCIES
acts-as-taggable-on (~> 5.0) acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2) addressable (~> 2.5.2)
akismet (~> 2.0) akismet (~> 2.0)
asana (~> 0.6.0) asana (~> 0.8.1)
asciidoctor (~> 1.5.6) asciidoctor (~> 1.5.8)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
awesome_print awesome_print
...@@ -966,12 +967,11 @@ DEPENDENCIES ...@@ -966,12 +967,11 @@ DEPENDENCIES
chronic (~> 0.10.2) chronic (~> 0.10.2)
chronic_duration (~> 0.10.6) chronic_duration (~> 0.10.6)
commonmarker (~> 0.17) commonmarker (~> 0.17)
concurrent-ruby (~> 1.0.5) concurrent-ruby (~> 1.1)
connection_pool (~> 2.0) connection_pool (~> 2.0)
creole (~> 0.5.0) creole (~> 0.5.0)
database_cleaner (~> 1.5.0) database_cleaner (~> 1.5.0)
deckar01-task_list (= 2.0.0) deckar01-task_list (= 2.0.0)
default_value_for (~> 3.0.5)
device_detector device_detector
devise (~> 4.4) devise (~> 4.4)
devise-two-factor (~> 3.0.0) devise-two-factor (~> 3.0.0)
...@@ -1007,6 +1007,7 @@ DEPENDENCIES ...@@ -1007,6 +1007,7 @@ DEPENDENCIES
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.123.0) gitaly-proto (~> 0.123.0)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1)
gitlab-markup (~> 1.6.5) gitlab-markup (~> 1.6.5)
gitlab-sidekiq-fetcher gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4) gitlab-styles (~> 2.4)
...@@ -1051,6 +1052,7 @@ DEPENDENCIES ...@@ -1051,6 +1052,7 @@ DEPENDENCIES
mini_magick mini_magick
minitest (~> 5.7.0) minitest (~> 5.7.0)
mysql2 (~> 0.4.10) mysql2 (~> 0.4.10)
nakayoshi_fork (~> 0.0.4)
net-ldap net-ldap
net-ssh (~> 5.0) net-ssh (~> 5.0)
nokogiri (~> 1.8.2) nokogiri (~> 1.8.2)
......
...@@ -50,12 +50,12 @@ GEM ...@@ -50,12 +50,12 @@ GEM
aes_key_wrap (1.0.1) aes_key_wrap (1.0.1)
akismet (2.0.0) akismet (2.0.0)
arel (6.0.4) arel (6.0.4)
asana (0.6.0) asana (0.8.1)
faraday (~> 0.9) faraday (~> 0.9)
faraday_middleware (~> 0.9) faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.6.2) asciidoctor (1.5.8)
asciidoctor-plantuml (0.0.8) asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5) asciidoctor (~> 1.5)
ast (2.4.0) ast (2.4.0)
...@@ -125,9 +125,9 @@ GEM ...@@ -125,9 +125,9 @@ GEM
concord (0.1.5) concord (0.1.5)
adamantium (~> 0.2.0) adamantium (~> 0.2.0)
equalizer (~> 0.0.9) equalizer (~> 0.0.9)
concurrent-ruby (1.0.5) concurrent-ruby (1.1.3)
concurrent-ruby-ext (1.0.5) concurrent-ruby-ext (1.1.3)
concurrent-ruby (= 1.0.5) concurrent-ruby (= 1.1.3)
connection_pool (2.2.2) connection_pool (2.2.2)
crack (0.4.3) crack (0.4.3)
safe_yaml (~> 1.0.0) safe_yaml (~> 1.0.0)
...@@ -441,7 +441,7 @@ GEM ...@@ -441,7 +441,7 @@ GEM
activesupport (>= 4) activesupport (>= 4)
railties (>= 4) railties (>= 4)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.2.2) loofah (2.2.3)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0) mail (2.7.0)
...@@ -467,10 +467,11 @@ GEM ...@@ -467,10 +467,11 @@ GEM
mustermann-grape (1.0.0) mustermann-grape (1.0.0)
mustermann (~> 1.0.0) mustermann (~> 1.0.0)
mysql2 (0.4.10) mysql2 (0.4.10)
nakayoshi_fork (0.0.4)
net-ldap (0.16.0) net-ldap (0.16.0)
net-ssh (5.0.1) net-ssh (5.0.1)
netrc (0.11.0) netrc (0.11.0)
nokogiri (1.8.4) nokogiri (1.8.5)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0) nokogumbo (1.5.0)
nokogiri nokogiri
...@@ -758,8 +759,8 @@ GEM ...@@ -758,8 +759,8 @@ GEM
ruby-progressbar (1.9.0) ruby-progressbar (1.9.0)
ruby-saml (1.7.2) ruby-saml (1.7.2)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
ruby_parser (3.9.0) ruby_parser (3.11.0)
sexp_processor (~> 4.1) sexp_processor (~> 4.9)
rubyntlm (0.6.2) rubyntlm (0.6.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.2) rubyzip (1.2.2)
...@@ -799,7 +800,7 @@ GEM ...@@ -799,7 +800,7 @@ GEM
sentry-raven (2.7.2) sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0) faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9) settingslogic (2.0.9)
sexp_processor (4.9.0) sexp_processor (4.11.0)
sham_rack (1.3.6) sham_rack (1.3.6)
rack rack
shoulda-matchers (3.1.2) shoulda-matchers (3.1.2)
...@@ -932,8 +933,8 @@ DEPENDENCIES ...@@ -932,8 +933,8 @@ DEPENDENCIES
acts-as-taggable-on (~> 5.0) acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2) addressable (~> 2.5.2)
akismet (~> 2.0) akismet (~> 2.0)
asana (~> 0.6.0) asana (~> 0.8.1)
asciidoctor (~> 1.5.6) asciidoctor (~> 1.5.8)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
awesome_print awesome_print
...@@ -957,7 +958,7 @@ DEPENDENCIES ...@@ -957,7 +958,7 @@ DEPENDENCIES
chronic (~> 0.10.2) chronic (~> 0.10.2)
chronic_duration (~> 0.10.6) chronic_duration (~> 0.10.6)
commonmarker (~> 0.17) commonmarker (~> 0.17)
concurrent-ruby (~> 1.0.5) concurrent-ruby (~> 1.1)
connection_pool (~> 2.0) connection_pool (~> 2.0)
creole (~> 0.5.0) creole (~> 0.5.0)
database_cleaner (~> 1.5.0) database_cleaner (~> 1.5.0)
...@@ -1042,6 +1043,7 @@ DEPENDENCIES ...@@ -1042,6 +1043,7 @@ DEPENDENCIES
mini_magick mini_magick
minitest (~> 5.7.0) minitest (~> 5.7.0)
mysql2 (~> 0.4.10) mysql2 (~> 0.4.10)
nakayoshi_fork (~> 0.0.4)
net-ldap net-ldap
net-ssh (~> 5.0) net-ssh (~> 5.0)
nokogiri (~> 1.8.2) nokogiri (~> 1.8.2)
......
...@@ -112,7 +112,7 @@ const JumpToDiscussion = Vue.extend({ ...@@ -112,7 +112,7 @@ const JumpToDiscussion = Vue.extend({
if (!hasDiscussionsToJumpTo) { if (!hasDiscussionsToJumpTo) {
// If there are no discussions to jump to on the current page, // If there are no discussions to jump to on the current page,
// switch to the notes tab and jump to the first disucssion there. // switch to the notes tab and jump to the first discussion there.
window.mrTabs.activateTab('show'); window.mrTabs.activateTab('show');
activeTab = 'show'; activeTab = 'show';
jumpToFirstDiscussion = true; jumpToFirstDiscussion = true;
......
...@@ -23,7 +23,7 @@ export const diffHasAllExpandedDiscussions = (state, getters) => diff => { ...@@ -23,7 +23,7 @@ export const diffHasAllExpandedDiscussions = (state, getters) => diff => {
}; };
/** /**
* Checks if the diff has all discussions collpased * Checks if the diff has all discussions collapsed
* @param {Object} diff * @param {Object} diff
* @returns {Boolean} * @returns {Boolean}
*/ */
......
...@@ -69,10 +69,13 @@ export default class DropdownEmoji extends FilteredSearchDropdown { ...@@ -69,10 +69,13 @@ export default class DropdownEmoji extends FilteredSearchDropdown {
// Replace empty gl-emoji tag to real content // Replace empty gl-emoji tag to real content
const dropdownItems = [...this.dropdown.querySelectorAll('.filter-dropdown-item')]; const dropdownItems = [...this.dropdown.querySelectorAll('.filter-dropdown-item')];
dropdownItems.forEach(dropdownItem => { dropdownItems.forEach(dropdownItem => {
const name = dropdownItem.querySelector('.js-data-value').innerText; const valueElement = dropdownItem.querySelector('.js-data-value');
const emojiTag = this.glEmojiTag(name); if (valueElement !== null) {
const emojiElement = dropdownItem.querySelector('gl-emoji'); const name = valueElement.innerText;
emojiElement.outerHTML = emojiTag; const emojiTag = this.glEmojiTag(name);
const emojiElement = dropdownItem.querySelector('gl-emoji');
emojiElement.outerHTML = emojiTag;
}
}); });
} }
......
/* eslint-disable no-new */
import LabelsSelect from './labels_select';
import subscriptionSelect from './subscription_select';
import UsersSelect from './users_select';
import issueStatusSelect from './issue_status_select';
import MilestoneSelect from './milestone_select';
export default () => {
new UsersSelect();
new LabelsSelect();
new MilestoneSelect();
issueStatusSelect();
subscriptionSelect();
};
...@@ -22,7 +22,7 @@ export default { ...@@ -22,7 +22,7 @@ export default {
}, },
output: { output: {
type: Object, type: Object,
requred: true, required: true,
default: () => ({}), default: () => ({}),
}, },
}, },
......
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
projectSelect(); projectSelect();
initLegacyFilters();
}); });
import projectSelect from '~/project_select'; import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters'; import initFilteredSearch from '~/pages/search/init_filtered_search';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests();
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
projectSelect(); projectSelect();
initLegacyFilters();
}); });
...@@ -253,7 +253,6 @@ export class SearchAutocomplete { ...@@ -253,7 +253,6 @@ export class SearchAutocomplete {
} }
getCategoryContents() { getCategoryContents() {
const userId = gon.current_user_id;
const userName = gon.current_username; const userName = gon.current_username;
const { projectOptions, groupOptions, dashboardOptions } = gl; const { projectOptions, groupOptions, dashboardOptions } = gl;
...@@ -279,21 +278,21 @@ export class SearchAutocomplete { ...@@ -279,21 +278,21 @@ export class SearchAutocomplete {
const issueItems = [ const issueItems = [
{ {
text: s__('SearchAutocomplete|Issues assigned to me'), text: s__('SearchAutocomplete|Issues assigned to me'),
url: `${issuesPath}/?assignee_id=${userId}`, url: `${issuesPath}/?assignee_username=${userName}`,
}, },
{ {
text: s__("SearchAutocomplete|Issues I've created"), text: s__("SearchAutocomplete|Issues I've created"),
url: `${issuesPath}/?author_id=${userId}`, url: `${issuesPath}/?author_username=${userName}`,
}, },
]; ];
const mergeRequestItems = [ const mergeRequestItems = [
{ {
text: s__('SearchAutocomplete|Merge requests assigned to me'), text: s__('SearchAutocomplete|Merge requests assigned to me'),
url: `${mrPath}/?assignee_id=${userId}`, url: `${mrPath}/?assignee_username=${userName}`,
}, },
{ {
text: s__("SearchAutocomplete|Merge requests I've created"), text: s__("SearchAutocomplete|Merge requests I've created"),
url: `${mrPath}/?author_id=${userId}`, url: `${mrPath}/?author_username=${userName}`,
}, },
]; ];
......
...@@ -33,6 +33,10 @@ export default { ...@@ -33,6 +33,10 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
showMetrics: {
type: Boolean,
required: true,
},
}, },
deployedTextMap: { deployedTextMap: {
running: __('Deploying to'), running: __('Deploying to'),
...@@ -74,6 +78,9 @@ export default { ...@@ -74,6 +78,9 @@ export default {
shouldRenderDropdown() { shouldRenderDropdown() {
return this.deployment.changes && this.deployment.changes.length > 0; return this.deployment.changes && this.deployment.changes.length > 0;
}, },
showMemoryUsage() {
return this.hasMetrics && this.showMetrics;
},
}, },
methods: { methods: {
stopEnvironment() { stopEnvironment() {
...@@ -136,7 +143,7 @@ export default { ...@@ -136,7 +143,7 @@ export default {
{{ deployTimeago }} {{ deployTimeago }}
</span> </span>
<memory-usage <memory-usage
v-if="hasMetrics" v-if="showMemoryUsage"
:metrics-url="deployment.metrics_url" :metrics-url="deployment.metrics_url"
:metrics-monitoring-url="deployment.metrics_monitoring_url" :metrics-monitoring-url="deployment.metrics_monitoring_url"
/> />
......
...@@ -312,6 +312,7 @@ export default { ...@@ -312,6 +312,7 @@ export default {
:key="`pre-merge-deploy-${deployment.id}`" :key="`pre-merge-deploy-${deployment.id}`"
class="js-pre-merge-deploy" class="js-pre-merge-deploy"
:deployment="deployment" :deployment="deployment"
:show-metrics="false"
/> />
<div class="mr-section-container"> <div class="mr-section-container">
<grouped-test-reports-app <grouped-test-reports-app
...@@ -366,6 +367,7 @@ export default { ...@@ -366,6 +367,7 @@ export default {
v-for="postMergeDeployment in mr.postMergeDeployments" v-for="postMergeDeployment in mr.postMergeDeployments"
:key="`post-merge-deploy-${postMergeDeployment.id}`" :key="`post-merge-deploy-${postMergeDeployment.id}`"
:deployment="postMergeDeployment" :deployment="postMergeDeployment"
:show-metrics="true"
class="js-post-deployment" class="js-post-deployment"
/> />
</template> </template>
......
...@@ -181,11 +181,11 @@ class ApplicationController < ActionController::Base ...@@ -181,11 +181,11 @@ class ApplicationController < ActionController::Base
Ability.allowed?(object, action, subject) Ability.allowed?(object, action, subject)
end end
def access_denied!(message = nil) def access_denied!(message = nil, status = nil)
# If we display a custom access denied message to the user, we don't want to # If we display a custom access denied message to the user, we don't want to
# hide existence of the resource, rather tell them they cannot access it using # hide existence of the resource, rather tell them they cannot access it using
# the provided message # the provided message
status = message.present? ? :forbidden : :not_found status ||= message.present? ? :forbidden : :not_found
respond_to do |format| respond_to do |format|
format.any { head status } format.any { head status }
......
...@@ -81,36 +81,36 @@ module IssuableCollections ...@@ -81,36 +81,36 @@ module IssuableCollections
end end
def issuable_finder_for(finder_class) def issuable_finder_for(finder_class)
finder_class.new(current_user, filter_params) finder_class.new(current_user, finder_options)
end end
# rubocop:disable Gitlab/ModuleWithInstanceVariables # rubocop:disable Gitlab/ModuleWithInstanceVariables
# rubocop: disable CodeReuse/ActiveRecord def finder_options
def filter_params params[:state] = default_state if params[:state].blank?
set_sort_order_from_cookie
set_default_state
# Skip irrelevant Rails routing params options = {
@filter_params = params.dup.except(:controller, :action, :namespace_id) scope: params[:scope],
@filter_params[:sort] ||= default_sort_order state: params[:state],
sort: set_sort_order_from_cookie || default_sort_order
}
@sort = @filter_params[:sort] # Used by view to highlight active option
@sort = options[:sort]
if @project if @project
@filter_params[:project_id] = @project.id options[:project_id] = @project.id
elsif @group elsif @group
@filter_params[:group_id] = @group.id options[:group_id] = @group.id
@filter_params[:include_subgroups] = true options[:include_subgroups] = true
@filter_params[:use_cte_for_search] = true options[:use_cte_for_search] = true
end end
@filter_params.permit(finder_type.valid_params) params.permit(finder_type.valid_params).merge(options)
end end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop:enable Gitlab/ModuleWithInstanceVariables # rubocop:enable Gitlab/ModuleWithInstanceVariables
def set_default_state def default_state
params[:state] = 'opened' if params[:state].blank? 'opened'
end end
def set_sort_order_from_cookie def set_sort_order_from_cookie
...@@ -121,7 +121,7 @@ module IssuableCollections ...@@ -121,7 +121,7 @@ module IssuableCollections
sort_value = update_cookie_value(sort_param) sort_value = update_cookie_value(sort_param)
set_secure_cookie(remember_sorting_key, sort_value) set_secure_cookie(remember_sorting_key, sort_value)
params[:sort] = sort_value sort_value
end end
def remember_sorting_key def remember_sorting_key
......
...@@ -19,7 +19,7 @@ module MergeRequestsAction ...@@ -19,7 +19,7 @@ module MergeRequestsAction
(MergeRequestsFinder if action_name == 'merge_requests') (MergeRequestsFinder if action_name == 'merge_requests')
end end
def filter_params def finder_options
super.merge(non_archived: true) super.merge(non_archived: true)
end end
end end
...@@ -4,13 +4,6 @@ class DashboardController < Dashboard::ApplicationController ...@@ -4,13 +4,6 @@ class DashboardController < Dashboard::ApplicationController
include IssuesAction include IssuesAction
include MergeRequestsAction include MergeRequestsAction
FILTER_PARAMS = [
:author_id,
:assignee_id,
:milestone_title,
:label_name
].freeze
before_action :event_filter, only: :activity before_action :event_filter, only: :activity
before_action :projects, only: [:issues, :merge_requests] before_action :projects, only: [:issues, :merge_requests]
before_action :set_show_full_reference, only: [:issues, :merge_requests] before_action :set_show_full_reference, only: [:issues, :merge_requests]
...@@ -51,10 +44,13 @@ class DashboardController < Dashboard::ApplicationController ...@@ -51,10 +44,13 @@ class DashboardController < Dashboard::ApplicationController
end end
def check_filters_presence! def check_filters_presence!
@no_filters_set = FILTER_PARAMS.none? { |k| params.key?(k) } @no_filters_set = finder_type.scalar_params.none? { |k| params.key?(k) }
return unless @no_filters_set return unless @no_filters_set
# Call to set selected `state` and `sort` options in view
finder_options
respond_to do |format| respond_to do |format|
format.html { render } format.html { render }
format.atom { head :bad_request } format.atom { head :bad_request }
......
...@@ -46,7 +46,9 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -46,7 +46,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def enable def enable
Projects::EnableDeployKeyService.new(@project, current_user, params).execute key = Projects::EnableDeployKeyService.new(@project, current_user, params).execute
return render_404 unless key
respond_to do |format| respond_to do |format|
format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') } format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
...@@ -54,19 +56,16 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -54,19 +56,16 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
end end
# rubocop: disable CodeReuse/ActiveRecord
def disable def disable
deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]) deploy_key_project = Projects::DisableDeployKeyService.new(@project, current_user, params).execute
return render_404 unless deploy_key_project
deploy_key_project.destroy! return render_404 unless deploy_key_project
respond_to do |format| respond_to do |format|
format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') } format.html { redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings') }
format.json { head :ok } format.json { head :ok }
end end
end end
# rubocop: enable CodeReuse/ActiveRecord
protected protected
......
...@@ -45,9 +45,9 @@ class RootController < Dashboard::ProjectsController ...@@ -45,9 +45,9 @@ class RootController < Dashboard::ProjectsController
when 'todos' when 'todos'
redirect_to(dashboard_todos_path) redirect_to(dashboard_todos_path)
when 'issues' when 'issues'
redirect_to(issues_dashboard_path(assignee_id: current_user.id)) redirect_to(issues_dashboard_path(assignee_username: current_user.username))
when 'merge_requests' when 'merge_requests'
redirect_to(merge_requests_dashboard_path(assignee_id: current_user.id)) redirect_to(merge_requests_dashboard_path(assignee_username: current_user.username))
end end
end end
......
...@@ -58,7 +58,7 @@ class EventsFinder ...@@ -58,7 +58,7 @@ class EventsFinder
def by_target_type(events) def by_target_type(events)
return events unless Event::TARGET_TYPES[params[:target_type]] return events unless Event::TARGET_TYPES[params[:target_type]]
events.where(target_type: Event::TARGET_TYPES[params[:target_type]]) events.where(target_type: Event::TARGET_TYPES[params[:target_type]].name)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -14,7 +14,9 @@ ...@@ -14,7 +14,9 @@
# project_id: integer # project_id: integer
# milestone_title: string # milestone_title: string
# author_id: integer # author_id: integer
# author_username: string
# assignee_id: integer or 'None' or 'Any' # assignee_id: integer or 'None' or 'Any'
# assignee_username: string
# search: string # search: string
# label_name: string # label_name: string
# sort: string # sort: string
...@@ -49,25 +51,15 @@ class IssuableFinder ...@@ -49,25 +51,15 @@ class IssuableFinder
assignee_username assignee_username
author_id author_id
author_username author_username
authorized_only
group_id
iids
label_name label_name
milestone_title milestone_title
my_reaction_emoji my_reaction_emoji
non_archived
project_id
scope
search search
sort
state
include_subgroups
use_cte_for_search
] ]
end end
def self.array_params def self.array_params
@array_params ||= { label_name: [], iids: [], assignee_username: [] } @array_params ||= { label_name: [], assignee_username: [] }
end end
def self.valid_params def self.valid_params
......
...@@ -173,17 +173,7 @@ module ApplicationHelper ...@@ -173,17 +173,7 @@ module ApplicationHelper
without = options.delete(:without) without = options.delete(:without)
add_label = options.delete(:label) add_label = options.delete(:label)
exist_opts = { options = request.query_parameters.merge(options)
state: params[:state],
scope: params[:scope],
milestone_title: params[:milestone_title],
assignee_id: params[:assignee_id],
author_id: params[:author_id],
search: params[:search],
label_name: params[:label_name]
}
options = exist_opts.merge(options)
if without.present? if without.present?
without.each do |key| without.each do |key|
......
...@@ -24,6 +24,23 @@ module AuthHelper ...@@ -24,6 +24,23 @@ module AuthHelper
Gitlab::Auth::OAuth::Provider.label_for(name) Gitlab::Auth::OAuth::Provider.label_for(name)
end end
def form_based_provider_priority
['crowd', /^ldap/, 'kerberos']
end
def form_based_provider_with_highest_priority
@form_based_provider_with_highest_priority ||= begin
form_based_provider_priority.each do |provider_regexp|
highest_priority = form_based_providers.find { |provider| provider.match?(provider_regexp) }
break highest_priority unless highest_priority.nil?
end
end
end
def form_based_auth_provider_has_active_class?(provider)
form_based_provider_with_highest_priority == provider
end
def form_based_provider?(name) def form_based_provider?(name)
[LDAP_PROVIDER, 'crowd'].any? { |pattern| pattern === name.to_s } [LDAP_PROVIDER, 'crowd'].any? { |pattern| pattern === name.to_s }
end end
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
module DashboardHelper module DashboardHelper
def assigned_issues_dashboard_path def assigned_issues_dashboard_path
issues_dashboard_path(assignee_id: current_user.id) issues_dashboard_path(assignee_username: current_user.username)
end end
def assigned_mrs_dashboard_path def assigned_mrs_dashboard_path
merge_requests_dashboard_path(assignee_id: current_user.id) merge_requests_dashboard_path(assignee_username: current_user.username)
end end
def dashboard_nav_links def dashboard_nav_links
......
...@@ -163,15 +163,26 @@ module SearchHelper ...@@ -163,15 +163,26 @@ module SearchHelper
if @project.present? if @project.present?
opts[:data]['project-id'] = @project.id opts[:data]['project-id'] = @project.id
opts[:data]['base-endpoint'] = project_path(@project) opts[:data]['base-endpoint'] = project_path(@project)
else elsif @group.present?
# Group context
opts[:data]['group-id'] = @group.id opts[:data]['group-id'] = @group.id
opts[:data]['base-endpoint'] = group_canonical_path(@group) opts[:data]['base-endpoint'] = group_canonical_path(@group)
else
opts[:data]['base-endpoint'] = root_dashboard_path
end end
opts opts
end end
def search_history_storage_prefix
if @project.present?
@project.full_path
elsif @group.present?
@group.full_path
else
'dashboard'
end
end
# Sanitize a HTML field for search display. Most tags are stripped out and the # Sanitize a HTML field for search display. Most tags are stripped out and the
# maximum length is set to 200 characters. # maximum length is set to 200 characters.
def search_md_sanitize(object, field) def search_md_sanitize(object, field)
......
...@@ -98,7 +98,7 @@ module Ci ...@@ -98,7 +98,7 @@ module Ci
scope :matches_tag_ids, -> (tag_ids) do scope :matches_tag_ids, -> (tag_ids) do
matcher = ::ActsAsTaggableOn::Tagging matcher = ::ActsAsTaggableOn::Tagging
.where(taggable_type: CommitStatus) .where(taggable_type: CommitStatus.name)
.where(context: 'tags') .where(context: 'tags')
.where('taggable_id = ci_builds.id') .where('taggable_id = ci_builds.id')
.where.not(tag_id: tag_ids).select('1') .where.not(tag_id: tag_ids).select('1')
...@@ -108,7 +108,7 @@ module Ci ...@@ -108,7 +108,7 @@ module Ci
scope :with_any_tags, -> do scope :with_any_tags, -> do
matcher = ::ActsAsTaggableOn::Tagging matcher = ::ActsAsTaggableOn::Tagging
.where(taggable_type: CommitStatus) .where(taggable_type: CommitStatus.name)
.where(context: 'tags') .where(context: 'tags')
.where('taggable_id = ci_builds.id').select('1') .where('taggable_id = ci_builds.id').select('1')
...@@ -464,7 +464,9 @@ module Ci ...@@ -464,7 +464,9 @@ module Ci
end end
def repo_url def repo_url
auth = "gitlab-ci-token:#{ensure_token!}@" return unless token
auth = "gitlab-ci-token:#{token}@"
project.http_url_to_repo.sub(%r{^https?://}) do |prefix| project.http_url_to_repo.sub(%r{^https?://}) do |prefix|
prefix + auth prefix + auth
end end
...@@ -725,7 +727,7 @@ module Ci ...@@ -725,7 +727,7 @@ module Ci
trace = trace.dup trace = trace.dup
Gitlab::Ci::MaskSecret.mask!(trace, project.runners_token) if project Gitlab::Ci::MaskSecret.mask!(trace, project.runners_token) if project
Gitlab::Ci::MaskSecret.mask!(trace, token) Gitlab::Ci::MaskSecret.mask!(trace, token) if token
trace trace
end end
...@@ -814,12 +816,12 @@ module Ci ...@@ -814,12 +816,12 @@ module Ci
.concat(pipeline.persisted_variables) .concat(pipeline.persisted_variables)
.append(key: 'CI_JOB_ID', value: id.to_s) .append(key: 'CI_JOB_ID', value: id.to_s)
.append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self)) .append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self))
.append(key: 'CI_JOB_TOKEN', value: token, public: false) .append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false)
.append(key: 'CI_BUILD_ID', value: id.to_s) .append(key: 'CI_BUILD_ID', value: id.to_s)
.append(key: 'CI_BUILD_TOKEN', value: token, public: false) .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false)
.append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER) .append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER)
.append(key: 'CI_REGISTRY_PASSWORD', value: token, public: false) .append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false)
.append(key: 'CI_REPOSITORY_URL', value: repo_url, public: false) .append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false)
.concat(deploy_token_variables) .concat(deploy_token_variables)
end end
end end
...@@ -831,9 +833,9 @@ module Ci ...@@ -831,9 +833,9 @@ module Ci
variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(',')) variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(','))
variables.append(key: 'CI_SERVER_NAME', value: 'GitLab') variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION) variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: gitlab_version_info.major.to_s) variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s)
variables.append(key: 'CI_SERVER_VERSION_MINOR', value: gitlab_version_info.minor.to_s) variables.append(key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s)
variables.append(key: 'CI_SERVER_VERSION_PATCH', value: gitlab_version_info.patch.to_s) variables.append(key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s)
variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision) variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
variables.append(key: 'CI_JOB_NAME', value: name) variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage) variables.append(key: 'CI_JOB_STAGE', value: stage)
...@@ -850,10 +852,6 @@ module Ci ...@@ -850,10 +852,6 @@ module Ci
end end
end end
def gitlab_version_info
@gitlab_version_info ||= Gitlab::VersionInfo.parse(Gitlab::VERSION)
end
def legacy_variables def legacy_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables| Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_BUILD_REF', value: sha) variables.append(key: 'CI_BUILD_REF', value: sha)
......
...@@ -11,9 +11,13 @@ module Clusters ...@@ -11,9 +11,13 @@ module Clusters
belongs_to :project, class_name: '::Project' belongs_to :project, class_name: '::Project'
has_one :platform_kubernetes, through: :cluster has_one :platform_kubernetes, through: :cluster
before_validation :set_defaults
validates :namespace, presence: true validates :namespace, presence: true
validates :namespace, uniqueness: { scope: :cluster_id } validates :namespace, uniqueness: { scope: :cluster_id }
validates :service_account_name, presence: true
delegate :ca_pem, to: :platform_kubernetes, allow_nil: true delegate :ca_pem, to: :platform_kubernetes, allow_nil: true
delegate :api_url, to: :platform_kubernetes, allow_nil: true delegate :api_url, to: :platform_kubernetes, allow_nil: true
...@@ -28,38 +32,43 @@ module Clusters ...@@ -28,38 +32,43 @@ module Clusters
"#{namespace}-token" "#{namespace}-token"
end end
def configure_predefined_credentials
self.namespace = kubernetes_or_project_namespace
self.service_account_name = default_service_account_name
end
def predefined_variables def predefined_variables
config = YAML.dump(kubeconfig) config = YAML.dump(kubeconfig)
Gitlab::Ci::Variables::Collection.new.tap do |variables| Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables variables
.append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name) .append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name.to_s)
.append(key: 'KUBE_NAMESPACE', value: namespace) .append(key: 'KUBE_NAMESPACE', value: namespace.to_s)
.append(key: 'KUBE_TOKEN', value: service_account_token, public: false) .append(key: 'KUBE_TOKEN', value: service_account_token.to_s, public: false)
.append(key: 'KUBECONFIG', value: config, public: false, file: true) .append(key: 'KUBECONFIG', value: config, public: false, file: true)
end end
end end
private def set_defaults
self.namespace ||= default_platform_kubernetes_namespace
def kubernetes_or_project_namespace self.namespace ||= default_project_namespace
platform_kubernetes&.namespace.presence || project_namespace self.service_account_name ||= default_service_account_name
end end
private
def default_service_account_name def default_service_account_name
return unless namespace
"#{namespace}-service-account" "#{namespace}-service-account"
end end
def project_namespace def default_platform_kubernetes_namespace
Gitlab::NamespaceSanitizer.sanitize(project_slug) platform_kubernetes&.namespace.presence
end
def default_project_namespace
Gitlab::NamespaceSanitizer.sanitize(project_slug) if project_slug
end end
def project_slug def project_slug
return unless project
"#{project.path}-#{project.id}".downcase "#{project.path}-#{project.id}".downcase
end end
......
...@@ -230,24 +230,13 @@ class Commit ...@@ -230,24 +230,13 @@ class Commit
def lazy_author def lazy_author
BatchLoader.for(author_email.downcase).batch do |emails, loader| BatchLoader.for(author_email.downcase).batch do |emails, loader|
# A Hash that maps user Emails to the corresponding User objects. The users = User.by_any_email(emails).includes(:emails)
# Emails at this point are the _primary_ Emails of the Users.
users_for_emails = User emails.each do |email|
.by_any_email(emails) user = users.find { |u| u.any_email?(email) }
.each_with_object({}) { |user, hash| hash[user.email] = user }
loader.call(email, user)
users_for_ids = users_for_emails end
.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
end end
......
...@@ -86,7 +86,7 @@ module Avatarable ...@@ -86,7 +86,7 @@ module Avatarable
params[:model].upload_paths(params[:identifier]) params[:model].upload_paths(params[:identifier])
end end
Upload.where(uploader: AvatarUploader, path: paths).find_each do |upload| Upload.where(uploader: AvatarUploader.name, path: paths).find_each do |upload|
model = model_class.instantiate('id' => upload.model_id) model = model_class.instantiate('id' => upload.model_id)
loader.call({ model: model, identifier: File.basename(upload.path) }, upload) loader.call({ model: model, identifier: File.basename(upload.path) }, upload)
......
...@@ -152,11 +152,13 @@ class Member < ActiveRecord::Base ...@@ -152,11 +152,13 @@ class Member < ActiveRecord::Base
return member unless can_update_member?(current_user, member) return member unless can_update_member?(current_user, member)
member.attributes = { set_member_attributes(
created_by: member.created_by || current_user, member,
access_level: access_level, access_level,
expires_at: expires_at current_user: current_user,
} expires_at: expires_at,
ldap: ldap
)
if member.request? if member.request?
::Members::ApproveAccessRequestService.new( ::Members::ApproveAccessRequestService.new(
...@@ -175,6 +177,18 @@ class Member < ActiveRecord::Base ...@@ -175,6 +177,18 @@ class Member < ActiveRecord::Base
# rubocop: enable CodeReuse/ServiceClass # rubocop: enable CodeReuse/ServiceClass
end end
# Populates the attributes of a member.
#
# This logic resides in a separate method so that EE can extend this logic,
# without having to patch the `add_user` method directly.
def set_member_attributes(member, access_level, current_user: nil, expires_at: nil, ldap: false)
member.attributes = {
created_by: member.created_by || current_user,
access_level: access_level,
expires_at: expires_at
}
end
def add_users(source, users, access_level, current_user: nil, expires_at: nil) def add_users(source, users, access_level, current_user: nil, expires_at: nil)
return [] unless users.present? return [] unless users.present?
......
...@@ -26,16 +26,8 @@ module ChatMessage ...@@ -26,16 +26,8 @@ module ChatMessage
end end
def activity def activity
action = if new_branch?
"created"
elsif removed_branch?
"removed"
else
"pushed to"
end
{ {
title: "#{user_combined_name} #{action} #{ref_type}", title: humanized_action(short: true),
subtitle: "in #{project_link}", subtitle: "in #{project_link}",
text: compare_link, text: compare_link,
image: user_avatar image: user_avatar
...@@ -44,32 +36,21 @@ module ChatMessage ...@@ -44,32 +36,21 @@ module ChatMessage
private private
def humanized_action(short: false)
action, ref_link, target_link = compose_action_details
text = [user_combined_name, action, ref_type, ref_link]
text << target_link unless short
text.join(' ')
end
def message def message
if new_branch? humanized_action
new_branch_message
elsif removed_branch?
removed_branch_message
else
push_message
end
end end
def format(string) def format(string)
Slack::Notifier::LinkFormatter.format(string) Slack::Notifier::LinkFormatter.format(string)
end end
def new_branch_message
"#{user_combined_name} pushed new #{ref_type} #{branch_link} to #{project_link}"
end
def removed_branch_message
"#{user_combined_name} removed #{ref_type} #{ref} from #{project_link}"
end
def push_message
"#{user_combined_name} pushed to #{ref_type} #{branch_link} of #{project_link} (#{compare_link})"
end
def commit_messages def commit_messages
commits.map { |commit| compose_commit_message(commit) }.join("\n\n") commits.map { |commit| compose_commit_message(commit) }.join("\n\n")
end end
...@@ -115,6 +96,16 @@ module ChatMessage ...@@ -115,6 +96,16 @@ module ChatMessage
"[Compare changes](#{compare_url})" "[Compare changes](#{compare_url})"
end end
def compose_action_details
if new_branch?
['pushed new', branch_link, "to #{project_link}"]
elsif removed_branch?
['removed', ref, "from #{project_link}"]
else
['pushed to', branch_link, "of #{project_link} (#{compare_link})"]
end
end
def attachment_color def attachment_color
'#345' '#345'
end end
......
...@@ -349,20 +349,28 @@ class User < ActiveRecord::Base ...@@ -349,20 +349,28 @@ class User < ActiveRecord::Base
def find_by_any_email(email, confirmed: false) def find_by_any_email(email, confirmed: false)
return unless email return unless email
downcased = email.downcase by_any_email(email, confirmed: confirmed).take
find_by_private_commit_email(downcased) || by_any_email(downcased, confirmed: confirmed).take
end end
# Returns a relation containing all the users for the given Email address # Returns a relation containing all the users for the given email addresses
def by_any_email(email, confirmed: false) #
users = where(email: email) # @param emails [String, Array<String>] email addresses to check
users = users.confirmed if confirmed # @param confirmed [Boolean] Only return users where the email is confirmed
def by_any_email(emails, confirmed: false)
emails = Array(emails).map(&:downcase)
from_users = where(email: emails)
from_users = from_users.confirmed if confirmed
emails = joins(:emails).where(emails: { email: email }) from_emails = joins(:emails).where(emails: { email: emails })
emails = emails.confirmed if confirmed from_emails = from_emails.confirmed if confirmed
from_union([users, emails]) items = [from_users, from_emails]
user_ids = Gitlab::PrivateCommitEmail.user_ids_for_emails(emails)
items << where(id: user_ids) if user_ids.present?
from_union(items)
end end
def find_by_private_commit_email(email) def find_by_private_commit_email(email)
...@@ -1031,6 +1039,7 @@ class User < ActiveRecord::Base ...@@ -1031,6 +1039,7 @@ class User < ActiveRecord::Base
def all_emails def all_emails
all_emails = [] all_emails = []
all_emails << email unless temp_oauth_email? all_emails << email unless temp_oauth_email?
all_emails << private_commit_email
all_emails.concat(emails.map(&:email)) all_emails.concat(emails.map(&:email))
all_emails all_emails
end end
...@@ -1043,16 +1052,24 @@ class User < ActiveRecord::Base ...@@ -1043,16 +1052,24 @@ class User < ActiveRecord::Base
verified_emails verified_emails
end end
def any_email?(check_email)
downcased = check_email.downcase
# handle the outdated private commit email case
return true if persisted? &&
id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased)
all_emails.include?(check_email.downcase)
end
def verified_email?(check_email) def verified_email?(check_email)
downcased = check_email.downcase downcased = check_email.downcase
if email == downcased # handle the outdated private commit email case
primary_email_verified? return true if persisted? &&
else id == Gitlab::PrivateCommitEmail.user_id_for_email(downcased)
user_id = Gitlab::PrivateCommitEmail.user_id_for_email(downcased)
user_id == id || emails.confirmed.where(email: downcased).exists? verified_emails.include?(check_email.downcase)
end
end end
def hook_attrs def hook_attrs
......
...@@ -23,7 +23,7 @@ module Clusters ...@@ -23,7 +23,7 @@ module Clusters
attr_reader :cluster, :kubernetes_namespace, :platform attr_reader :cluster, :kubernetes_namespace, :platform
def configure_kubernetes_namespace def configure_kubernetes_namespace
kubernetes_namespace.configure_predefined_credentials kubernetes_namespace.set_defaults
end end
def create_project_service_account def create_project_service_account
......
# frozen_string_literal: true
module Projects
class DisableDeployKeyService < BaseService
def execute
# rubocop: disable CodeReuse/ActiveRecord
deploy_key_project = project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
# rubocop: enable CodeReuse/ActiveRecord
deploy_key_project&.destroy!
end
end
end
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
module Projects module Projects
class EnableDeployKeyService < BaseService class EnableDeployKeyService < BaseService
# rubocop: disable CodeReuse/ActiveRecord
def execute def execute
key = accessible_keys.find_by(id: params[:key_id] || params[:id]) key_id = params[:key_id] || params[:id]
key = find_accessible_key(key_id)
return unless key return unless key
unless project.deploy_keys.include?(key) unless project.deploy_keys.include?(key)
...@@ -13,12 +14,15 @@ module Projects ...@@ -13,12 +14,15 @@ module Projects
key key
end end
# rubocop: enable CodeReuse/ActiveRecord
private private
def accessible_keys def find_accessible_key(key_id)
current_user.accessible_deploy_keys if current_user.admin?
DeployKey.find_by_id(key_id)
else
current_user.accessible_deploy_keys.find_by_id(key_id)
end
end end
end end
end end
...@@ -45,7 +45,7 @@ module Todos ...@@ -45,7 +45,7 @@ module Todos
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def remove_confidential_issue_todos def remove_confidential_issue_todos
Todo.where( Todo.where(
target_id: confidential_issues.select(:id), target_type: Issue, user_id: user.id target_id: confidential_issues.select(:id), target_type: Issue.name, user_id: user.id
).delete_all ).delete_all
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -14,9 +14,9 @@ module Todos ...@@ -14,9 +14,9 @@ module Todos
def execute def execute
ProjectFeature.where(project_id: project_ids).each do |project_features| ProjectFeature.where(project_id: project_ids).each do |project_features|
target_types = [] target_types = []
target_types << Issue if private?(project_features.issues_access_level) target_types << Issue.name if private?(project_features.issues_access_level)
target_types << MergeRequest if private?(project_features.merge_requests_access_level) target_types << MergeRequest.name if private?(project_features.merge_requests_access_level)
target_types << Commit if private?(project_features.repository_access_level) target_types << Commit.name if private?(project_features.repository_access_level)
next if target_types.empty? next if target_types.empty?
......
...@@ -28,7 +28,7 @@ module Users ...@@ -28,7 +28,7 @@ module Users
identity_attrs = params.slice(:extern_uid, :provider) identity_attrs = params.slice(:extern_uid, :provider)
if identity_attrs.any? unless identity_attrs.empty?
user.identities.build(identity_attrs) user.identities.build(identity_attrs)
end end
......
- @hide_top_links = true - @hide_top_links = true
- page_title _("Issues") - page_title _("Issues")
- @breadcrumb_link = issues_dashboard_path(assignee_id: current_user.id) - @breadcrumb_link = issues_dashboard_path(assignee_username: current_user.username)
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues") = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.nav-controls .nav-controls
= render 'shared/issuable/feed_buttons' = render 'shared/issuable/feed_buttons'
= render 'shared/issuable/filter', type: :issues = render 'shared/issuable/search_bar', type: :issues
- if current_user && @no_filters_set - if current_user && @no_filters_set
= render 'shared/dashboard/no_filter_selected' = render 'shared/dashboard/no_filter_selected'
......
- @hide_top_links = true - @hide_top_links = true
- page_title _("Merge Requests") - page_title _("Merge Requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id) - @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username)
.page-title-holder .page-title-holder
%h1.page-title= _('Merge Requests') %h1.page-title= _('Merge Requests')
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
.top-area .top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set = render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set
= render 'shared/issuable/filter', type: :merge_requests = render 'shared/issuable/search_bar', type: :merge_requests
- if current_user && @no_filters_set - if current_user && @no_filters_set
= render 'shared/dashboard/no_filter_selected' = render 'shared/dashboard/no_filter_selected'
......
- if form_based_providers.any? - if form_based_providers.any?
- if crowd_enabled? - if crowd_enabled?
.login-box.tab-pane.active{ id: "crowd", role: 'tabpanel' } .login-box.tab-pane{ id: "crowd", role: 'tabpanel', class: active_when(form_based_auth_provider_has_active_class?(:crowd)) }
.login-body .login-body
= render 'devise/sessions/new_crowd' = render 'devise/sessions/new_crowd'
- @ldap_servers.each_with_index do |server, i| - @ldap_servers.each_with_index do |server, i|
.login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && !crowd_enabled?) } .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain)) }
.login-body .login-body
= render 'devise/sessions/new_ldap', server: server = render 'devise/sessions/new_ldap', server: server
- if password_authentication_enabled_for_web? - if password_authentication_enabled_for_web?
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
.login-body .login-body
= render 'devise/sessions/new_base' = render 'devise/sessions/new_base'
= render_if_exists 'devise/sessions/new_smartcard'
- elsif password_authentication_enabled_for_web? - elsif password_authentication_enabled_for_web?
.login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' } .login-box.tab-pane.active{ id: 'login-pane', role: 'tabpanel' }
.login-body .login-body
......
%ul.nav-links.new-session-tabs.nav-tabs.nav{ class: ('custom-provider-tabs' if form_based_providers.any?) } %ul.nav-links.new-session-tabs.nav-tabs.nav{ class: ('custom-provider-tabs' if form_based_providers.any?) }
- if crowd_enabled? - if crowd_enabled?
%li.nav-item %li.nav-item
= link_to "Crowd", "#crowd", class: 'nav-link active', 'data-toggle' => 'tab' = link_to "Crowd", "#crowd", class: "nav-link #{active_when(form_based_auth_provider_has_active_class?(:crowd))}", 'data-toggle' => 'tab'
- @ldap_servers.each_with_index do |server, i| - @ldap_servers.each_with_index do |server, i|
%li.nav-item %li.nav-item
= link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && !crowd_enabled?)} qa-ldap-tab", 'data-toggle' => 'tab' = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i.zero? && form_based_auth_provider_has_active_class?(:ldapmain))} qa-ldap-tab", 'data-toggle' => 'tab'
= render_if_exists 'devise/shared/tab_smartcard'
- if password_authentication_enabled_for_web? - if password_authentication_enabled_for_web?
%li.nav-item %li.nav-item
= link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab' = link_to 'Standard', '#login-pane', class: 'nav-link qa-standard-tab', 'data-toggle' => 'tab'
......
.issues-filters
.issues-details-filters.row-content-block.second-block
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do
- if params[:search].present?
= hidden_field_tag :search, params[:search]
.issues-other-filters
.filter-item.inline
- if params[:author_id].present?
= hidden_field_tag(:author_id, params[:author_id])
= dropdown_tag(user_dropdown_label(params[:author_id], "Author"), options: { toggle_class: "js-user-search js-filter-submit js-author-search", title: "Filter by author", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit",
placeholder: "Search authors", data: { any_user: "Any Author", first_user: current_user&.username, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:author_id], field_name: "author_id", default_label: "Author" } })
.filter-item.inline
- if params[:assignee_id].present?
= hidden_field_tag(:assignee_id, params[:assignee_id])
= dropdown_tag(user_dropdown_label(params[:assignee_id], "Assignee"), options: { toggle_class: "js-user-search js-filter-submit js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: current_user&.username, null_user: true, current_user: true, project_id: @project&.id, group_id: @group&.id, selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter
= render "shared/issuable/milestone_dropdown", selected: finder.milestones.try(:first), name: :milestone_title, show_any: true, show_upcoming: true, show_started: true
.filter-item.inline.labels-filter
= render "shared/issuable/label_dropdown", selected: selected_labels, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" }
- unless @no_filters_set
.float-right
= render 'shared/sort_dropdown'
- has_labels = @labels && @labels.any?
.row-content-block.second-block.filtered-labels{ class: ("hidden" unless has_labels) }
- if has_labels
= render 'shared/labels_row', labels: @labels
- type = local_assigns.fetch(:type) - type = local_assigns.fetch(:type)
- board = local_assigns.fetch(:board, nil) - board = local_assigns.fetch(:board, nil)
- block_css_class = type != :boards_modal ? 'row-content-block second-block' : '' - block_css_class = type != :boards_modal ? 'row-content-block second-block' : ''
- full_path = @project.present? ? @project.full_path : @group.full_path
- user_can_admin_list = board && can?(current_user, :admin_list, board.parent) - user_can_admin_list = board && can?(current_user, :admin_list, board.parent)
- show_sorting_dropdown = local_assigns.fetch(:show_sorting_dropdown, true) - show_sorting_dropdown = local_assigns.fetch(:show_sorting_dropdown, true)
...@@ -10,7 +9,7 @@ ...@@ -10,7 +9,7 @@
- if type == :boards - if type == :boards
#js-multiple-boards-switcher.inline.boards-switcher{ "v-cloak" => true } #js-multiple-boards-switcher.inline.boards-switcher{ "v-cloak" => true }
= render_if_exists "shared/boards/switcher", board: board = render_if_exists "shared/boards/switcher", board: board
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search]), method: :get, class: 'filter-form js-filter-form' do = form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form' do
- if params[:search].present? - if params[:search].present?
= hidden_field_tag :search, params[:search] = hidden_field_tag :search, params[:search]
- if @can_bulk_update - if @can_bulk_update
...@@ -25,7 +24,7 @@ ...@@ -25,7 +24,7 @@
dropdown_class: "filtered-search-history-dropdown", dropdown_class: "filtered-search-history-dropdown",
content_class: "filtered-search-history-dropdown-content", content_class: "filtered-search-history-dropdown-content",
title: "Recent searches" }) do title: "Recent searches" }) do
.js-filtered-search-history-dropdown{ data: { full_path: full_path } } .js-filtered-search-history-dropdown{ data: { full_path: search_history_storage_prefix } }
.filtered-search-box-input-container.droplab-dropdown .filtered-search-box-input-container.droplab-dropdown
.scroll-container .scroll-container
%ul.tokens-container.list-unstyled %ul.tokens-container.list-unstyled
......
---
title: Don't show Memory Usage for unmerged MRs
merge_request:
author:
type: changed
---
title: Chat message push notifications now include links back to GitLab branches
merge_request: 22651
author: Tony Castrogiovanni
type: added
---
title: Use search bar for filtering in dashboard issues / MRs
merge_request: 22641
author: Heinrich Lee Yu
type: changed
---
title: Update asana to 0.8.1
merge_request: 23039
author: Takuya Noguchi
type: other
---
title: Update asciidoctor to 1.5.8
merge_request: 23047
author: Takuya Noguchi
type: other
---
title: Fixes an issue where default values from models would override values set in
the interface (e.g. users would be set to external even though their emails matches
the internal email address pattern)
merge_request: 23114
author:
type: fixed
---
title: Updated Gitaly to v0.133.0
merge_request: 23148
author:
type: other
---
title: Auto DevOps support for Group Security Dashboard
merge_request: 23165
author:
type: fixed
---
title: Fix not render emoji in filter dropdown
merge_request: 23112
author: Hiroyuki Sato
type: fixed
---
title: Fix typo in notebook props
merge_request: 23103
author: George Tsiolis
type: other
---
title: 'Rails5: Passing a class as a value in an Active Record query is deprecated'
merge_request: 23164
author: Jasper Maes
type: other
---
title: Bump nokogiri, loofah, and rack gems for security updates
merge_request: 23204
author:
type: security
---
title: Fix enabling project deploy key for admins
merge_request: 23043
author:
type: fixed
---
title: Improve memory performance by reducing dirty pages after fork()
merge_request: 23169
author:
type: performance
This diff is collapsed.
...@@ -294,7 +294,7 @@ runners will not use regular runners, they must be tagged accordingly. ...@@ -294,7 +294,7 @@ runners will not use regular runners, they must be tagged accordingly.
[jobs]: #jobs [jobs]: #jobs
[jobs-yaml]: yaml/README.md#jobs [jobs-yaml]: yaml/README.md#jobs
[manual]: yaml/README.md#manual [manual]: yaml/README.md#whenmanual
[env-manual]: environments.md#manually-deploying-to-environments [env-manual]: environments.md#manually-deploying-to-environments
[stages]: yaml/README.md#stages [stages]: yaml/README.md#stages
[runners]: runners/README.html [runners]: runners/README.html
......
...@@ -1604,10 +1604,11 @@ test: ...@@ -1604,10 +1604,11 @@ test:
## `include` ## `include`
> Introduced in [GitLab Edition Premium][ee] 10.5. > Introduced in [GitLab Premium](https://about.gitlab.com/pricing/) 10.5.
> Available for Starter, Premium and Ultimate [versions][gitlab-versions] since 10.6. > Available for Starter, Premium and Ultimate since 10.6.
> Behaviour expanded in GitLab 10.8 to allow more flexible overriding. > Behaviour expanded in GitLab 10.8 to allow more flexible overriding.
> Available for Libre since [11.4](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21603) > [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21603)
to GitLab Core in 11.4
Using the `include` keyword, you can allow the inclusion of external YAML files. Using the `include` keyword, you can allow the inclusion of external YAML files.
......
...@@ -104,7 +104,7 @@ features of GitLab work with MySQL/MariaDB: ...@@ -104,7 +104,7 @@ features of GitLab work with MySQL/MariaDB:
1. MySQL support for subgroups was [dropped with GitLab 9.3][post]. 1. MySQL support for subgroups was [dropped with GitLab 9.3][post].
See [issue #30472][30472] for more information. See [issue #30472][30472] for more information.
1. Geo does [not support MySQL](https://docs.gitlab.com/ee/administration/geo/replication/database.html#mysql-replication). This means no supported Disaster Recovery solution if using MySQL. **[PREMIUM ONLY]** 1. Geo does [not support MySQL](https://docs.gitlab.com/ee/administration/geo/replication/database.html#mysql-replication). This means no supported Disaster Recovery solution if using MySQL. **[PREMIUM ONLY]**
1. [Zero downtime migrations][../update/README.md#upgrading-without-downtime] do not work with MySQL. 1. [Zero downtime migrations](../update/README.md#upgrading-without-downtime) do not work with MySQL.
1. GitLab [optimizes the loading of dashboard events](https://gitlab.com/gitlab-org/gitlab-ce/issues/31806) using [PostgreSQL LATERAL JOINs](https://blog.heapanalytics.com/postgresqls-powerful-new-join-type-lateral/). 1. GitLab [optimizes the loading of dashboard events](https://gitlab.com/gitlab-org/gitlab-ce/issues/31806) using [PostgreSQL LATERAL JOINs](https://blog.heapanalytics.com/postgresqls-powerful-new-join-type-lateral/).
1. In general, SQL optimized for PostgreSQL may run much slower in MySQL due to 1. In general, SQL optimized for PostgreSQL may run much slower in MySQL due to
differences in query planners. For example, subqueries that work well in PostgreSQL differences in query planners. For example, subqueries that work well in PostgreSQL
......
...@@ -657,6 +657,8 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac ...@@ -657,6 +657,8 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. | | `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. |
| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. | | `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. |
| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. | | `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. |
| `OLD_REPORTS_DISABLED` | From GitLab 11.5, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
| `NEW_REPORTS_DISABLED` | From GitLab 11.5, this variable can be used to disable the `sast_dashboard` job. If the variable is present, the job will not be created. |
TIP: **Tip:** TIP: **Tip:**
Set up the replica variables using a Set up the replica variables using a
......
...@@ -228,7 +228,7 @@ twice, which can lead to confusion during deployments. ...@@ -228,7 +228,7 @@ twice, which can lead to confusion during deployments.
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) | | [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
| [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) | | [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found at [Nurtch Documentation](http://docs.nurtch.com/en/latest). **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) | | [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found at [Nurtch Documentation](http://docs.nurtch.com/en/latest). **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
| [Knative](https://cloud.google.com/knative) | 0.1.2 | Knative provides a platform to create, deploy, and manage serverless workloads from a Kubernetes cluster. It is used in conjunction with, and includes [Istio](https://istio.io) to provide an external IP address for all programs hosted by Knative. You will be prompted to enter a wildcard domain where your applications will be exposed. Configure your DNS server to use the external IP address for that domain. For any application created and installed, they will be accessible as `<program_name>.<kubernetes_namespace>.<domain_name>`. **Note**: This will require your kubernetes cluster to have RBAC enabled. | [knative/knative](https://storage.googleapis.com/triggermesh-charts) | [Knative](https://cloud.google.com/knative) | 11.5+ | Knative provides a platform to create, deploy, and manage serverless workloads from a Kubernetes cluster. It is used in conjunction with, and includes [Istio](https://istio.io) to provide an external IP address for all programs hosted by Knative. You will be prompted to enter a wildcard domain where your applications will be exposed. Configure your DNS server to use the external IP address for that domain. For any application created and installed, they will be accessible as `<program_name>.<kubernetes_namespace>.<domain_name>`. This will require your kubernetes cluster to have [RBAC enabled](#role-based-access-control-rbac). | [knative/knative](https://storage.googleapis.com/triggermesh-charts)
NOTE: **Note:** NOTE: **Note:**
As of GitLab 11.6 Helm Tiller will be upgraded to the latest version supported As of GitLab 11.6 Helm Tiller will be upgraded to the latest version supported
...@@ -267,6 +267,9 @@ the `gcloud` command in a local terminal or using the **Cloud Shell**. ...@@ -267,6 +267,9 @@ the `gcloud` command in a local terminal or using the **Cloud Shell**.
If the cluster is not on GKE, follow the specific instructions for your If the cluster is not on GKE, follow the specific instructions for your
Kubernetes provider to configure `kubectl` with the right credentials. Kubernetes provider to configure `kubectl` with the right credentials.
The output of the following examples will show the external IP address of your
cluster. This information can then be used to set up DNS entries and forwarding
rules that allow external access to your deployed applications.
If you installed the Ingress [via the **Applications**](#installing-applications), If you installed the Ingress [via the **Applications**](#installing-applications),
run the following command: run the following command:
...@@ -275,28 +278,23 @@ run the following command: ...@@ -275,28 +278,23 @@ run the following command:
kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
``` ```
NOTE: **Note:** For Istio/Knative, the command will be different:
For Istio/Knative, use the following command:
```bash ```bash
kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} ' kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
``` ```
Otherwise, you can list the IP addresses of all load balancers: Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
```bash ```bash
kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} ' kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}".
``` ```
> **Note**: Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run: Otherwise, you can list the IP addresses of all load balancers:
> ```bash
> kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}".
> ```
The output is the external IP address of your cluster. This information can then ```bash
be used to set up DNS entries and forwarding rules that allow external access to kubectl get svc --all-namespaces -o jsonpath='{range.items[?(@.status.loadBalancer.ingress)]}{.status.loadBalancer.ingress[*].ip} '
your deployed applications. ```
### Using a static IP ### Using a static IP
......
...@@ -10,8 +10,8 @@ Historically, runbooks took the form of a decision tree or a detailed ...@@ -10,8 +10,8 @@ Historically, runbooks took the form of a decision tree or a detailed
step-by-step guide depending on the condition or system. step-by-step guide depending on the condition or system.
Modern implementations have introduced the concept of an "executable Modern implementations have introduced the concept of an "executable
runbooks", where along with a well define process, operators can execute runbooks", where, along with a well-defined process, operators can execute
code blocks or database queries against a given environment. pre-written code blocks or database queries against a given environment.
## Nurtch Executable Runbooks ## Nurtch Executable Runbooks
...@@ -45,5 +45,93 @@ To create an executable runbook, you will need: ...@@ -45,5 +45,93 @@ To create an executable runbook, you will need:
Nurtch is the company behind the [Rubix library](https://github.com/Nurtch/rubix). Rubix is Nurtch is the company behind the [Rubix library](https://github.com/Nurtch/rubix). Rubix is
an open-source python library that makes it easy to perform common DevOps tasks inside Jupyter Notebooks. an open-source python library that makes it easy to perform common DevOps tasks inside Jupyter Notebooks.
Tasks such as plotting Cloudwatch metrics and rolling your ECS/Kubernetes app are simplified Tasks such as plotting Cloudwatch metrics and rolling your ECS/Kubernetes app are simplified
down to a couple of lines of code. Check the [Nurtch Documentation](http://docs.nurtch.com/en/latest) down to a couple of lines of code. See the [Nurtch Documentation](http://docs.nurtch.com/en/latest)
for more information. for more information.
## Configure an executable runbook with GitLab
Follow this step-by-step guide to configure an executable runbook in GitLab using
the components outlined above and the preloaded demo runbook.
### 1. Add a Kubernetes cluster
Follow the steps outlined in [Adding and creating a new GKE cluster via GitLab](https://docs.gitlab.com/ee/user/project/clusters/#adding-and-creating-a-new-gke-cluster-via-gitlab)
to add a Kubernetes cluster to your project.
### 2. Install Helm Tiller, Ingress, and JupyterHub
Once the cluster has been provisioned in GKE, click the **Install** button next to the **Helm Tiller** app.
![install helm](img/helm-install.png)
Once Tiller has been installed successfully, click the **Install** button next to the **Ingress** app.
![install ingress](img/ingress-install.png)
Once Ingress has been installed successfully, click the **Install** button next to the **JupyterHub** app.
![install jupyterhub](img/jupyterhub-install.png)
### 3. Login to JupyterHub and start the server
Once JupyterHub has been installed successfully, navigate to the displayed **Jupyter Hostname** URL and click
**Sign in with GitLab**. Authentication is automatically enabled for any user of the GitLab instance via OAuth2. This
will redirect to GitLab in order to authorize JupyterHub to use your GitLab account. Click **Authorize**.
![authorize jupyter](img/authorize-jupyter.png)
Once the application has been authorized you will taken back to the JupyterHub application. Click **Start My Server**.
The server will take a couple of seconds to start.
### 4. Configure access
In order for the runbook to access your GitLab project, you will need to enter a
[GitLab Access Token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html)
as well as your Project ID in the **Setup** section of the demo runbook.
Double-click the **DevOps-Runbook-Demo** folder located on the left panel.
![demo runbook](img/demo-runbook.png)
Double-click the "Nurtch-DevOps-Demo.ipynb" runbook.
![sample runbook](img/sample-runbook.png)
The contents on the runbook will be displayed on the right side of the screen. Under the "Setup" section, you will find
entries for both your `PRIVATE_TOKEN` and your `PROJECT_ID`. Enter both these values, conserving the single quotes as follows:
```sql
PRIVATE_TOKEN = 'n671WNGecHugsdEDPsyo'
PROJECT_ID = '1234567'
```
Update the `VARIABLE_NAME` on the last line of this section to match the name of the variable you are using for your
access token. In this example our variable name is `PRIVATE_TOKEN`.
```sql
VARIABLE_VALUE = project.variables.get('PRIVATE_TOKEN').value
```
### 5. Configure an operation
For this example we'll use the "**Run SQL queries in Notebook**" section in the sample runbook to query
a postgres database. The first 4 lines of the section define the variables that are required for this query to function.
```sql
%env DB_USER={project.variables.get('DB_USER').value}
%env DB_PASSWORD={project.variables.get('DB_PASSWORD').value}
%env DB_ENDPOINT={project.variables.get('DB_ENDPOINT').value}
%env DB_NAME={project.variables.get('DB_NAME').value}
```
Create the matching variables in your project's **Settings >> CI/CD >> Variables**
![gitlab variables](img/gitlab-variables.png)
Back in Jupyter, click the "Run SQL queries in Notebook" heading and the click *Run*. The results will be
displayed in-line as follows:
![postgres query](img/postgres-query.png)
You can try other operations such as running shell scripts or interacting with a Kubernetes cluster. Visit the
[Nurtch Documentation](http://docs.nurtch.com/) for more information.
\ No newline at end of file
...@@ -177,11 +177,11 @@ administrator to do so. ...@@ -177,11 +177,11 @@ administrator to do so.
### Adding patches when creating a merge request via e-mail ### Adding patches when creating a merge request via e-mail
> **Note**: This feature was [implemented in GitLab 11.5](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22723) > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22723) in GitLab 11.5.
You can add commits to the merge request being created by adding You can add commits to the merge request being created by adding
patches as attachments to the email, all attachments with a filename patches as attachments to the email. All attachments with a filename
ending in `.patch` will be considered patches. The patches will be processed ending in `.patch` will be considered patches and they will be processed
ordered by name. ordered by name.
The combined size of the patches can be 2MB. The combined size of the patches can be 2MB.
...@@ -194,7 +194,7 @@ branch already exists, the patches will be applied on top of it. ...@@ -194,7 +194,7 @@ branch already exists, the patches will be applied on top of it.
## Find the merge request that introduced a change ## Find the merge request that introduced a change
> **Note**: this feature was [implemented in GitLab 10.5](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383). > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383) in GitLab 10.5.
When viewing the commit details page, GitLab will link to the merge request (or When viewing the commit details page, GitLab will link to the merge request (or
merge requests, if it's in more than one) containing that commit. merge requests, if it's in more than one) containing that commit.
...@@ -231,9 +231,10 @@ have been marked as a **Work In Progress**. ...@@ -231,9 +231,10 @@ have been marked as a **Work In Progress**.
## Merge request diff file navigation ## Merge request diff file navigation
The diff view has a file tree for file navigation. As you scroll through When reviewing changes in the **Changes** tab the diff can be navigated using
diffs with a large number of files, you can easily jump to any changed file the file tree or file list. As you scroll through large diffs with many
using the file tree. changes, you can quickly jump to any changed file using the file tree or file
list.
![Merge request diff file navigation](img/merge_request_diff_file_navigation.png) ![Merge request diff file navigation](img/merge_request_diff_file_navigation.png)
......
...@@ -2,27 +2,27 @@ ...@@ -2,27 +2,27 @@
## Issues and merge requests ## Issues and merge requests
To search through issues and merge requests in multiple projects, you can use the left-sidebar. To search through issues and merge requests in multiple projects, you can use the **Issues** or **Merge Requests** links
in the top-right part of your screen.
Click the menu bar, then **Issues** or **Merge Requests**, which work in the same way, Both of them work in the same way, therefore, the following notes are valid for both.
therefore, the following notes are valid for both.
The number displayed on their right represents the number of issues and merge requests assigned to you. The number displayed on their right represents the number of issues and merge requests assigned to you.
![menu bar - issues and MRs assigned to you](img/left_menu_bar.png) ![issues and MRs dashboard links](img/dashboard_links.png)
When you click **Issues**, you'll see the opened issues assigned to you straight away: When you click **Issues**, you'll see the opened issues assigned to you straight away:
![Issues assigned to you](img/issues_assigned_to_you.png) ![Issues assigned to you](img/issues_assigned_to_you.png)
You can filter them by **Author**, **Assignee**, **Milestone**, and **Labels**, You can search through **Open**, **Closed**, or **All** issues.
searching through **Open**, **Closed**, and **All** issues.
Of course, you can combine all filters together. You can also filter the results using the search and filter field. This works in the same way as the ones found in the
per project pages described below.
### Issues and MRs assigned to you or created by you ### Issues and MRs assigned to you or created by you
You'll find a shortcut to issues and merge requests create by you or assigned to you You'll also find shortcuts to issues and merge requests created by you or assigned to you
on the search field on the top-right of your screen: on the search field on the top-right of your screen:
![shortcut to your issues and mrs](img/issues_mrs_shortcut.png) ![shortcut to your issues and mrs](img/issues_mrs_shortcut.png)
......
...@@ -53,4 +53,8 @@ module Gitlab ...@@ -53,4 +53,8 @@ module Gitlab
def self.pre_release? def self.pre_release?
VERSION.include?('pre') VERSION.include?('pre')
end end
def self.version_info
Gitlab::VersionInfo.parse(Gitlab::VERSION)
end
end end
...@@ -19,6 +19,15 @@ ...@@ -19,6 +19,15 @@
# * review: REVIEW_DISABLED # * review: REVIEW_DISABLED
# * stop_review: REVIEW_DISABLED # * stop_review: REVIEW_DISABLED
# #
# The sast and sast_dashboard jobs are executed to guarantee full compatibility
# with the group security dashboard and the security reports with old runners.
# If you use only runners with version 11.5 or above, you can disable the sast
# job by setting the OLD_REPORTS_DISABLED environment variable. If you use only
# runners with version below 11.5, you can disable the sast_dashboard job by
# setting the NEW_REPORTS_DISABLED environment variable.
# The sast_dashboard job will be removed in the future, when the sast job will
# use the new reports syntax.
#
# In order to deploy, you must have a Kubernetes cluster configured either # In order to deploy, you must have a Kubernetes cluster configured either
# via a project integration, or via group/project variables. # via a project integration, or via group/project variables.
# AUTO_DEVOPS_DOMAIN must also be set as a variable at the group or project # AUTO_DEVOPS_DOMAIN must also be set as a variable at the group or project
...@@ -173,6 +182,29 @@ sast: ...@@ -173,6 +182,29 @@ sast:
except: except:
variables: variables:
- $SAST_DISABLED - $SAST_DISABLED
- $OLD_REPORTS_DISABLED
sast_dashboard:
stage: test
image: docker:stable
allow_failure: true
services:
- docker:stable-dind
script:
- setup_docker
- sast
artifacts:
reports:
sast: gl-sast-report.json
only:
refs:
- branches
variables:
- $GITLAB_FEATURES =~ /\bsast\b/
except:
variables:
- $SAST_DISABLED
- $NEW_REPORTS_DISABLED
dependency_scanning: dependency_scanning:
stage: test stage: test
......
...@@ -6,8 +6,8 @@ module Gitlab ...@@ -6,8 +6,8 @@ module Gitlab
class Collection class Collection
class Item class Item
def initialize(key:, value:, public: true, file: false) def initialize(key:, value:, public: true, file: false)
raise ArgumentError, "`value` must be of type String, while it was: #{value.class}" unless raise ArgumentError, "`#{key}` must be of type String, while it was: #{value.class}" unless
value.is_a?(String) || value.nil? value.is_a?(String)
@variable = { @variable = {
key: key, value: value, public: public, file: file key: key, value: value, public: public, file: file
......
...@@ -18,6 +18,10 @@ module Gitlab ...@@ -18,6 +18,10 @@ module Gitlab
match[:id].to_i match[:id].to_i
end end
def user_ids_for_emails(emails)
emails.map { |email| user_id_for_email(email) }.compact.uniq
end
def for_user(user) def for_user(user)
hostname = Gitlab::CurrentSettings.current_application_settings.commit_email_hostname hostname = Gitlab::CurrentSettings.current_application_settings.commit_email_hostname
......
...@@ -76,6 +76,17 @@ module QA ...@@ -76,6 +76,17 @@ module QA
} }
end end
def self.fabricate_or_use(username, password)
if Runtime::Env.signup_disabled?
self.new.tap do |user|
user.username = username
user.password = password
end
else
self.fabricate!
end
end
private private
def fetch_id(username) def fetch_id(username)
......
...@@ -75,6 +75,22 @@ module QA ...@@ -75,6 +75,22 @@ module QA
ENV['GITLAB_FORKER_PASSWORD'] ENV['GITLAB_FORKER_PASSWORD']
end end
def gitlab_qa_username_1
ENV['GITLAB_QA_USERNAME_1'] || 'gitlab-qa-user1'
end
def gitlab_qa_password_1
ENV['GITLAB_QA_PASSWORD_1']
end
def gitlab_qa_username_2
ENV['GITLAB_QA_USERNAME_2'] || 'gitlab-qa-user2'
end
def gitlab_qa_password_2
ENV['GITLAB_QA_PASSWORD_2']
end
def ldap_username def ldap_username
ENV['GITLAB_LDAP_USERNAME'] ENV['GITLAB_LDAP_USERNAME']
end end
......
...@@ -7,7 +7,7 @@ module QA ...@@ -7,7 +7,7 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login) Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials) Page::Main::Login.perform(&:sign_in_using_credentials)
user = Resource::User.fabricate! user = Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1)
project = Resource::Project.fabricate! do |resource| project = Resource::Project.fabricate! do |resource|
resource.name = 'add-member-project' resource.name = 'add-member-project'
......
...@@ -650,7 +650,7 @@ describe ApplicationController do ...@@ -650,7 +650,7 @@ describe ApplicationController do
describe '#access_denied' do describe '#access_denied' do
controller(described_class) do controller(described_class) do
def index def index
access_denied!(params[:message]) access_denied!(params[:message], params[:status])
end end
end end
...@@ -669,6 +669,12 @@ describe ApplicationController do ...@@ -669,6 +669,12 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(403) expect(response).to have_gitlab_http_status(403)
end end
it 'renders a status passed to access denied' do
get :index, status: 401
expect(response).to have_gitlab_http_status(401)
end
end end
context 'when invalid UTF-8 parameters are received' do context 'when invalid UTF-8 parameters are received' do
......
...@@ -60,7 +60,7 @@ describe IssuableCollections do ...@@ -60,7 +60,7 @@ describe IssuableCollections do
end end
end end
describe '#filter_params' do describe '#finder_options' do
let(:params) do let(:params) do
{ {
assignee_id: '1', assignee_id: '1',
...@@ -84,25 +84,20 @@ describe IssuableCollections do ...@@ -84,25 +84,20 @@ describe IssuableCollections do
} }
end end
it 'filters params' do it 'only allows whitelisted params' do
allow(controller).to receive(:cookies).and_return({}) allow(controller).to receive(:cookies).and_return({})
filtered_params = controller.send(:filter_params) finder_options = controller.send(:finder_options)
expect(filtered_params).to eq({ expect(finder_options).to eq({
'assignee_id' => '1', 'assignee_id' => '1',
'assignee_username' => 'user1', 'assignee_username' => 'user1',
'author_id' => '2', 'author_id' => '2',
'author_username' => 'user2', 'author_username' => 'user2',
'authorized_only' => 'true',
'due_date' => '2017-01-01',
'group_id' => '3',
'iids' => '4',
'label_name' => 'foo', 'label_name' => 'foo',
'milestone_title' => 'bar', 'milestone_title' => 'bar',
'my_reaction_emoji' => 'thumbsup', 'my_reaction_emoji' => 'thumbsup',
'non_archived' => 'true', 'due_date' => '2017-01-01',
'project_id' => '5',
'scope' => 'all', 'scope' => 'all',
'search' => 'baz', 'search' => 'baz',
'sort' => 'priority', 'sort' => 'priority',
......
...@@ -27,12 +27,8 @@ describe Projects::DeployKeysController do ...@@ -27,12 +27,8 @@ describe Projects::DeployKeysController do
let(:project2) { create(:project, :internal)} let(:project2) { create(:project, :internal)}
let(:project_private) { create(:project, :private)} let(:project_private) { create(:project, :private)}
let(:deploy_key_internal) do let(:deploy_key_internal) { create(:deploy_key) }
create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com') let(:deploy_key_actual) { create(:deploy_key) }
end
let(:deploy_key_actual) do
create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com')
end
let!(:deploy_key_public) { create(:deploy_key, public: true) } let!(:deploy_key_public) { create(:deploy_key, public: true) }
let!(:deploy_keys_project_internal) do let!(:deploy_keys_project_internal) do
...@@ -63,4 +59,145 @@ describe Projects::DeployKeysController do ...@@ -63,4 +59,145 @@ describe Projects::DeployKeysController do
end end
end end
end end
describe '/enable/:id' do
let(:deploy_key) { create(:deploy_key) }
let(:project2) { create(:project) }
let!(:deploy_keys_project_internal) do
create(:deploy_keys_project, project: project2, deploy_key: deploy_key)
end
context 'with anonymous user' do
before do
sign_out(:user)
end
it 'redirects to login' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.not_to change { DeployKeysProject.count }
expect(response).to have_http_status(302)
expect(response).to redirect_to(new_user_session_path)
end
end
context 'with user with no permission' do
before do
sign_in(create(:user))
end
it 'returns 404' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.not_to change { DeployKeysProject.count }
expect(response).to have_http_status(404)
end
end
context 'with user with permission' do
before do
project2.add_maintainer(user)
end
it 'returns 302' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.to change { DeployKeysProject.count }.by(1)
expect(DeployKeysProject.where(project_id: project.id, deploy_key_id: deploy_key.id).count).to eq(1)
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
end
it 'returns 404' do
put :enable, id: 0, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(404)
end
end
context 'with admin' do
before do
sign_in(create(:admin))
end
it 'returns 302' do
expect do
put :enable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.to change { DeployKeysProject.count }.by(1)
expect(DeployKeysProject.where(project_id: project.id, deploy_key_id: deploy_key.id).count).to eq(1)
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
end
end
end
describe '/disable/:id' do
let(:deploy_key) { create(:deploy_key) }
let!(:deploy_key_project) { create(:deploy_keys_project, project: project, deploy_key: deploy_key) }
context 'with anonymous user' do
before do
sign_out(:user)
end
it 'redirects to login' do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(302)
expect(response).to redirect_to(new_user_session_path)
expect(DeployKey.find(deploy_key.id)).to eq(deploy_key)
end
end
context 'with user with no permission' do
before do
sign_in(create(:user))
end
it 'returns 404' do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(404)
expect(DeployKey.find(deploy_key.id)).to eq(deploy_key)
end
end
context 'with user with permission' do
it 'returns 302' do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
expect { DeployKey.find(deploy_key.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'returns 404' do
put :disable, id: 0, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(404)
end
end
context 'with admin' do
before do
sign_in(create(:admin))
end
it 'returns 302' do
expect do
put :disable, id: deploy_key.id, namespace_id: project.namespace, project_id: project
end.to change { DeployKey.count }.by(-1)
expect(response).to have_http_status(302)
expect(response).to redirect_to(namespace_project_settings_repository_path(anchor: 'js-deploy-keys-settings'))
expect { DeployKey.find(deploy_key.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end end
...@@ -98,7 +98,7 @@ describe RootController do ...@@ -98,7 +98,7 @@ describe RootController do
it 'redirects to their assigned issues' do it 'redirects to their assigned issues' do
get :index get :index
expect(response).to redirect_to issues_dashboard_path(assignee_id: user.id) expect(response).to redirect_to issues_dashboard_path(assignee_username: user.username)
end end
end end
...@@ -110,7 +110,7 @@ describe RootController do ...@@ -110,7 +110,7 @@ describe RootController do
it 'redirects to their assigned merge requests' do it 'redirects to their assigned merge requests' do
get :index get :index
expect(response).to redirect_to merge_requests_dashboard_path(assignee_id: user.id) expect(response).to redirect_to merge_requests_dashboard_path(assignee_username: user.username)
end end
end end
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
FactoryBot.define do FactoryBot.define do
factory :cluster_kubernetes_namespace, class: Clusters::KubernetesNamespace do factory :cluster_kubernetes_namespace, class: Clusters::KubernetesNamespace do
association :cluster, :project, :provided_by_gcp association :cluster, :project, :provided_by_gcp
namespace { |n| "environment#{n}" }
after(:build) do |kubernetes_namespace| after(:build) do |kubernetes_namespace|
cluster_project = kubernetes_namespace.cluster.cluster_project cluster_project = kubernetes_namespace.cluster.cluster_project
......
...@@ -130,7 +130,7 @@ describe "Admin::Users" do ...@@ -130,7 +130,7 @@ describe "Admin::Users" do
context 'with regex to match internal user email address set', :js do context 'with regex to match internal user email address set', :js do
before do before do
stub_application_setting(user_default_external: true) stub_application_setting(user_default_external: true)
stub_application_setting(user_default_internal_regex: '.internal@') stub_application_setting(user_default_internal_regex: '\.internal@')
visit new_admin_user_path visit new_admin_user_path
end end
...@@ -169,6 +169,22 @@ describe "Admin::Users" do ...@@ -169,6 +169,22 @@ describe "Admin::Users" do
expects_warning_to_be_hidden expects_warning_to_be_hidden
end end
it 'creates an internal user' do
user_name = 'tester1'
fill_in 'user_email', with: 'test.internal@domain.ch'
fill_in 'user_name', with: 'tester1 name'
fill_in 'user_username', with: user_name
expects_external_to_be_unchecked
expects_warning_to_be_shown
click_button 'Create user'
new_user = User.find_by(username: user_name)
expect(new_user.external).to be_falsy
end
end end
end end
end end
......
...@@ -25,35 +25,35 @@ describe "Dashboard Issues Feed" do ...@@ -25,35 +25,35 @@ describe "Dashboard Issues Feed" do
it "renders atom feed via personal access token" do it "renders atom feed via personal access token" do
personal_access_token = create(:personal_access_token, user: user) personal_access_token = create(:personal_access_token, user: user)
visit issues_dashboard_path(:atom, private_token: personal_access_token.token, assignee_id: user.id) visit issues_dashboard_path(:atom, private_token: personal_access_token.token, assignee_username: user.username)
expect(response_headers['Content-Type']).to have_content('application/atom+xml') expect(response_headers['Content-Type']).to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{user.name} issues") expect(body).to have_selector('title', text: "#{user.name} issues")
end end
it "renders atom feed via feed token" do it "renders atom feed via feed token" do
visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: user.id) visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_username: user.username)
expect(response_headers['Content-Type']).to have_content('application/atom+xml') expect(response_headers['Content-Type']).to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{user.name} issues") expect(body).to have_selector('title', text: "#{user.name} issues")
end end
it "renders atom feed with url parameters" do it "renders atom feed with url parameters" do
visit issues_dashboard_path(:atom, feed_token: user.feed_token, state: 'opened', assignee_id: user.id) visit issues_dashboard_path(:atom, feed_token: user.feed_token, state: 'opened', assignee_username: user.username)
link = find('link[type="application/atom+xml"]') link = find('link[type="application/atom+xml"]')
params = CGI.parse(URI.parse(link[:href]).query) params = CGI.parse(URI.parse(link[:href]).query)
expect(params).to include('feed_token' => [user.feed_token]) expect(params).to include('feed_token' => [user.feed_token])
expect(params).to include('state' => ['opened']) expect(params).to include('state' => ['opened'])
expect(params).to include('assignee_id' => [user.id.to_s]) expect(params).to include('assignee_username' => [user.username.to_s])
end end
context "issue with basic fields" do context "issue with basic fields" do
let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') } let!(:issue2) { create(:issue, author: user, assignees: [assignee], project: project2, description: 'test desc') }
it "renders issue fields" do it "renders issue fields" do
visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: assignee.id) visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_username: assignee.username)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]") entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue2.title}')]")
...@@ -76,7 +76,7 @@ describe "Dashboard Issues Feed" do ...@@ -76,7 +76,7 @@ describe "Dashboard Issues Feed" do
end end
it "renders issue label and milestone info" do it "renders issue label and milestone info" do
visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_id: assignee.id) visit issues_dashboard_path(:atom, feed_token: user.feed_token, assignee_username: assignee.username)
entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]") entry = find(:xpath, "//feed/entry[contains(summary/text(),'#{issue1.title}')]")
......
...@@ -45,11 +45,11 @@ describe 'Navigation bar counter', :use_clean_rails_memory_store_caching do ...@@ -45,11 +45,11 @@ describe 'Navigation bar counter', :use_clean_rails_memory_store_caching do
end end
def issues_path def issues_path
issues_dashboard_path(assignee_id: user.id) issues_dashboard_path(assignee_username: user.username)
end end
def merge_requests_path def merge_requests_path
merge_requests_dashboard_path(assignee_id: user.id) merge_requests_dashboard_path(assignee_username: user.username)
end end
def expect_counters(issuable_type, count) def expect_counters(issuable_type, count)
......
...@@ -2,6 +2,7 @@ require 'spec_helper' ...@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'Dashboard Issues filtering', :js do describe 'Dashboard Issues filtering', :js do
include Spec::Support::Helpers::Features::SortingHelpers include Spec::Support::Helpers::Features::SortingHelpers
include FilteredSearchHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
...@@ -25,27 +26,21 @@ describe 'Dashboard Issues filtering', :js do ...@@ -25,27 +26,21 @@ describe 'Dashboard Issues filtering', :js do
context 'filtering by milestone' do context 'filtering by milestone' do
it 'shows all issues with no milestone' do it 'shows all issues with no milestone' do
show_milestone_dropdown input_filtered_search("milestone:none")
click_link 'No Milestone'
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
it 'shows all issues with the selected milestone' do it 'shows all issues with the selected milestone' do
show_milestone_dropdown input_filtered_search("milestone:%\"#{milestone.title}\"")
page.within '.dropdown-content' do
click_link milestone.title
end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1) expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
it 'updates atom feed link' do it 'updates atom feed link' do
visit_issues(milestone_title: '', assignee_id: user.id) visit_issues(milestone_title: '', assignee_username: user.username)
link = find('.nav-controls a[title="Subscribe to RSS feed"]') link = find('.nav-controls a[title="Subscribe to RSS feed"]')
params = CGI.parse(URI.parse(link[:href]).query) params = CGI.parse(URI.parse(link[:href]).query)
...@@ -54,10 +49,10 @@ describe 'Dashboard Issues filtering', :js do ...@@ -54,10 +49,10 @@ describe 'Dashboard Issues filtering', :js do
expect(params).to include('feed_token' => [user.feed_token]) expect(params).to include('feed_token' => [user.feed_token])
expect(params).to include('milestone_title' => ['']) expect(params).to include('milestone_title' => [''])
expect(params).to include('assignee_id' => [user.id.to_s]) expect(params).to include('assignee_username' => [user.username.to_s])
expect(auto_discovery_params).to include('feed_token' => [user.feed_token]) expect(auto_discovery_params).to include('feed_token' => [user.feed_token])
expect(auto_discovery_params).to include('milestone_title' => ['']) expect(auto_discovery_params).to include('milestone_title' => [''])
expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s]) expect(auto_discovery_params).to include('assignee_username' => [user.username.to_s])
end end
end end
...@@ -66,10 +61,7 @@ describe 'Dashboard Issues filtering', :js do ...@@ -66,10 +61,7 @@ describe 'Dashboard Issues filtering', :js do
let!(:label_link) { create(:label_link, label: label, target: issue) } let!(:label_link) { create(:label_link, label: label, target: issue) }
it 'shows all issues with the selected label' do it 'shows all issues with the selected label' do
page.within '.labels-filter' do input_filtered_search("label:~#{label.title}")
find('.dropdown').click
click_link label.title
end
page.within 'ul.content-list' do page.within 'ul.content-list' do
expect(page).to have_content issue.title expect(page).to have_content issue.title
...@@ -80,12 +72,12 @@ describe 'Dashboard Issues filtering', :js do ...@@ -80,12 +72,12 @@ describe 'Dashboard Issues filtering', :js do
context 'sorting' do context 'sorting' do
before do before do
visit_issues(assignee_id: user.id) visit_issues(assignee_username: user.username)
end end
it 'remembers last sorting value' do it 'remembers last sorting value' do
sort_by('Created date') sort_by('Created date')
visit_issues(assignee_id: user.id) visit_issues(assignee_username: user.username)
expect(find('.issues-filters')).to have_content('Created date') expect(find('.issues-filters')).to have_content('Created date')
end end
...@@ -98,11 +90,6 @@ describe 'Dashboard Issues filtering', :js do ...@@ -98,11 +90,6 @@ describe 'Dashboard Issues filtering', :js do
end end
end end
def show_milestone_dropdown
click_button 'Milestone'
expect(page).to have_selector('.dropdown-content', visible: true)
end
def visit_issues(*args) def visit_issues(*args)
visit issues_dashboard_path(*args) visit issues_dashboard_path(*args)
end end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment