Merge branch 'master' into ide-staged-changes

This commit is contained in:
Phil Hughes 2018-03-27 11:24:06 +01:00
commit 74b77ebf50
No known key found for this signature in database
GPG Key ID: 32245528C52E0F9F
413 changed files with 8796 additions and 2578 deletions

View File

@ -1,20 +1,20 @@
{
"presets": [
["latest", { "es2015": { "modules": false } }],
"stage-2"
],
"presets": [["latest", { "es2015": { "modules": false } }], "stage-2"],
"env": {
"coverage": {
"plugins": [
["istanbul", {
"exclude": [
"spec/javascripts/**/*",
"app/assets/javascripts/locale/**/app.js"
]
}],
["transform-define", {
"process.env.BABEL_ENV": "coverage"
}]
[
"istanbul",
{
"exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"]
}
],
[
"transform-define",
{
"process.env.BABEL_ENV": "coverage"
}
]
]
}
}

View File

@ -1,11 +1,12 @@
/app/assets/javascripts/locale/**/app.js
/config/
/builds/
/coverage/
/coverage-javascript/
/node_modules/
/public/
/scripts/
/tmp/
/vendor/
karma.config.js
webpack.config.js
svg.config.js
/app/assets/javascripts/locale/**/app.js

View File

@ -544,7 +544,7 @@ migration:path-mysql:
.db-rollback: &db-rollback
<<: *dedicated-no-docs-pull-cache-job
script:
- bundle exec rake db:rollback STEP=119
- bundle exec rake db:migrate VERSION=20170523121229
- bundle exec rake db:migrate
db:rollback-pg:

5
.prettierignore Normal file
View File

@ -0,0 +1,5 @@
/app/assets/javascripts/locale/**/app.js
/node_modules/
/public/
/vendor/
/tmp/

View File

@ -1,4 +1,13 @@
{
"printWidth": 100,
"singleQuote": true,
"trailingComma": "all"
"trailingComma": "es5",
"overrides": [
{
"files": ["**/app/**/*", "**/spec/**/*"],
"options": {
"trailingComma": "all"
}
}
]
}

View File

@ -118,6 +118,9 @@ Gitlab/ModuleWithInstanceVariables:
- spec/support/**/*.rb
- features/steps/**/*.rb
Gitlab/HTTParty:
Enabled: true
GitlabSecurity/PublicSend:
Enabled: true
Exclude:

View File

@ -168,13 +168,17 @@ entry.
- Add one group board to Libre.
- Add support for filtering by source and target branch to merge requests API.
### Other (14 changes, 3 of them are from the community)
### Other (18 changes, 7 of them are from the community)
- Group MRs on issue page by project and namespace. !8494 (Jeff Stubler)
- Make oauth provider login generic. !8809 (Horatiu Eugen Vlad)
- Add email button to new issue by email. !10942 (Islam Wazery)
- Update vue component naming guidelines. !17018 (George Tsiolis)
- Added new design for promotion modals. !17197
- Update to github-linguist 5.3.x. !17241 (Ken Ding)
- update toml-rb to 1.0.0. !17259 (Ken Ding)
- Keep track of projects a user interacted with. !17327
- Moved o_auth/saml/ldap modules under gitlab/auth. !17359 (Horatiu Eugen Vlad)
- Enables eslint in codeclimate job. !17392
- Port Labels Select dropdown to Vue. !17411
- Add NOT NULL constraint to projects.namespace_id. !17448

View File

@ -1 +1 @@
7.1.0
7.1.1

View File

@ -38,7 +38,7 @@ gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.3'
gem 'doorkeeper-openid_connect', '~> 1.3'
gem 'omniauth', '~> 1.8'
gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-auth0', '~> 2.0.0'
gem 'omniauth-azure-oauth2', '~> 0.0.9'
gem 'omniauth-cas3', '~> 1.1.4'
gem 'omniauth-facebook', '~> 4.0.0'
@ -49,9 +49,10 @@ gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-oauth2-generic', '~> 0.2.2'
gem 'omniauth-saml', '~> 1.10'
gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.4'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.1'
gem 'omniauth-jwt', '~> 0.0.2'
gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt', '~> 1.5.6'
@ -162,7 +163,7 @@ group :unicorn do
end
# State machine
gem 'state_machines-activerecord', '~> 0.4.0'
gem 'state_machines-activerecord', '~> 0.5.1'
# Issue tags
gem 'acts-as-taggable-on', '~> 5.0'
@ -231,7 +232,7 @@ gem 'sanitize', '~> 2.0'
gem 'babosa', '~> 1.0.2'
# Sanitizes SVG input
gem 'loofah', '~> 2.0.3'
gem 'loofah', '~> 2.2'
# Working with license
gem 'licensee', '~> 8.9'

View File

@ -143,6 +143,7 @@ GEM
connection_pool (2.2.1)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.3)
creole (0.5.0)
css_parser (1.5.0)
addressable
@ -356,7 +357,7 @@ GEM
signet (~> 0.7)
gpgme (2.0.13)
mini_portile2 (~> 2.1)
grape (1.0.0)
grape (1.0.2)
activesupport
builder
mustermann-grape (~> 1.0.0)
@ -485,7 +486,8 @@ GEM
actionpack (>= 4, < 5.2)
activesupport (>= 4, < 5.2)
railties (>= 4, < 5.2)
loofah (2.0.3)
loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.0)
mini_mime (>= 0.1.1)
@ -505,7 +507,7 @@ GEM
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
mustermann (1.0.0)
mustermann (1.0.2)
mustermann-grape (1.0.0)
mustermann (~> 1.0.0)
mysql2 (0.4.10)
@ -515,7 +517,7 @@ GEM
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
numerizer (0.1.1)
oauth (0.5.1)
oauth (0.5.4)
oauth2 (1.4.0)
faraday (>= 0.8, < 0.13)
jwt (~> 1.0)
@ -527,8 +529,8 @@ GEM
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3)
omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1)
omniauth-auth0 (2.0.0)
omniauth-oauth2 (~> 1.4)
omniauth-authentiq (0.3.1)
omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.9)
@ -552,6 +554,9 @@ GEM
multi_json (~> 1.3)
omniauth (>= 1.1.1)
omniauth-oauth2 (>= 1.3.1)
omniauth-jwt (0.0.2)
jwt
omniauth (~> 1.1)
omniauth-kerberos (0.3.0)
omniauth-multipassword
timfel-krb5-auth (~> 0.8)
@ -570,9 +575,9 @@ GEM
ruby-saml (~> 1.7)
omniauth-shibboleth (1.2.1)
omniauth (>= 1.0.0)
omniauth-twitter (1.2.1)
json (~> 1.3)
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
omniauth_crowd (2.2.3)
activesupport
nokogiri (>= 1.4.4)
@ -679,8 +684,8 @@ GEM
activesupport (>= 4.2.0, < 5.0)
nokogiri (~> 1.6)
rails-deprecated_sanitizer (>= 1.0.1)
rails-html-sanitizer (1.0.3)
loofah (~> 2.0)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
rails-i18n (4.0.9)
i18n (~> 0.7)
railties (~> 4.0)
@ -895,13 +900,13 @@ GEM
sqlite3 (1.3.13)
sshkey (1.9.0)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
activemodel (>= 4.1, < 5.1)
state_machines (>= 0.4.0)
state_machines-activerecord (0.4.0)
activerecord (>= 4.1, < 5.1)
state_machines-activemodel (>= 0.3.0)
state_machines (0.5.0)
state_machines-activemodel (0.5.1)
activemodel (>= 4.1, < 6.0)
state_machines (>= 0.5.0)
state_machines-activerecord (0.5.1)
activerecord (>= 4.1, < 6.0)
state_machines-activemodel (>= 0.5.0)
stringex (2.7.1)
sys-filesystem (1.1.6)
ffi
@ -1093,7 +1098,7 @@ DEPENDENCIES
license_finder (~> 3.1)
licensee (~> 8.9)
lograge (~> 0.5)
loofah (~> 2.0.3)
loofah (~> 2.2)
mail_room (~> 0.9.1)
method_source (~> 0.8)
minitest (~> 5.7.0)
@ -1105,7 +1110,7 @@ DEPENDENCIES
oauth2 (~> 1.4)
octokit (~> 4.8)
omniauth (~> 1.8)
omniauth-auth0 (~> 1.4.1)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.9)
omniauth-cas3 (~> 1.1.4)
@ -1113,11 +1118,12 @@ DEPENDENCIES
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.2)
omniauth-google-oauth2 (~> 0.5.2)
omniauth-jwt (~> 0.0.2)
omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
omniauth-saml (~> 1.10)
omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0)
omniauth-twitter (~> 1.4)
omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12)
peek (~> 1.0.1)
@ -1192,7 +1198,7 @@ DEPENDENCIES
sprockets (~> 3.7.0)
sshkey (~> 1.9.0)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
state_machines-activerecord (~> 0.5.1)
sys-filesystem (~> 1.1.6)
test-prof (~> 0.2.5)
test_after_commit (~> 1.1)

View File

@ -43,8 +43,8 @@ GEM
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
acts-as-taggable-on (4.0.0)
activerecord (>= 4.0)
acts-as-taggable-on (5.0.0)
activerecord (>= 4.2.8)
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
@ -178,10 +178,10 @@ GEM
docile (1.1.5)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (4.2.6)
doorkeeper (4.3.1)
railties (>= 4.2)
doorkeeper-openid_connect (1.2.0)
doorkeeper (~> 4.0)
doorkeeper-openid_connect (1.3.0)
doorkeeper (~> 4.3)
json-jwt (~> 1.6)
dropzonejs-rails (0.7.4)
rails (> 3.1)
@ -220,13 +220,13 @@ GEM
path_expander (~> 1.0)
ruby_parser (~> 3.0)
sexp_processor (~> 4.0)
flipper (0.11.0)
flipper-active_record (0.11.0)
flipper (0.13.0)
flipper-active_record (0.13.0)
activerecord (>= 3.2, < 6)
flipper (~> 0.11.0)
flipper-active_support_cache_store (0.11.0)
flipper (~> 0.13.0)
flipper-active_support_cache_store (0.13.0)
activesupport (>= 3.2, < 6)
flipper (~> 0.11.0)
flipper (~> 0.13.0)
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
@ -235,7 +235,7 @@ GEM
fog-json (~> 1.0)
ipaddress (~> 0.8)
xml-simple (~> 1.1)
fog-aws (1.4.1)
fog-aws (2.0.1)
fog-core (~> 1.38)
fog-json (~> 1.0)
fog-xml (~> 0.1)
@ -267,7 +267,7 @@ GEM
nokogiri (>= 1.5.11, < 2.0.0)
font-awesome-rails (4.7.0.3)
railties (>= 3.2, < 5.2)
foreman (0.78.0)
foreman (0.84.0)
thor (~> 0.19.1)
formatador (0.2.5)
fuubar (2.2.0)
@ -283,7 +283,7 @@ GEM
text (>= 1.3.0)
gettext_i18n_rails (1.8.0)
fast_gettext (>= 0.9.0)
gettext_i18n_rails_js (1.2.0)
gettext_i18n_rails_js (1.3.0)
gettext (>= 3.0.2)
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
@ -337,9 +337,9 @@ GEM
json
multi_json
request_store (>= 1.0)
google-api-client (0.13.6)
google-api-client (0.19.8)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.5)
googleauth (>= 0.5, < 0.7.0)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
@ -404,7 +404,7 @@ GEM
html2text (0.2.1)
nokogiri (~> 1.6)
htmlentities (4.3.4)
http (0.9.8)
http (2.2.2)
addressable (~> 2.3)
http-cookie (~> 1.0)
http-form_data (~> 1.0.1)
@ -427,10 +427,6 @@ GEM
multipart-post
oauth (~> 0.5, >= 0.5.0)
jquery-atwho-rails (1.3.2)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.6)
json-jwt (1.9.2)
activesupport
@ -454,13 +450,12 @@ GEM
kaminari-core (= 1.1.1)
kaminari-core (1.1.1)
kgio (2.11.2)
knapsack (1.11.1)
knapsack (1.16.0)
rake
timecop (>= 0.1.0)
kubeclient (2.2.0)
http (= 0.9.8)
recursive-open-struct (= 1.0.0)
rest-client
kubeclient (3.0.0)
http (~> 2.2.2)
recursive-open-struct (~> 1.0.4)
rest-client (~> 2.0)
launchy (2.4.3)
addressable (~> 2.3)
letter_opener (1.6.0)
@ -477,7 +472,7 @@ GEM
toml (= 0.1.2)
with_env (> 1.0)
xml-simple
licensee (8.7.0)
licensee (8.9.2)
rugged (~> 0.24)
little-plugger (1.1.4)
locale (2.1.2)
@ -514,7 +509,7 @@ GEM
mustermann (~> 1.0.0)
mysql2 (0.4.10)
net-ldap (0.16.1)
net-ssh (4.1.0)
net-ssh (4.2.0)
netrc (0.11.0)
nio4r (2.2.0)
nokogiri (1.8.2)
@ -527,11 +522,10 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
octokit (4.6.2)
octokit (4.8.0)
sawyer (~> 0.8.0, >= 0.5.3)
oj (2.17.5)
omniauth (1.4.3)
hashie (>= 1.2, < 4)
omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0)
rack (>= 1.6.2, < 3)
omniauth-auth0 (1.4.2)
omniauth-oauth2 (~> 1.1)
@ -570,9 +564,9 @@ GEM
omniauth (~> 1.2)
omniauth-oauth2-generic (0.2.4)
omniauth-oauth2 (~> 1.0)
omniauth-saml (1.7.0)
omniauth (~> 1.3)
ruby-saml (~> 1.4)
omniauth-saml (1.10.0)
omniauth (~> 1.3, >= 1.3.2)
ruby-saml (~> 1.7)
omniauth-shibboleth (1.2.1)
omniauth (>= 1.0.0)
omniauth-twitter (1.2.1)
@ -598,8 +592,6 @@ GEM
railties (>= 4.0.0)
peek-gc (0.0.2)
peek
peek-host (1.0.0)
peek
peek-mysql2 (1.1.0)
atomic (>= 1.0.0)
mysql2
@ -713,7 +705,7 @@ GEM
re2 (1.1.1)
recaptcha (3.4.0)
json
recursive-open-struct (1.0.0)
recursive-open-struct (1.0.5)
redcarpet (3.4.0)
redis (3.3.5)
redis-actionpack (5.0.2)
@ -805,7 +797,7 @@ GEM
i18n
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (0.16.2)
ruby-prof (0.17.0)
ruby-progressbar (1.9.0)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
@ -846,7 +838,7 @@ GEM
selenium-webdriver (3.11.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
sentry-raven (2.5.3)
sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
sexp_processor (4.10.1)
@ -903,12 +895,12 @@ GEM
sshkey (1.9.0)
stackprof (0.2.11)
state_machines (0.5.0)
state_machines-activemodel (0.5.0)
activemodel (>= 4.1, < 5.2)
state_machines-activemodel (0.5.1)
activemodel (>= 4.1, < 6.0)
state_machines (>= 0.5.0)
state_machines-activerecord (0.4.1)
activerecord (>= 4.1, < 5.2)
state_machines-activemodel (>= 0.3.0)
state_machines-activerecord (0.5.1)
activerecord (>= 4.1, < 6.0)
state_machines-activemodel (>= 0.5.0)
stringex (2.8.4)
sys-filesystem (1.1.9)
ffi
@ -998,7 +990,7 @@ DEPENDENCIES
RedCloth (~> 4.3.2)
ace-rails-ap (~> 4.1.0)
activerecord_sane_schema_dumper (= 1.0)
acts-as-taggable-on (~> 4.0)
acts-as-taggable-on (~> 5.0)
addressable (~> 2.5.2)
akismet (~> 2.0)
allocations (~> 1.0)
@ -1038,8 +1030,8 @@ DEPENDENCIES
devise (~> 4.2)
devise-two-factor (~> 3.0.0)
diffy (~> 3.1.0)
doorkeeper (~> 4.2.0)
doorkeeper-openid_connect (~> 1.2.0)
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.3)
dropzonejs-rails (~> 0.7.1)
email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0)
@ -1048,24 +1040,24 @@ DEPENDENCIES
fast_blank
ffaker (~> 2.4)
flay (~> 2.10.0)
flipper (~> 0.11.0)
flipper-active_record (~> 0.11.0)
flipper-active_support_cache_store (~> 0.11.0)
flipper (~> 0.13.0)
flipper-active_record (~> 0.13.0)
flipper-active_support_cache_store (~> 0.13.0)
fog-aliyun (~> 0.2.0)
fog-aws (~> 1.4)
fog-aws (~> 2.0)
fog-core (~> 1.44)
fog-google (~> 0.5)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
foreman (~> 0.78.0)
foreman (~> 0.84.0)
fuubar (~> 2.2.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.3)
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.88.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
@ -1075,7 +1067,7 @@ DEPENDENCIES
gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0)
google-api-client (~> 0.13.6)
google-api-client (~> 0.19.8)
google-protobuf (= 3.5.1)
gpgme
grape (~> 1.0)
@ -1094,15 +1086,14 @@ DEPENDENCIES
influxdb (~> 0.2)
jira-ruby (~> 1.4)
jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.3.1)
json-schema (~> 2.8.0)
jwt (~> 1.5.6)
kaminari (~> 1.0)
knapsack (~> 1.11.0)
kubeclient (~> 2.2.0)
knapsack (~> 1.16)
kubeclient (~> 3.0)
letter_opener_web (~> 1.3.0)
license_finder (~> 3.1)
licensee (~> 8.7.0)
licensee (~> 8.9)
lograge (~> 0.5)
loofah (~> 2.0.3)
mail_room (~> 0.9.1)
@ -1111,12 +1102,11 @@ DEPENDENCIES
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.4.10)
net-ldap
net-ssh (~> 4.1.0)
net-ssh (~> 4.2.0)
nokogiri (~> 1.8.2)
oauth2 (~> 1.4)
octokit (~> 4.6.2)
oj (~> 2.17.4)
omniauth (~> 1.4.2)
octokit (~> 4.8)
omniauth (~> 1.8)
omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.9)
@ -1127,14 +1117,13 @@ DEPENDENCIES
omniauth-google-oauth2 (~> 0.5.2)
omniauth-kerberos (~> 0.3.0)
omniauth-oauth2-generic (~> 0.2.2)
omniauth-saml (~> 1.7.0)
omniauth-saml (~> 1.10)
omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0)
omniauth_crowd (~> 2.2.0)
org-ruby (~> 0.9.12)
peek (~> 1.0.1)
peek-gc (~> 0.0.2)
peek-host (~> 1.0.0)
peek-mysql2 (~> 1.1.0)
peek-performance_bar (~> 1.3.0)
peek-pg (~> 1.3.0)
@ -1177,7 +1166,7 @@ DEPENDENCIES
rubocop (~> 0.52.1)
rubocop-rspec (~> 1.22.1)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2)
ruby-prof (~> 0.17.0)
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.26.0)
@ -1187,7 +1176,7 @@ DEPENDENCIES
seed-fu (~> 2.3.7)
select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5)
sentry-raven (~> 2.5.3)
sentry-raven (~> 2.7)
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
shoulda-matchers (~> 3.1.2)
@ -1205,7 +1194,7 @@ DEPENDENCIES
sprockets (~> 3.7.0)
sshkey (~> 1.9.0)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
state_machines-activerecord (~> 0.5.1)
sys-filesystem (~> 1.1.6)
test-prof (~> 0.2.5)
test_after_commit (~> 1.1)

View File

@ -1,6 +1,6 @@
import $ from 'jquery';
import Vue from 'vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub';
const Store = gl.issueBoards.BoardsStore;
@ -45,7 +45,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
};
},
components: {
userAvatarLink,
UserAvatarLink,
},
computed: {
numberOverLimit() {

View File

@ -33,7 +33,7 @@ export default class VariableList {
selector: '.js-ci-variable-input-key',
default: '',
},
value: {
secret_value: {
selector: '.js-ci-variable-input-value',
default: '',
},
@ -105,7 +105,7 @@ export default class VariableList {
setupToggleButtons($row[0]);
// Reset the resizable textarea
$row.find(this.inputMap.value.selector).css('height', '');
$row.find(this.inputMap.secret_value.selector).css('height', '');
const $environmentSelect = $row.find('.js-variable-environment-toggle');
if ($environmentSelect.length) {

View File

@ -1,4 +1,5 @@
// ECMAScript polyfills
import 'core-js/fn/array/fill';
import 'core-js/fn/array/find';
import 'core-js/fn/array/find-index';
import 'core-js/fn/array/from';

View File

@ -4,7 +4,7 @@
import $ from 'jquery';
import { s__ } from '~/locale';
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { getParameterByName } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
@ -15,7 +15,7 @@ import groupsComponent from './groups.vue';
export default {
components: {
loadingIcon,
modal,
DeprecatedModal,
groupsComponent,
},
props: {
@ -52,8 +52,9 @@ export default {
},
},
created() {
this.searchEmptyMessage = this.hideProjects ?
COMMON_STR.GROUP_SEARCH_EMPTY : COMMON_STR.GROUP_PROJECT_SEARCH_EMPTY;
this.searchEmptyMessage = this.hideProjects
? COMMON_STR.GROUP_SEARCH_EMPTY
: COMMON_STR.GROUP_PROJECT_SEARCH_EMPTY;
eventHub.$on('fetchPage', this.fetchPage);
eventHub.$on('toggleChildren', this.toggleChildren);
@ -72,22 +73,30 @@ export default {
eventHub.$off('updateGroups', this.updateGroups);
},
methods: {
fetchGroups({ parentId, page, filterGroupsBy, sortBy, archived, updatePagination }) {
return this.service.getGroups(parentId, page, filterGroupsBy, sortBy, archived)
.then((res) => {
if (updatePagination) {
this.updatePagination(res.headers);
}
fetchGroups({
parentId,
page,
filterGroupsBy,
sortBy,
archived,
updatePagination,
}) {
return this.service
.getGroups(parentId, page, filterGroupsBy, sortBy, archived)
.then(res => {
if (updatePagination) {
this.updatePagination(res.headers);
}
return res;
})
.then(res => res.json())
.catch(() => {
this.isLoading = false;
$.scrollTo(0);
return res;
})
.then(res => res.json())
.catch(() => {
this.isLoading = false;
$.scrollTo(0);
Flash(COMMON_STR.FAILURE);
});
Flash(COMMON_STR.FAILURE);
});
},
fetchAllGroups() {
const page = getParameterByName('page') || null;
@ -103,7 +112,7 @@ export default {
sortBy,
archived,
updatePagination: true,
}).then((res) => {
}).then(res => {
this.isLoading = false;
this.updateGroups(res, Boolean(filterGroupsBy));
});
@ -118,14 +127,18 @@ export default {
sortBy,
archived,
updatePagination: true,
}).then((res) => {
}).then(res => {
this.isLoading = false;
$.scrollTo(0);
const currentPath = mergeUrlParams({ page }, window.location.href);
window.history.replaceState({
page: currentPath,
}, document.title, currentPath);
window.history.replaceState(
{
page: currentPath,
},
document.title,
currentPath,
);
this.updateGroups(res);
});
@ -138,11 +151,13 @@ export default {
// eslint-disable-next-line promise/catch-or-return
this.fetchGroups({
parentId: parentGroup.id,
}).then((res) => {
this.store.setGroupChildren(parentGroup, res);
}).catch(() => {
parentGroup.isChildrenLoading = false;
});
})
.then(res => {
this.store.setGroupChildren(parentGroup, res);
})
.catch(() => {
parentGroup.isChildrenLoading = false;
});
} else {
parentGroup.isOpen = true;
}
@ -154,7 +169,11 @@ export default {
this.targetGroup = group;
this.targetParentGroup = parentGroup;
this.showModal = true;
this.groupLeaveConfirmationMessage = s__(`GroupsTree|Are you sure you want to leave the "${group.fullName}" group?`);
this.groupLeaveConfirmationMessage = s__(
`GroupsTree|Are you sure you want to leave the "${
group.fullName
}" group?`,
);
},
hideLeaveGroupModal() {
this.showModal = false;
@ -162,14 +181,15 @@ export default {
leaveGroup() {
this.showModal = false;
this.targetGroup.isBeingRemoved = true;
this.service.leaveGroup(this.targetGroup.leavePath)
this.service
.leaveGroup(this.targetGroup.leavePath)
.then(res => res.json())
.then((res) => {
.then(res => {
$.scrollTo(0);
this.store.removeGroup(this.targetGroup, this.targetParentGroup);
Flash(res.notice, 'notice');
})
.catch((err) => {
.catch(err => {
let message = COMMON_STR.FAILURE;
if (err.status === 403) {
message = COMMON_STR.LEAVE_FORBIDDEN;
@ -208,8 +228,8 @@ export default {
:search-empty-message="searchEmptyMessage"
:page-info="pageInfo"
/>
<modal
v-if="showModal"
<deprecated-modal
v-show="showModal"
kind="warning"
:primary-button-label="__('Leave')"
:title="__('Are you sure?')"

View File

@ -1,75 +1,75 @@
<script>
import { __ } from '~/locale';
import modal from '~/vue_shared/components/modal.vue';
import { __ } from '~/locale';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
export default {
components: {
modal,
export default {
components: {
DeprecatedModal,
},
props: {
branchId: {
type: String,
required: true,
},
props: {
branchId: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
path: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
data() {
return {
entryName: this.path !== '' ? `${this.path}/` : '',
};
path: {
type: String,
required: true,
},
computed: {
modalTitle() {
if (this.type === 'tree') {
return __('Create new directory');
}
},
data() {
return {
entryName: this.path !== '' ? `${this.path}/` : '',
};
},
computed: {
modalTitle() {
if (this.type === 'tree') {
return __('Create new directory');
}
return __('Create new file');
},
buttonLabel() {
if (this.type === 'tree') {
return __('Create directory');
}
return __('Create file');
},
formLabelName() {
if (this.type === 'tree') {
return __('Directory name');
}
return __('File name');
},
return __('Create new file');
},
mounted() {
this.$refs.fieldName.focus();
},
methods: {
createEntryInStore() {
this.$emit('create', {
branchId: this.branchId,
name: this.entryName,
type: this.type,
});
buttonLabel() {
if (this.type === 'tree') {
return __('Create directory');
}
this.hideModal();
},
hideModal() {
this.$emit('hide');
},
return __('Create file');
},
};
formLabelName() {
if (this.type === 'tree') {
return __('Directory name');
}
return __('File name');
},
},
mounted() {
this.$refs.fieldName.focus();
},
methods: {
createEntryInStore() {
this.$emit('create', {
branchId: this.branchId,
name: this.entryName,
type: this.type,
});
this.hideModal();
},
hideModal() {
this.$emit('hide');
},
},
};
</script>
<template>
<modal
<deprecated-modal
:title="modalTitle"
:primary-button-label="buttonLabel"
kind="success"
@ -95,5 +95,5 @@
</div>
</fieldset>
</form>
</modal>
</deprecated-modal>
</template>

View File

@ -2,7 +2,7 @@
import { mapState, mapActions, mapGetters } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip';
import icon from '~/vue_shared/components/icon.vue';
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import commitFilesList from './commit_sidebar/list.vue';
import EmptyState from './commit_sidebar/empty_state.vue';
@ -11,7 +11,7 @@ import * as consts from '../stores/modules/commit/constants';
export default {
components: {
modal,
DeprecatedModal,
icon,
commitFilesList,
EmptyState,
@ -34,11 +34,7 @@ export default {
computed: {
...mapState(['changedFiles', 'stagedFiles', 'rightPanelCollapsed']),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
...mapGetters('commit', [
'commitButtonDisabled',
'discardDraftButtonDisabled',
'branchName',
]),
...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled', 'branchName']),
},
methods: {
...mapActions('commit', [
@ -48,9 +44,7 @@ export default {
'updateCommitAction',
]),
forceCreateNewBranch() {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() =>
this.commitChanges(),
);
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commitChanges());
},
},
};
@ -60,7 +54,7 @@ export default {
<div
class="multi-file-commit-panel-section"
>
<modal
<deprecated-modal
id="ide-create-branch-modal"
:primary-button-label="__('Create new branch')"
kind="success"
@ -71,7 +65,7 @@ export default {
{{ __(`This branch has changed since you started editing.
Would you like to create a new branch?`) }}
</template>
</modal>
</deprecated-modal>
<template
v-if="changedFiles.length || stagedFiles.length"
>

View File

@ -65,6 +65,10 @@ export default class Editor {
(this.instance = this.monaco.editor.createDiffEditor(domElement, {
...defaultEditorOptions,
readOnly: true,
quickSuggestions: false,
occurrencesHighlight: false,
renderLineHighlight: 'none',
hideCursorInOverviewRuler: true,
})),
);

View File

@ -1809,9 +1809,11 @@ export default class Notes {
}
}
$closeBtn.text($closeBtn.data('originalText'));
/* eslint-disable promise/catch-or-return */
// Make request to submit comment on server
axios
return axios
.post(`${formAction}?html=true`, formData)
.then(res => {
const note = res.data;
@ -1928,8 +1930,6 @@ export default class Notes {
this.reenableTargetFormSubmitButton(e);
this.addNoteError($form);
});
return $closeBtn.text($closeBtn.data('originalText'));
}
/**

View File

@ -0,0 +1,6 @@
import initSettingsPanels from '~/settings_panels';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
});

View File

@ -1,11 +1,11 @@
<script>
import _ from 'underscore';
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
deleteProjectUrl: {
@ -79,7 +79,7 @@
</script>
<template>
<modal
<deprecated-modal
id="delete-project-modal"
:title="title"
:text="text"
@ -121,5 +121,5 @@
/>
</form>
</template>
</modal>
</deprecated-modal>
</template>

View File

@ -1,11 +1,11 @@
<script>
import _ from 'underscore';
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
deleteUserUrl: {
@ -113,7 +113,7 @@
</script>
<template>
<modal
<deprecated-modal
id="delete-user-modal"
:title="title"
:text="text"
@ -170,5 +170,5 @@
{{ secondaryButtonLabel }}
</button>
</template>
</modal>
</deprecated-modal>
</template>

View File

@ -2,14 +2,14 @@
import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash';
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { n__, s__, sprintf } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
issueCount: {
@ -92,7 +92,7 @@ Once deleted, it cannot be undone or recovered.`),
</script>
<template>
<modal
<deprecated-modal
id="delete-milestone-modal"
:title="title"
:text="text"
@ -106,5 +106,5 @@ Once deleted, it cannot be undone or recovered.`),
<p v-html="props.text"></p>
</template>
</modal>
</deprecated-modal>
</template>

View File

@ -70,6 +70,7 @@ export default {
<td
v-for="key in keys"
:key="key"
class="break-word"
>
{{ item[key] }}
</td>

View File

@ -1,11 +1,28 @@
import Vue from 'vue';
import _ from 'underscore';
import axios from '../../lib/utils/axios_utils';
let vueResourceInterceptor;
export default class PerformanceBarService {
static fetchRequestDetails(peekUrl, requestId) {
return axios.get(peekUrl, { params: { request_id: requestId } });
}
static registerInterceptor(peekUrl, callback) {
vueResourceInterceptor = (request, next) => {
next(response => {
const requestId = response.headers['x-request-id'];
const requestUrl = response.url;
if (requestUrl !== peekUrl && requestId) {
callback(requestId, requestUrl);
}
});
};
Vue.http.interceptors.push(vueResourceInterceptor);
return axios.interceptors.response.use(response => {
const requestId = response.headers['x-request-id'];
const requestUrl = response.config.url;
@ -20,5 +37,9 @@ export default class PerformanceBarService {
static removeInterceptor(interceptor) {
axios.interceptors.response.eject(interceptor);
Vue.http.interceptors = _.without(
Vue.http.interceptors,
vueResourceInterceptor,
);
}
}

View File

@ -1,5 +1,5 @@
<script>
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { s__, sprintf } from '~/locale';
import pipelinesTableRowComponent from './pipelines_table_row.vue';
import eventHub from '../event_hub';
@ -12,7 +12,7 @@
export default {
components: {
pipelinesTableRowComponent,
modal,
DeprecatedModal,
},
props: {
pipelines: {
@ -120,7 +120,7 @@
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
/>
<modal
<deprecated-modal
id="confirmation-modal"
:title="modalTitle"
:text="modalText"
@ -134,6 +134,6 @@
>
<p v-html="props.text"></p>
</template>
</modal>
</deprecated-modal>
</div>
</template>

View File

@ -1,11 +1,11 @@
<script>
import modal from '~/vue_shared/components/modal.vue';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { __, s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
export default {
components: {
modal,
DeprecatedModal,
},
props: {
actionUrl: {
@ -76,7 +76,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</script>
<template>
<modal
<deprecated-modal
id="delete-account-modal"
:title="s__('Profiles|Delete your account?')"
:text="text"
@ -131,5 +131,5 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</form>
</template>
</modal>
</deprecated-modal>
</template>

View File

@ -1,4 +1,5 @@
<script>
import { sprintf, s__ } from '~/locale';
import statusCodes from '../../lib/utils/http_status';
import { bytesToMiB } from '../../lib/utils/number_utils';
import { backOff } from '../../lib/utils/common_utils';
@ -45,17 +46,28 @@ export default {
shouldShowMetricsUnavailable() {
return !this.loadingMetrics && !this.hasMetrics && !this.loadFailed;
},
memoryChangeType() {
memoryChangeMessage() {
const messageProps = {
memoryFrom: this.memoryFrom,
memoryTo: this.memoryTo,
metricsLinkStart: `<a href="${this.metricsMonitoringUrl}">`,
metricsLinkEnd: '</a>',
emphasisStart: '<b>',
emphasisEnd: '</b>',
};
const memoryTo = Number(this.memoryTo);
const memoryFrom = Number(this.memoryFrom);
let memoryUsageMsg = '';
if (memoryTo > memoryFrom) {
return 'increased';
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} increased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
} else if (memoryTo < memoryFrom) {
return 'decreased';
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB'), messageProps, false);
} else {
memoryUsageMsg = sprintf(s__('mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage is %{emphasisStart} unchanged %{emphasisEnd} at %{memoryFrom}MB'), messageProps, false);
}
return 'unchanged';
return memoryUsageMsg;
},
},
mounted() {
@ -130,24 +142,22 @@ export default {
<i
class="fa fa-spinner fa-spin usage-info-load-spinner"
aria-hidden="true">
</i>Loading deployment statistics
</i>{{ s__('mrWidget|Loading deployment statistics') }}
</p>
<p
v-if="shouldShowMemoryGraph"
class="usage-info js-usage-info">
<a
:href="metricsMonitoringUrl"
>Memory</a> usage <b>{{ memoryChangeType }}</b> from {{ memoryFrom }}MB to {{ memoryTo }}MB
{{ memoryChangeMessage }}
</p>
<p
v-if="shouldShowLoadFailure"
class="usage-info js-usage-info usage-info-failed">
Failed to load deployment statistics
{{ s__('mrWidget|Failed to load deployment statistics') }}
</p>
<p
v-if="shouldShowMetricsUnavailable"
class="usage-info js-usage-info usage-info-unavailable">
Deployment statistics are not available currently
{{ s__('mrWidget|Deployment statistics are not available currently') }}
</p>
<memory-graph
v-if="shouldShowMemoryGraph"

View File

@ -1,7 +1,7 @@
<script>
/* eslint-disable vue/require-default-prop */
export default {
name: 'Modal',
name: 'DeprecatedModal', // use GlModal instead
props: {
id: {

View File

@ -1,11 +1,11 @@
<script>
import modal from './modal.vue';
import DeprecatedModal from './deprecated_modal.vue';
export default {
name: 'RecaptchaModal',
components: {
modal,
DeprecatedModal,
},
props: {
@ -65,7 +65,7 @@
</script>
<template>
<modal
<deprecated-modal
kind="warning"
class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true"
@ -82,5 +82,5 @@
>
</div>
</div>
</modal>
</deprecated-modal>
</template>

View File

@ -446,6 +446,10 @@ img.emoji {
opacity: .5;
}
.break-word {
word-wrap: break-word;
}
/** COMMON CLASSES **/
.prepend-top-0 { margin-top: 0; }
.prepend-top-5 { margin-top: 5px; }

View File

@ -622,7 +622,7 @@
}
.dropdown-content {
max-height: $dropdown-max-height;
max-height: 252px;
overflow-y: auto;
}
@ -699,6 +699,31 @@
border-radius: $border-radius-base;
}
.git-revision-dropdown {
.dropdown-content {
max-height: 215px;
}
}
.sidebar-move-issue-dropdown {
.dropdown-content {
max-height: 160px;
}
}
.dropdown-menu-author {
.dropdown-content {
max-height: 215px;
}
}
.dropdown-menu-labels {
.dropdown-content {
max-height: 128px;
}
}
.dropdown-menu-due-date {
.dropdown-content {
max-height: 230px;

View File

@ -152,3 +152,4 @@
.sidebar-collapsed-icon .sidebar-collapsed-value {
font-size: 12px;
}

View File

@ -31,8 +31,12 @@
.dropdown-menu-issues-board-new {
width: 320px;
.open & {
max-height: 400px;
}
.dropdown-content {
max-height: 150px;
max-height: 162px;
}
}

View File

@ -1,6 +1,17 @@
.content-list > .branch-item,
.branch-title {
display: flex;
align-items: center;
}
.branch-info {
flex: auto;
min-width: 0;
overflow: hidden;
}
.divergence-graph {
padding: 12px 12px 0 0;
float: right;
padding: 0 6px;
.graph-side {
position: relative;
@ -53,3 +64,9 @@
background-color: $divergence-graph-separator-bg;
}
}
.divergence-graph,
.branch-item .controls {
flex: 0 0 auto;
white-space: nowrap;
}

View File

@ -162,17 +162,14 @@
* Last push widget
*/
.event-last-push {
overflow: auto;
width: 100%;
display: flex;
align-items: center;
.event-last-push-text {
@include str-truncated(100%);
padding: 4px 0;
font-size: 13px;
float: left;
margin-right: -150px;
padding-right: 150px;
line-height: 20px;
margin-right: $gl-padding;
}
}

View File

@ -26,9 +26,15 @@
}
}
.dropdown-menu-labels {
.dropdown-content {
max-height: 135px;
}
}
.dropdown-new-label {
.dropdown-content {
max-height: 260px;
max-height: 136px;
}
}

View File

@ -9,7 +9,6 @@
.new_project,
.edit-project,
.import-project {
.help-block {
margin-bottom: 10px;
}
@ -18,18 +17,25 @@
border-radius: $border-radius-base;
}
.input-group > div {
.input-group {
display: flex;
&:last-child {
padding-right: 0;
.select2-container {
display: unset;
max-width: unset;
width: unset !important;
flex-grow: 1;
}
> div {
&:last-child {
padding-right: 0;
}
}
}
@media (max-width: $screen-xs-max) {
.input-group > div {
margin-bottom: 14px;
&:last-child {
margin-bottom: 0;
}
@ -41,17 +47,24 @@
}
.input-group-addon {
overflow: hidden;
text-overflow: ellipsis;
line-height: unset;
width: unset;
max-width: 50%;
text-align: left;
&.static-namespace {
height: 35px;
border-radius: 3px;
border: 1px solid $border-color;
max-width: 100%;
flex-grow: 1;
}
+ .select2 a,
+ .btn-default {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0 $border-radius-base $border-radius-base 0;
}
}
}
@ -290,7 +303,7 @@
font-size: 13px;
font-weight: $gl-font-weight-bold;
line-height: 13px;
letter-spacing: .4px;
letter-spacing: 0.4px;
padding: 6px 14px;
text-align: center;
vertical-align: middle;
@ -443,7 +456,7 @@ a.deploy-project-label {
text-decoration: none;
&.disabled {
opacity: .3;
opacity: 0.3;
cursor: not-allowed;
}
}
@ -600,26 +613,26 @@ a.deploy-project-label {
}
.first-column {
@media(min-width: $screen-xs-min) {
@media (min-width: $screen-xs-min) {
max-width: 50%;
padding-right: 30px;
}
@media(max-width: $screen-xs-max) {
@media (max-width: $screen-xs-max) {
max-width: 100%;
width: 100%;
}
}
.second-column {
@media(min-width: $screen-xs-min) {
@media (min-width: $screen-xs-min) {
width: 50%;
flex: 1;
padding-left: 30px;
position: relative;
}
@media(max-width: $screen-xs-max) {
@media (max-width: $screen-xs-max) {
max-width: 100%;
width: 100%;
padding-left: 0;
@ -632,7 +645,7 @@ a.deploy-project-label {
}
&::before {
content: "OR";
content: 'OR';
position: absolute;
left: -10px;
top: 50%;
@ -656,7 +669,7 @@ a.deploy-project-label {
}
&::after {
content: "";
content: '';
position: absolute;
background-color: $border-color;
bottom: 0;
@ -921,10 +934,7 @@ pre.light-well {
border-right: solid 1px transparent;
}
}
}
.protected-tags-list,
.protected-branches-list {
.dropdown-menu-toggle {
width: 100%;
max-width: 300px;

View File

@ -294,6 +294,10 @@
.margin-view-overlays .delete-sign {
opacity: 0.4;
}
.cursors-layer {
display: none;
}
}
}

View File

@ -112,7 +112,7 @@ input[type="checkbox"]:hover {
}
.dropdown-content {
max-height: 350px;
max-height: 302px;
}
}

View File

@ -15,6 +15,10 @@
line-height: $performance-bar-height;
color: $perf-bar-text;
select {
width: 200px;
}
&.disabled {
display: none;
}
@ -102,8 +106,14 @@
}
}
.performance-bar-modal .modal-footer {
display: none;
.performance-bar-modal {
.modal-footer {
display: none;
}
.modal-dialog {
width: 860px;
}
}
}

View File

@ -3,23 +3,9 @@
# Automatically sets the layout and ensures an administrator is logged in
class Admin::ApplicationController < ApplicationController
before_action :authenticate_admin!
before_action :display_read_only_information
layout 'admin'
def authenticate_admin!
render_404 unless current_user.admin?
end
def display_read_only_information
return unless Gitlab::Database.read_only?
flash.now[:notice] = read_only_message
end
private
# Overridden in EE
def read_only_message
_('You are on a read-only GitLab instance.')
end
end

View File

@ -0,0 +1,17 @@
module SendFileUpload
def send_upload(file_upload, send_params: {}, redirect_params: {}, attachment: nil, disposition: 'attachment')
if attachment
redirect_params[:query] = { "response-content-disposition" => "#{disposition};filename=#{attachment.inspect}" }
send_params.merge!(filename: attachment, disposition: disposition)
end
if file_upload.file_storage?
send_file file_upload.path, send_params
elsif file_upload.class.proxy_download_enabled?
headers.store(*Gitlab::Workhorse.send_url(file_upload.url(**redirect_params)))
head :ok
else
redirect_to file_upload.url(**redirect_params)
end
end
end

View File

@ -1,5 +1,6 @@
module UploadsActions
include Gitlab::Utils::StrongMemoize
include SendFileUpload
UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo).freeze
@ -26,14 +27,11 @@ module UploadsActions
def show
return render_404 unless uploader&.exists?
if uploader.file_storage?
disposition = uploader.image_or_video? ? 'inline' : 'attachment'
expires_in 0.seconds, must_revalidate: true, private: true
expires_in 0.seconds, must_revalidate: true, private: true
send_file uploader.file.path, disposition: disposition
else
redirect_to uploader.url
end
disposition = uploader.image_or_video? ? 'inline' : 'attachment'
send_upload(uploader, attachment: uploader.filename, disposition: disposition)
end
private
@ -62,22 +60,30 @@ module UploadsActions
end
def build_uploader_from_upload
return nil unless params[:secret] && params[:filename]
return unless uploader = build_uploader
upload_path = uploader_class.upload_path(params[:secret], params[:filename])
upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_path)
upload_paths = uploader.upload_paths(params[:filename])
upload = Upload.find_by(uploader: uploader_class.to_s, path: upload_paths)
upload&.build_uploader
end
def build_uploader_from_params
uploader = uploader_class.new(model, secret: params[:secret])
return nil unless uploader.model_valid?
return unless uploader = build_uploader
uploader.retrieve_from_store!(params[:filename])
uploader
end
def build_uploader
return unless params[:secret] && params[:filename]
uploader = uploader_class.new(model, secret: params[:secret])
return unless uploader.model_valid?
uploader
end
def image_or_video?
uploader && uploader.exists? && uploader.image_or_video?
end

View File

@ -39,7 +39,7 @@ module Groups
end
def variable_params_attributes
%i[id key value protected _destroy]
%i[id key secret_value protected _destroy]
end
def authorize_admin_build!

View File

@ -18,6 +18,18 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
end
# Extend the standard implementation to also increment
# the number of failed sign in attempts
def failure
if params[:username].present? && AuthHelper.form_based_provider?(failed_strategy.name)
user = User.by_login(params[:username])
user&.increment_failed_attempts!
end
super
end
# Extend the standard message generation to accept our custom exception
def failure_message
exception = env["omniauth.error"]
@ -95,6 +107,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
handle_omniauth
end
def auth0
if oauth['uid'].blank?
fail_auth0_login
else
handle_omniauth
end
end
private
def handle_omniauth
@ -170,6 +190,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to new_user_session_path
end
def fail_auth0_login
flash[:alert] = 'Wrong extern UID provided. Make sure Auth0 is configured correctly.'
redirect_to new_user_session_path
end
def handle_disabled_provider
label = Gitlab::Auth::OAuth::Provider.label_for(oauth['provider'])
flash[:alert] = "Signing in using #{label} has been disabled"

View File

@ -1,6 +1,7 @@
class Projects::ArtifactsController < Projects::ApplicationController
include ExtractsPath
include RendersBlob
include SendFileUpload
layout 'project'
before_action :authorize_read_build!
@ -10,11 +11,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
before_action :entry, only: [:file]
def download
if artifacts_file.file_storage?
send_file artifacts_file.path, disposition: 'attachment'
else
redirect_to artifacts_file.url
end
send_upload(artifacts_file, attachment: artifacts_file.filename)
end
def browse
@ -45,8 +42,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
end
def raw
path = Gitlab::Ci::Build::Artifacts::Path
.new(params[:path])
path = Gitlab::Ci::Build::Artifacts::Path.new(params[:path])
send_artifacts_entry(build, path)
end
@ -75,7 +71,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
end
def validate_artifacts!
render_404 unless build && build.artifacts?
render_404 unless build&.artifacts?
end
def build

View File

@ -1,4 +1,6 @@
class Projects::JobsController < Projects::ApplicationController
include SendFileUpload
before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!,
@ -117,11 +119,17 @@ class Projects::JobsController < Projects::ApplicationController
end
def raw
build.trace.read do |stream|
if stream.file?
send_file stream.path, type: 'text/plain; charset=utf-8', disposition: 'inline'
else
render_404
if trace_artifact_file
send_upload(trace_artifact_file,
send_params: raw_send_params,
redirect_params: raw_redirect_params)
else
build.trace.read do |stream|
if stream.file?
send_file stream.path, type: 'text/plain; charset=utf-8', disposition: 'inline'
else
render_404
end
end
end
end
@ -136,9 +144,21 @@ class Projects::JobsController < Projects::ApplicationController
return access_denied! unless can?(current_user, :erase_build, build)
end
def raw_send_params
{ type: 'text/plain; charset=utf-8', disposition: 'inline' }
end
def raw_redirect_params
{ query: { 'response-content-type' => 'text/plain; charset=utf-8', 'response-content-disposition' => 'inline' } }
end
def trace_artifact_file
@trace_artifact_file ||= build.job_artifacts_trace&.file
end
def build
@build ||= project.builds.find(params[:id])
.present(current_user: current_user)
.present(current_user: current_user)
end
def build_path(build)

View File

@ -1,6 +1,7 @@
class Projects::LfsStorageController < Projects::GitHttpClientController
include LfsRequest
include WorkhorseRequest
include SendFileUpload
skip_before_action :verify_workhorse_api!, only: [:download, :upload_finalize]
@ -11,7 +12,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
return
end
send_file lfs_object.file.path, content_type: "application/octet-stream"
send_upload(lfs_object.file, send_params: { content_type: "application/octet-stream" })
end
def upload_authorize
@ -70,10 +71,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController
end
def move_tmp_file_to_storage(object, path)
File.open(path) do |f|
object.file = f
end
object.file = File.open(path)
object.file.store!
object.save
end

View File

@ -21,4 +21,26 @@ class Projects::PagesController < Projects::ApplicationController
end
end
end
def update
result = Projects::UpdateService.new(@project, current_user, project_params).execute
respond_to do |format|
format.html do
if result[:status] == :success
flash[:notice] = 'Your changes have been saved'
else
flash[:alert] = 'Something went wrong on our end'
end
redirect_to project_pages_path(@project)
end
end
end
private
def project_params
params.require(:project).permit(:pages_https_only)
end
end

View File

@ -92,7 +92,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
def schedule_params
params.require(:schedule)
.permit(:description, :cron, :cron_timezone, :ref, :active,
variables_attributes: [:id, :key, :value, :_destroy] )
variables_attributes: [:id, :key, :secret_value, :_destroy] )
end
def authorize_play_pipeline_schedule!

View File

@ -10,10 +10,7 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
if service.execute
flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated."
if service.run_auto_devops_pipeline?
CreatePipelineWorker.perform_async(project.id, current_user.id, project.default_branch, :web, ignore_skip_ci: true, save_on_errors: false)
flash[:success] = "A new Auto DevOps pipeline has been created, go to <a href=\"#{project_pipelines_path(@project)}\">Pipelines page</a> for details".html_safe
end
run_autodevops_pipeline(service)
redirect_to project_settings_ci_cd_path(@project)
else
@ -24,6 +21,18 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
private
def run_autodevops_pipeline(service)
return unless service.run_auto_devops_pipeline?
if @project.empty_repo?
flash[:warning] = "This repository is currently empty. A new Auto DevOps pipeline will be created after a new file has been pushed to a branch."
return
end
CreatePipelineWorker.perform_async(project.id, current_user.id, project.default_branch, :web, ignore_skip_ci: true, save_on_errors: false)
flash[:success] = "A new Auto DevOps pipeline has been created, go to <a href=\"#{project_pipelines_path(@project)}\">Pipelines page</a> for details".html_safe
end
def update_params
params.require(:project).permit(
:runners_token, :builds_enabled, :build_allow_git_fetch,

View File

@ -2,6 +2,7 @@
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
include BlobHelper
include SendFileUpload
before_action :require_non_empty_project
before_action :assign_ref_vars
@ -31,7 +32,7 @@ class Projects::RawController < Projects::ApplicationController
lfs_object = find_lfs_object
if lfs_object && lfs_object.project_allowed_access?(@project)
send_file lfs_object.file.path, filename: @blob.name, disposition: 'attachment'
send_upload(lfs_object.file, attachment: @blob.name)
else
render_404
end

View File

@ -36,6 +36,6 @@ class Projects::VariablesController < Projects::ApplicationController
end
def variable_params_attributes
%i[id key value protected _destroy]
%i[id key secret_value protected _destroy]
end
end

View File

@ -323,4 +323,11 @@ module ApplicationHelper
def locale_path
asset_path("locale/#{Gitlab::I18n.locale}/app.js")
end
# Overridden in EE
def read_only_message
return unless Gitlab::Database.read_only?
_('You are on a read-only GitLab instance.')
end
end

View File

@ -96,7 +96,7 @@ module ApplicationSettingsHelper
def repository_storages_options_for_select(selected)
options = Gitlab.config.repositories.storages.map do |name, storage|
["#{name} - #{storage['path']}", name]
["#{name} - #{storage['gitaly_address']}", name]
end
options_for_select(options, selected)
@ -245,7 +245,8 @@ module ApplicationSettingsHelper
:usage_ping_enabled,
:user_default_external,
:user_oauth_applications,
:version_check_enabled
:version_check_enabled,
:allow_local_requests_from_hooks_and_services
]
end
end

View File

@ -531,4 +531,22 @@ module ProjectsHelper
def can_show_last_commit_in_list?(project)
can?(current_user, :read_cross_project) && project.commit
end
def pages_https_only_disabled?
!@project.pages_domains.all?(&:https?)
end
def pages_https_only_title
return unless pages_https_only_disabled?
"You must enable HTTPS for all your domains first"
end
def pages_https_only_label_class
if pages_https_only_disabled?
"list-label disabled"
else
"list-label"
end
end
end

View File

@ -11,6 +11,14 @@ module Emails
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
def push_to_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil, new_commits: [], existing_commits: [])
setup_merge_request_mail(merge_request_id, recipient_id)
@new_commits = new_commits
@existing_commits = existing_commits
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)

View File

@ -1,5 +1,7 @@
class Appearance < ActiveRecord::Base
include CacheMarkdownField
include AfterCommitQueue
include ObjectStorage::BackgroundMove
cache_markdown_field :description
cache_markdown_field :new_project_guidelines

View File

@ -330,7 +330,8 @@ class ApplicationSetting < ActiveRecord::Base
usage_ping_enabled: Settings.gitlab['usage_ping_enabled'],
gitaly_timeout_fast: 10,
gitaly_timeout_medium: 30,
gitaly_timeout_default: 55
gitaly_timeout_default: 55,
allow_local_requests_from_hooks_and_services: false
}
end

View File

@ -3,6 +3,7 @@ module Ci
prepend ArtifactMigratable
include TokenAuthenticatable
include AfterCommitQueue
include ObjectStorage::BackgroundMove
include Presentable
include Importable
@ -45,6 +46,7 @@ module Ci
where('(artifacts_file IS NOT NULL AND artifacts_file <> ?) OR EXISTS (?)',
'', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').archive)
end
scope :with_artifacts_stored_locally, -> { with_artifacts_archive.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
@ -365,13 +367,19 @@ module Ci
project.running_or_pending_build_count(force: true)
end
def artifacts_metadata_entry(path, **options)
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
artifacts_metadata.path,
path,
**options)
def browsable_artifacts?
artifacts_metadata?
end
metadata.to_entry
def artifacts_metadata_entry(path, **options)
artifacts_metadata.use_file do |metadata_path|
metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
metadata_path,
path,
**options)
metadata.to_entry
end
end
def erase_artifacts!

View File

@ -6,6 +6,8 @@ module Ci
belongs_to :group
alias_attribute :secret_value, :value
validates :key, uniqueness: {
scope: :group_id,
message: "(%{value}) has already been taken"

View File

@ -1,5 +1,7 @@
module Ci
class JobArtifact < ActiveRecord::Base
include AfterCommitQueue
include ObjectStorage::BackgroundMove
extend Gitlab::Ci::Model
belongs_to :project
@ -7,9 +9,11 @@ module Ci
before_save :set_size, if: :file_changed?
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
mount_uploader :file, JobArtifactUploader
delegate :open, :exists?, to: :file
delegate :exists?, :open, to: :file
enum file_type: {
archive: 1,
@ -21,6 +25,10 @@ module Ci
self.where(project: project).sum(:size)
end
def local_store?
[nil, ::JobArtifactUploader::Store::LOCAL].include?(self.file_store)
end
def set_size
self.size = file.size
end

View File

@ -6,6 +6,7 @@ module Ci
include AfterCommitQueue
include Presentable
include Gitlab::OptimisticLocking
include Gitlab::Utils::StrongMemoize
belongs_to :project, inverse_of: :pipelines
belongs_to :user
@ -14,7 +15,7 @@ module Ci
has_many :stages
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable'
@ -361,21 +362,23 @@ module Ci
def stage_seeds
return [] unless config_processor
@stage_seeds ||= config_processor.stage_seeds(self)
strong_memoize(:stage_seeds) do
seeds = config_processor.stages_attributes.map do |attributes|
Gitlab::Ci::Pipeline::Seed::Stage.new(self, attributes)
end
seeds.select(&:included?)
end
end
def seeds_size
@seeds_size ||= stage_seeds.sum(&:size)
stage_seeds.sum(&:size)
end
def has_kubernetes_active?
project.deployment_platform&.active?
end
def has_stage_seeds?
stage_seeds.any?
end
def has_warnings?
builds.latest.failed_but_allowed.any?
end
@ -388,6 +391,9 @@ module Ci
end
end
##
# TODO, setting yaml_errors should be moved to the pipeline creation chain.
#
def config_processor
return unless ci_yaml_file
return @config_processor if defined?(@config_processor)
@ -472,6 +478,14 @@ module Ci
end
end
def protected_ref?
strong_memoize(:protected_ref) { project.protected_for?(ref) }
end
def legacy_trigger
strong_memoize(:legacy_trigger) { trigger_requests.first }
end
def predefined_variables
Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_PIPELINE_ID', value: id.to_s)

View File

@ -5,6 +5,8 @@ module Ci
belongs_to :pipeline_schedule
alias_attribute :secret_value, :value
validates :key, uniqueness: { scope: :pipeline_schedule_id }
end
end

View File

@ -6,6 +6,8 @@ module Ci
belongs_to :project
alias_attribute :secret_value, :value
validates :key, uniqueness: {
scope: [:project_id, :environment_scope],
message: "(%{value}) has already been taken"

View File

@ -10,6 +10,7 @@ module Clusters
Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner
}.freeze
DEFAULT_ENVIRONMENT = '*'.freeze
belongs_to :user
@ -50,6 +51,7 @@ module Clusters
scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }
scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
def status_name
if provider

View File

@ -175,7 +175,7 @@ class Commit
if safe_message.blank?
no_commit_message
else
safe_message.split("\n", 2).first
safe_message.split(/[\r\n]/, 2).first
end
end

View File

@ -3,6 +3,7 @@ module Avatarable
included do
prepend ShadowMethods
include ObjectStorage::BackgroundMove
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
@ -21,7 +22,7 @@ module Avatarable
def avatar_type
unless self.avatar.image?
self.errors.add :avatar, "only images allowed"
errors.add :avatar, "file format is not supported. Please try one of the following supported formats: #{AvatarUploader::IMAGE_EXT.join(', ')}"
end
end

View File

@ -1,16 +1,24 @@
module DeploymentPlatform
# EE would override this and utilize the extra argument
# EE would override this and utilize environment argument
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def deployment_platform(environment: nil)
@deployment_platform ||=
find_cluster_platform_kubernetes ||
find_kubernetes_service_integration ||
build_cluster_and_deployment_platform
@deployment_platform ||= {}
@deployment_platform[environment] ||= find_deployment_platform(environment)
end
private
def find_cluster_platform_kubernetes
clusters.find_by(enabled: true)&.platform_kubernetes
def find_deployment_platform(environment)
find_cluster_platform_kubernetes(environment: environment) ||
find_kubernetes_service_integration ||
build_cluster_and_deployment_platform
end
# EE would override this and utilize environment argument
def find_cluster_platform_kubernetes(environment: nil)
clusters.enabled.default_environment
.last&.platform_kubernetes
end
def find_kubernetes_service_integration

View File

@ -52,12 +52,12 @@ class Event < ActiveRecord::Base
belongs_to :target, -> {
# If the association for "target" defines an "author" association we want to
# eager-load this so Banzai & friends don't end up performing N+1 queries to
# get the authors of notes, issues, etc.
if reflections['events'].active_record.reflect_on_association(:author)
includes(:author)
else
self
# get the authors of notes, issues, etc. (likewise for "noteable").
incs = %i(author noteable).select do |a|
reflections['events'].active_record.reflect_on_association(a)
end
incs.reduce(self) { |obj, a| obj.includes(a) }
}, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
has_one :push_event_payload

View File

@ -189,12 +189,6 @@ class Group < Namespace
owners.include?(user) && owners.size == 1
end
def avatar_type
unless self.avatar.image?
self.errors.add :avatar, "only images allowed"
end
end
def post_create_hook
Gitlab::AppLogger.info("Group \"#{name}\" was created")

View File

@ -1,7 +1,12 @@
class LfsObject < ActiveRecord::Base
include AfterCommitQueue
include ObjectStorage::BackgroundMove
has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :lfs_objects_projects
scope :with_files_stored_locally, -> { where(file_store: [nil, LfsObjectUploader::Store::LOCAL]) }
validates :oid, presence: true, uniqueness: true
mount_uploader :file, LfsObjectUploader
@ -10,6 +15,10 @@ class LfsObject < ActiveRecord::Base
projects.exists?(project.lfs_storage_project.id)
end
def local_store?
[nil, LfsObjectUploader::Store::LOCAL].include?(self.file_store)
end
def self.destroy_unreferenced
joins("LEFT JOIN lfs_objects_projects ON lfs_objects_projects.lfs_object_id = #{table_name}.id")
.where(lfs_objects_projects: { id: nil })

View File

@ -48,7 +48,7 @@ class NotificationRecipient
when :custom
custom_enabled? || %i[participating mention].include?(@type)
when :watch, :participating
!excluded_watcher_action?
!action_excluded?
when :mention
@type == :mention
else
@ -96,13 +96,22 @@ class NotificationRecipient
end
end
def action_excluded?
excluded_watcher_action? || excluded_participating_action?
end
def excluded_watcher_action?
return false unless @custom_action
return false if notification_level == :custom
return false unless @custom_action && notification_level == :watch
NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@custom_action)
end
def excluded_participating_action?
return false unless @custom_action && notification_level == :participating
NotificationSetting::EXCLUDED_PARTICIPATING_EVENTS.include?(@custom_action)
end
private
def read_ability

View File

@ -33,6 +33,7 @@ class NotificationSetting < ActiveRecord::Base
:close_issue,
:reassign_issue,
:new_merge_request,
:push_to_merge_request,
:reopen_merge_request,
:close_merge_request,
:reassign_merge_request,
@ -41,10 +42,14 @@ class NotificationSetting < ActiveRecord::Base
:success_pipeline
].freeze
EXCLUDED_WATCHER_EVENTS = [
EXCLUDED_PARTICIPATING_EVENTS = [
:success_pipeline
].freeze
EXCLUDED_WATCHER_EVENTS = [
:push_to_merge_request
].push(*EXCLUDED_PARTICIPATING_EVENTS).freeze
def self.find_or_create_for(source)
setting = find_or_initialize_by(source: source)

View File

@ -6,8 +6,10 @@ class PagesDomain < ActiveRecord::Base
validates :domain, hostname: { allow_numeric_hostname: true }
validates :domain, uniqueness: { case_sensitive: false }
validates :certificate, certificate: true, allow_nil: true, allow_blank: true
validates :key, certificate_key: true, allow_nil: true, allow_blank: true
validates :certificate, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? }
validates :certificate, certificate: true, if: ->(domain) { domain.certificate.present? }
validates :key, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? }
validates :key, certificate_key: true, if: ->(domain) { domain.key.present? }
validates :verification_code, presence: true, allow_blank: false
validate :validate_pages_domain
@ -46,6 +48,10 @@ class PagesDomain < ActiveRecord::Base
!Gitlab::CurrentSettings.pages_domain_verification_enabled? || enabled_until.present?
end
def https?
certificate.present?
end
def to_param
domain
end

View File

@ -38,6 +38,9 @@ class Project < ActiveRecord::Base
attachments: 2
}.freeze
# Valids ports to import from
VALID_IMPORT_PORTS = [22, 80, 443].freeze
cache_markdown_field :description, pipeline: :description
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
@ -264,6 +267,7 @@ class Project < ActiveRecord::Base
validate :visibility_level_allowed_by_group
validate :visibility_level_allowed_as_fork
validate :check_wiki_path_conflict
validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) }
validates :repository_storage,
presence: true,
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
@ -500,7 +504,7 @@ class Project < ActiveRecord::Base
end
def repository_storage_path
Gitlab.config.repositories.storages[repository_storage].try(:[], 'path')
Gitlab.config.repositories.storages[repository_storage]&.legacy_disk_path
end
def team
@ -734,6 +738,26 @@ class Project < ActiveRecord::Base
end
end
def pages_https_only
return false unless Gitlab.config.pages.external_https
super
end
def pages_https_only?
return false unless Gitlab.config.pages.external_https
super
end
def validate_pages_https_only
return unless pages_https_only?
unless pages_domains.all?(&:https?)
errors.add(:pages_https_only, "cannot be enabled unless all domains have TLS certificates")
end
end
def to_param
if persisted? && errors.include?(:path)
path_was

View File

@ -1,6 +1,4 @@
class AssemblaService < Service
include HTTParty
prop_accessor :token, :subdomain
validates :token, presence: true, if: :activated?
@ -31,6 +29,6 @@ class AssemblaService < Service
return unless supported_events.include?(data[:object_kind])
url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}"
AssemblaService.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' })
Gitlab::HTTP.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' })
end
end

View File

@ -117,14 +117,14 @@ class BambooService < CiService
url = build_url(path)
if username.blank? && password.blank?
HTTParty.get(url, verify: false)
Gitlab::HTTP.get(url, verify: false)
else
url << '&os_authType=basic'
HTTParty.get(url, verify: false,
basic_auth: {
username: username,
password: password
})
Gitlab::HTTP.get(url, verify: false,
basic_auth: {
username: username,
password: password
})
end
end
end

View File

@ -71,7 +71,7 @@ class BuildkiteService < CiService
end
def calculate_reactive_cache(sha, ref)
response = HTTParty.get(commit_status_path(sha), verify: false)
response = Gitlab::HTTP.get(commit_status_path(sha), verify: false)
status =
if response.code == 200 && response['status']

View File

@ -1,6 +1,4 @@
class CampfireService < Service
include HTTParty
prop_accessor :token, :subdomain, :room
validates :token, presence: true, if: :activated?
@ -31,7 +29,6 @@ class CampfireService < Service
def execute(data)
return unless supported_events.include?(data[:object_kind])
self.class.base_uri base_uri
message = build_message(data)
speak(self.room, message, auth)
end
@ -69,14 +66,14 @@ class CampfireService < Service
}
}
}
res = self.class.post(path, auth.merge(body))
res = Gitlab::HTTP.post(path, base_uri: base_uri, **auth.merge(body))
res.code == 201 ? res : nil
end
# Returns a list of rooms, or [].
# https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms
def rooms(auth)
res = self.class.get("/rooms.json", auth)
res = Gitlab::HTTP.get("/rooms.json", base_uri: base_uri, **auth)
res.code == 200 ? res["rooms"] : []
end

View File

@ -49,7 +49,7 @@ class DroneCiService < CiService
end
def calculate_reactive_cache(sha, ref)
response = HTTParty.get(commit_status_path(sha, ref), verify: enable_ssl_verification)
response = Gitlab::HTTP.get(commit_status_path(sha, ref), verify: enable_ssl_verification)
status =
if response.code == 200 && response['status']

View File

@ -1,6 +1,4 @@
class ExternalWikiService < Service
include HTTParty
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, url: true, if: :activated?
@ -24,7 +22,7 @@ class ExternalWikiService < Service
end
def execute(_data)
@response = HTTParty.get(properties['external_wiki_url'], verify: true) rescue nil
@response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true) rescue nil
if @response != 200
nil
end

View File

@ -77,13 +77,13 @@ class IssueTrackerService < Service
result = false
begin
response = HTTParty.head(self.project_url, verify: true)
response = Gitlab::HTTP.head(self.project_url, verify: true)
if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
end
rescue HTTParty::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end
Rails.logger.info(message)

View File

@ -52,7 +52,7 @@ class MockCiService < CiService
#
#
def commit_status(sha, ref)
response = HTTParty.get(commit_status_path(sha), verify: false)
response = Gitlab::HTTP.get(commit_status_path(sha), verify: false)
read_commit_status(response)
rescue Errno::ECONNREFUSED
:error

View File

@ -1,6 +1,4 @@
class PackagistService < Service
include HTTParty
prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated?

View File

@ -1,6 +1,4 @@
class PivotaltrackerService < Service
include HTTParty
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze
prop_accessor :token, :restrict_to_branch
@ -52,7 +50,7 @@ class PivotaltrackerService < Service
'message' => commit[:message]
}
}
PivotaltrackerService.post(
Gitlab::HTTP.post(
API_ENDPOINT,
body: message.to_json,
headers: {

View File

@ -1,6 +1,5 @@
class PushoverService < Service
include HTTParty
base_uri 'https://api.pushover.net/1'
BASE_URI = 'https://api.pushover.net/1'.freeze
prop_accessor :api_key, :user_key, :device, :priority, :sound
validates :api_key, :user_key, :priority, presence: true, if: :activated?
@ -99,6 +98,6 @@ class PushoverService < Service
pushover_data[:sound] = sound
end
PushoverService.post('/messages.json', body: pushover_data)
Gitlab::HTTP.post('/messages.json', base_uri: BASE_URI, body: pushover_data)
end
end

View File

@ -83,7 +83,7 @@ class TeamcityService < CiService
branch = Gitlab::Git.ref_name(data[:ref])
HTTParty.post(
Gitlab::HTTP.post(
build_url('httpAuth/app/rest/buildQueue'),
body: "<build branchName=\"#{branch}\">"\
"<buildType id=\"#{build_type}\"/>"\
@ -134,10 +134,10 @@ class TeamcityService < CiService
end
def get_path(path)
HTTParty.get(build_url(path), verify: false,
basic_auth: {
username: username,
password: password
})
Gitlab::HTTP.get(build_url(path), verify: false,
basic_auth: {
username: username,
password: password
})
end
end

View File

@ -9,6 +9,8 @@ class Upload < ActiveRecord::Base
validates :model, presence: true
validates :uploader, presence: true
scope :with_files_stored_locally, -> { where(store: [nil, ObjectStorage::Store::LOCAL]) }
before_save :calculate_checksum!, if: :foreground_checksummable?
after_commit :schedule_checksum, if: :checksummable?
@ -21,6 +23,7 @@ class Upload < ActiveRecord::Base
end
def absolute_path
raise ObjectStorage::RemoteStoreError, "Remote object has no absolute path." unless local?
return path unless relative_path?
uploader_class.absolute_path(self)
@ -30,11 +33,11 @@ class Upload < ActiveRecord::Base
self.checksum = nil
return unless checksummable?
self.checksum = self.class.hexdigest(absolute_path)
self.checksum = Digest::SHA256.file(absolute_path).hexdigest
end
def build_uploader
uploader_class.new(model, mount_point, **uploader_context).tap do |uploader|
def build_uploader(mounted_as = nil)
uploader_class.new(model, mounted_as || mount_point).tap do |uploader|
uploader.upload = self
uploader.retrieve_from_store!(identifier)
end
@ -51,6 +54,12 @@ class Upload < ActiveRecord::Base
}.compact
end
def local?
return true if store.nil?
store == ObjectStorage::Store::LOCAL
end
private
def delete_file!
@ -61,10 +70,6 @@ class Upload < ActiveRecord::Base
checksum.nil? && local? && exist?
end
def local?
true
end
def foreground_checksummable?
checksummable? && size <= CHECKSUM_THRESHOLD
end

View File

@ -623,9 +623,7 @@ class User < ActiveRecord::Base
end
def owned_projects
@owned_projects ||=
Project.where('namespace_id IN (?) OR namespace_id = ?',
owned_groups.select(:id), namespace.id).joins(:namespace)
@owned_projects ||= Project.from("(#{owned_projects_union.to_sql}) AS projects")
end
# Returns projects which user can admin issues on (for example to move an issue to that project).
@ -1196,6 +1194,15 @@ class User < ActiveRecord::Base
private
def owned_projects_union
Gitlab::SQL::Union.new([
Project.where(namespace: namespace),
Project.joins(:project_authorizations)
.where("projects.namespace_id <> ?", namespace.id)
.where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER })
], remove_duplicates: false)
end
def ci_projects_union
scope = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] }
groups = groups_projects.where(members: scope)

View File

@ -7,6 +7,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Validate::Repository,
Gitlab::Ci::Pipeline::Chain::Validate::Config,
Gitlab::Ci::Pipeline::Chain::Skip,
Gitlab::Ci::Pipeline::Chain::Populate,
Gitlab::Ci::Pipeline::Chain::Create].freeze
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, &block)
@ -65,7 +66,7 @@ module Ci
project.pipelines
.where(ref: pipeline.ref)
.where.not(id: pipeline.id)
.where.not(sha: project.repository.sha_from_ref(pipeline.ref))
.where.not(sha: project.commit(pipeline.ref).try(:id))
.created_or_pending
end

View File

@ -1,20 +0,0 @@
module Ci
class CreatePipelineStagesService < BaseService
def execute(pipeline)
pipeline.stage_seeds.each do |seed|
seed.user = current_user
seed.create! do |build|
##
# Create the environment before the build starts. This sets its slug and
# makes it available as an environment variable
#
if build.has_environment?
environment_name = build.expanded_environment_name
project.environments.find_or_create_by(name: environment_name)
end
end
end
end
end
end

View File

@ -16,8 +16,8 @@ module Ci
pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: params[:ref])
.execute(:trigger, ignore_skip_ci: true) do |pipeline|
pipeline.trigger_requests.create!(trigger: trigger)
create_pipeline_variables!(pipeline)
pipeline.trigger_requests.build(trigger: trigger)
pipeline.variables.build(variables)
end
if pipeline.persisted?
@ -33,14 +33,10 @@ module Ci
end
end
def create_pipeline_variables!(pipeline)
return unless params[:variables]
variables = params[:variables].map do |key, value|
def variables
params[:variables].to_h.map do |key, value|
{ key: key, value: value }
end
pipeline.variables.create!(variables)
end
end
end

View File

@ -21,7 +21,7 @@ module MergeRequests
comment_mr_branch_presence_changed
end
comment_mr_with_commits
notify_about_push
mark_mr_as_wip_from_commits
execute_mr_web_hooks
@ -141,8 +141,8 @@ module MergeRequests
end
end
# Add comment about pushing new commits to merge requests
def comment_mr_with_commits
# Add comment about pushing new commits to merge requests and send nofitication emails
def notify_about_push
return unless @commits.present?
merge_requests_for_source_branch.each do |merge_request|
@ -155,6 +155,8 @@ module MergeRequests
SystemNoteService.add_commits(merge_request, merge_request.project,
@current_user, new_commits,
existing_commits, @oldrev)
notification_service.push_to_merge_request(merge_request, @current_user, new_commits: new_commits, existing_commits: existing_commits)
end
end

View File

@ -113,6 +113,16 @@ class NotificationService
new_resource_email(merge_request, :new_merge_request_email)
end
def push_to_merge_request(merge_request, current_user, new_commits: [], existing_commits: [])
new_commits = new_commits.map { |c| { short_id: c.short_id, title: c.title } }
existing_commits = existing_commits.map { |c| { short_id: c.short_id, title: c.title } }
recipients = NotificationRecipientService.build_recipients(merge_request, current_user, action: "push_to")
recipients.each do |recipient|
mailer.send(:push_to_merge_request_email, recipient.user.id, merge_request.id, current_user.id, recipient.reason, new_commits: new_commits, existing_commits: existing_commits).deliver_later
end
end
# When merge request text is updated, we should send an email to:
#
# * newly mentioned project team members with notification level higher than Participating

View File

@ -28,7 +28,7 @@ module Projects
def add_repository_to_project
if project.external_import? && !unknown_url?
raise Error, 'Blocked import URL.' if Gitlab::UrlBlocker.blocked_url?(project.import_url)
raise Error, 'Blocked import URL.' if Gitlab::UrlBlocker.blocked_url?(project.import_url, valid_ports: Project::VALID_IMPORT_PORTS)
end
# We should skip the repository for a GitHub import or GitLab project import,

View File

@ -18,7 +18,8 @@ module Projects
def pages_config
{
domains: pages_domains_config
domains: pages_domains_config,
https_only: project.pages_https_only?
}
end
@ -27,7 +28,8 @@ module Projects
{
domain: domain.domain,
certificate: domain.certificate,
key: domain.key
key: domain.key,
https_only: project.pages_https_only? && domain.https?
}
end
end

View File

@ -81,11 +81,13 @@ module Projects
end
def extract_tar_archive!(temp_path)
results = Open3.pipeline(%W(gunzip -c #{artifacts}),
%W(dd bs=#{BLOCK_SIZE} count=#{blocks}),
%W(tar -x -C #{temp_path} #{SITE_PATH}),
err: '/dev/null')
raise FailedToExtractError, 'pages failed to extract' unless results.compact.all?(&:success?)
build.artifacts_file.use_file do |artifacts_path|
results = Open3.pipeline(%W(gunzip -c #{artifacts_path}),
%W(dd bs=#{BLOCK_SIZE} count=#{blocks}),
%W(tar -x -C #{temp_path} #{SITE_PATH}),
err: '/dev/null')
raise FailedToExtractError, 'pages failed to extract' unless results.compact.all?(&:success?)
end
end
def extract_zip_archive!(temp_path)
@ -103,8 +105,10 @@ module Projects
# -n never overwrite existing files
# We add * to end of SITE_PATH, because we want to extract SITE_PATH and all subdirectories
site_path = File.join(SITE_PATH, '*')
unless system(*%W(unzip -qq -n #{artifacts} #{site_path} -d #{temp_path}))
raise FailedToExtractError, 'pages failed to extract'
build.artifacts_file.use_file do |artifacts_path|
unless system(*%W(unzip -n #{artifacts_path} #{site_path} -d #{temp_path}))
raise FailedToExtractError, 'pages failed to extract'
end
end
end

View File

@ -24,6 +24,8 @@ module Projects
system_hook_service.execute_hooks_for(project, :update)
end
update_pages_config if changing_pages_https_only?
success
else
model_errors = project.errors.full_messages.to_sentence
@ -67,5 +69,13 @@ module Projects
log_error("Could not create wiki for #{project.full_name}")
Gitlab::Metrics.counter(:wiki_can_not_be_created_total, 'Counts the times we failed to create a wiki')
end
def update_pages_config
Projects::UpdatePagesConfigurationService.new(project).execute
end
def changing_pages_https_only?
project.previous_changes.include?(:pages_https_only)
end
end
end

Some files were not shown because too many files have changed in this diff Show More