Commit ae11fa56 authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'ce/master' into rc/ce-to-ee-monday

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parents 5f1cc738 b83bc417
...@@ -15,15 +15,20 @@ variables: ...@@ -15,15 +15,20 @@ variables:
GIT_DEPTH: "20" GIT_DEPTH: "20"
PHANTOMJS_VERSION: "2.1.1" PHANTOMJS_VERSION: "2.1.1"
GET_SOURCES_ATTEMPTS: "3" GET_SOURCES_ATTEMPTS: "3"
<<<<<<< HEAD
# This hack is needed to make ES not that memory hungry # This hack is needed to make ES not that memory hungry
ES_JAVA_OPTS: "-Xms600m -Xmx600m" ES_JAVA_OPTS: "-Xms600m -Xmx600m"
=======
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-${CI_COMMIT_REF_SLUG}.json
KNAPSACK_SPINACH_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/spinach_report-${CI_COMMIT_REF_SLUG}.json
>>>>>>> ce/master
before_script: before_script:
- source ./scripts/prepare_build.sh - source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml - cp config/gitlab.yml.example config/gitlab.yml
- bundle --version - bundle --version
- '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) --clean $FLAGS' - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) --clean $FLAGS'
- retry gem install knapsack - retry gem install knapsack fog-aws mime-types
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql' - '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql'
- '[ "$SETUP_DB" != "true" ] || bundle exec rake geo:db:drop geo:db:create geo:db:schema:load geo:db:migrate' - '[ "$SETUP_DB" != "true" ] || bundle exec rake geo:db:drop geo:db:create geo:db:schema:load geo:db:migrate'
...@@ -43,14 +48,15 @@ stages: ...@@ -43,14 +48,15 @@ stages:
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
KNAPSACK_S3_BUCKET: "gitlab-ce-cache"
cache: cache:
key: "knapsack" key: "knapsack"
paths: paths:
- knapsack/ - knapsack/
artifacts: artifacts:
expire_in: 31d expire_in: 31d
paths: paths:
- knapsack/ - knapsack/
.use-db: &use-db .use-db: &use-db
services: services:
...@@ -66,17 +72,17 @@ stages: ...@@ -66,17 +72,17 @@ stages:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]} - export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true - export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/rspec_report.json ${KNAPSACK_REPORT_PATH} - cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
- knapsack rspec "--color --format documentation" - knapsack rspec "--color --format documentation"
artifacts: artifacts:
expire_in: 31d expire_in: 31d
when: always when: always
paths: paths:
- coverage/ - coverage/
- knapsack/ - knapsack/
- tmp/capybara/ - tmp/capybara/
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test stage: test
...@@ -86,28 +92,44 @@ stages: ...@@ -86,28 +92,44 @@ stages:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]} - export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]} - export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true - export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH} - cp ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
- knapsack spinach "-r rerun" || retry '[[ -e tmp/spinach-rerun.txt ]] && bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)' - knapsack spinach "-r rerun" || retry '[[ -e tmp/spinach-rerun.txt ]] && bundle exec spinach -r rerun $(cat tmp/spinach-rerun.txt)'
artifacts: artifacts:
expire_in: 31d expire_in: 31d
when: always when: always
paths: paths:
- coverage/ - coverage/
- knapsack/ - knapsack/
- tmp/capybara/ - tmp/capybara/
# Prepare and merge knapsack tests # Prepare and merge knapsack tests
knapsack: knapsack:
<<: *knapsack-state <<: *knapsack-state
<<: *dedicated-runner <<: *dedicated-runner
stage: prepare stage: prepare
script: script:
- mkdir -p knapsack/ - mkdir -p knapsack/${CI_PROJECT_NAME}/
- '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json' - wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || true
- '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json' - wget -O $KNAPSACK_SPINACH_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_SPINACH_SUITE_REPORT_PATH || true
- '[[ -f $KNAPSACK_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${KNAPSACK_RSPEC_SUITE_REPORT_PATH}'
- '[[ -f $KNAPSACK_SPINACH_SUITE_REPORT_PATH ]] || echo "{}" > ${KNAPSACK_SPINACH_SUITE_REPORT_PATH}'
update-knapsack:
<<: *knapsack-state
<<: *dedicated-runner
stage: post-test
script:
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_node_*.json
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach_node_*.json
- '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
setup-test-env: setup-test-env:
<<: *use-db <<: *use-db
...@@ -127,20 +149,6 @@ setup-test-env: ...@@ -127,20 +149,6 @@ setup-test-env:
- public/assets - public/assets
- tmp/tests - tmp/tests
update-knapsack:
<<: *knapsack-state
<<: *dedicated-runner
stage: post-test
script:
- scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
rspec 0 20: *rspec-knapsack rspec 0 20: *rspec-knapsack
rspec 1 20: *rspec-knapsack rspec 1 20: *rspec-knapsack
rspec 2 20: *rspec-knapsack rspec 2 20: *rspec-knapsack
...@@ -201,6 +209,28 @@ rake brakeman: *exec ...@@ -201,6 +209,28 @@ rake brakeman: *exec
rake flay: *exec rake flay: *exec
license_finder: *exec license_finder: *exec
rake downtime_check: *exec rake downtime_check: *exec
<<<<<<< HEAD
=======
rake ee_compat_check:
<<: *exec
only:
- branches@gitlab-org/gitlab-ce
except:
- master
- tags
- /^[\d-]+-stable(-ee)?$/
allow_failure: yes
cache:
key: "ee_compat_check_repo"
paths:
- ee_compat_check/ee-repo/
artifacts:
name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}"
when: on_failure
expire_in: 10d
paths:
- ee_compat_check/patches/*.patch
>>>>>>> ce/master
rake db:migrate:reset: rake db:migrate:reset:
stage: test stage: test
...@@ -266,6 +296,7 @@ rake karma: ...@@ -266,6 +296,7 @@ rake karma:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
script: script:
- bundle exec rake karma - bundle exec rake karma
coverage: '/^Statements *: (\d+\.\d+%)/'
artifacts: artifacts:
name: coverage-javascript name: coverage-javascript
expire_in: 31d expire_in: 31d
...@@ -297,7 +328,7 @@ bundler:audit: ...@@ -297,7 +328,7 @@ bundler:audit:
- master@gitlab/gitlabhq - master@gitlab/gitlabhq
- master@gitlab/gitlab-ee - master@gitlab/gitlab-ee
script: script:
- "bundle exec bundle-audit check --update" - "bundle exec bundle-audit check --update --ignore CVE-2016-4658"
migration paths: migration paths:
stage: test stage: test
...@@ -331,6 +362,7 @@ coverage: ...@@ -331,6 +362,7 @@ coverage:
USE_BUNDLE_INSTALL: "true" USE_BUNDLE_INSTALL: "true"
script: script:
- bundle exec scripts/merge-simplecov - bundle exec scripts/merge-simplecov
coverage: '/LOC \((\d+\.\d+%)\) covered.$/'
artifacts: artifacts:
name: coverage name: coverage
expire_in: 31d expire_in: 31d
......
...@@ -255,7 +255,7 @@ gem 'base32', '~> 0.3.0' ...@@ -255,7 +255,7 @@ gem 'base32', '~> 0.3.0'
gem "gitlab-license", "~> 1.0" gem "gitlab-license", "~> 1.0"
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 2.0.0' gem 'sentry-raven', '~> 2.4.0'
gem 'premailer-rails', '~> 1.9.0' gem 'premailer-rails', '~> 1.9.0'
...@@ -268,15 +268,14 @@ end ...@@ -268,15 +268,14 @@ end
group :development do group :development do
gem 'foreman', '~> 0.78.0' gem 'foreman', '~> 0.78.0'
gem 'brakeman', '~> 3.4.0', require: false gem 'brakeman', '~> 3.6.0', require: false
gem 'letter_opener_web', '~> 1.3.0' gem 'letter_opener_web', '~> 1.3.0'
gem 'bullet', '~> 5.2.0', require: false gem 'bullet', '~> 5.5.0', require: false
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
gem 'web-console', '~> 2.0'
# Better errors handler # Better errors handler
gem 'better_errors', '~> 1.0.1' gem 'better_errors', '~> 2.1.0'
gem 'binding_of_caller', '~> 0.7.2' gem 'binding_of_caller', '~> 0.7.2'
# thin instead webrick # thin instead webrick
...@@ -308,7 +307,7 @@ group :development, :test do ...@@ -308,7 +307,7 @@ group :development, :test do
gem 'capybara-screenshot', '~> 1.0.0' gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.9.0' gem 'poltergeist', '~> 1.9.0'
gem 'spring', '~> 1.7.0' gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
...@@ -316,8 +315,8 @@ group :development, :test do ...@@ -316,8 +315,8 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.12.0', require: false gem 'rubocop-rspec', '~> 1.12.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.21.0', require: false gem 'haml_lint', '~> 0.21.0', require: false
gem 'simplecov', '0.12.0', require: false gem 'simplecov', '~> 0.14.0', require: false
gem 'flay', '~> 2.6.1', require: false gem 'flay', '~> 2.8.0', require: false
gem 'bundler-audit', '~> 0.5.0', require: false gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false gem 'benchmark-ips', '~> 2.3.0', require: false
...@@ -334,7 +333,7 @@ group :test do ...@@ -334,7 +333,7 @@ group :test do
gem 'shoulda-matchers', '~> 2.8.0', require: false gem 'shoulda-matchers', '~> 2.8.0', require: false
gem 'email_spec', '~> 1.6.0' gem 'email_spec', '~> 1.6.0'
gem 'json-schema', '~> 2.6.2' gem 'json-schema', '~> 2.6.2'
gem 'webmock', '~> 1.21.0' gem 'webmock', '~> 1.24.0'
gem 'test_after_commit', '~> 1.1' gem 'test_after_commit', '~> 1.1'
gem 'sham_rack', '~> 1.3.6' gem 'sham_rack', '~> 1.3.6'
gem 'timecop', '~> 0.8.0' gem 'timecop', '~> 0.8.0'
......
...@@ -83,19 +83,20 @@ GEM ...@@ -83,19 +83,20 @@ GEM
base32 (0.3.2) base32 (0.3.2)
bcrypt (3.1.11) bcrypt (3.1.11)
benchmark-ips (2.3.0) benchmark-ips (2.3.0)
better_errors (1.0.1) better_errors (2.1.1)
coderay (>= 1.0.0) coderay (>= 1.0.0)
erubis (>= 2.6.6) erubis (>= 2.6.6)
rack (>= 0.9.0)
bindata (2.3.5) bindata (2.3.5)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootstrap-sass (3.3.6) bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1) autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4) sass (>= 3.3.4)
brakeman (3.4.1) brakeman (3.6.1)
browser (2.2.0) browser (2.2.0)
builder (3.2.3) builder (3.2.3)
bullet (5.2.0) bullet (5.5.1)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0) uniform_notifier (~> 1.10.0)
bundler-audit (0.5.0) bundler-audit (0.5.0)
...@@ -109,7 +110,7 @@ GEM ...@@ -109,7 +110,7 @@ GEM
rack (>= 1.0.0) rack (>= 1.0.0)
rack-test (>= 0.5.4) rack-test (>= 0.5.4)
xpath (~> 2.0) xpath (~> 2.0)
capybara-screenshot (1.0.11) capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3) capybara (>= 1.0, < 3)
launchy launchy
carrierwave (0.11.2) carrierwave (0.11.2)
...@@ -125,7 +126,7 @@ GEM ...@@ -125,7 +126,7 @@ GEM
numerizer (~> 0.1.1) numerizer (~> 0.1.1)
chunky_png (1.3.5) chunky_png (1.3.5)
cliver (0.3.2) cliver (0.3.2)
coderay (1.1.0) coderay (1.1.1)
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
coffee-rails (4.1.1) coffee-rails (4.1.1)
...@@ -224,7 +225,9 @@ GEM ...@@ -224,7 +225,9 @@ GEM
multi_json multi_json
ffaker (2.4.0) ffaker (2.4.0)
ffi (1.9.10) ffi (1.9.10)
flay (2.6.1) flay (2.8.1)
erubis (~> 2.7.0)
path_expander (~> 1.0)
ruby_parser (~> 3.0) ruby_parser (~> 3.0)
sexp_processor (~> 4.0) sexp_processor (~> 4.0)
flowdock (0.7.1) flowdock (0.7.1)
...@@ -375,6 +378,7 @@ GEM ...@@ -375,6 +378,7 @@ GEM
temple (~> 0.7.6) temple (~> 0.7.6)
thor thor
tilt tilt
hashdiff (0.3.2)
hashie (3.5.5) hashie (3.5.5)
health_check (2.6.0) health_check (2.6.0)
rails (>= 4.0) rails (>= 4.0)
...@@ -554,6 +558,7 @@ GEM ...@@ -554,6 +558,7 @@ GEM
activerecord (>= 4.0, < 5.1) activerecord (>= 4.0, < 5.1)
parser (2.4.0.0) parser (2.4.0.0)
ast (~> 2.2) ast (~> 2.2)
path_expander (1.0.1)
pg (0.18.4) pg (0.18.4)
poltergeist (1.9.0) poltergeist (1.9.0)
capybara (~> 2.1) capybara (~> 2.1)
...@@ -568,14 +573,14 @@ GEM ...@@ -568,14 +573,14 @@ GEM
premailer-rails (1.9.2) premailer-rails (1.9.2)
actionmailer (>= 3, < 6) actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
pry (0.10.3) pry (0.10.4)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.8.1) method_source (~> 0.8.1)
slop (~> 3.4) slop (~> 3.4)
pry-byebug (3.4.1) pry-byebug (3.4.2)
byebug (~> 9.0) byebug (~> 9.0)
pry (~> 0.10) pry (~> 0.10)
pry-rails (0.3.4) pry-rails (0.3.5)
pry (>= 0.9.10) pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
rack (1.6.5) rack (1.6.5)
...@@ -707,7 +712,7 @@ GEM ...@@ -707,7 +712,7 @@ GEM
ruby-progressbar (1.8.1) ruby-progressbar (1.8.1)
ruby-saml (1.4.1) ruby-saml (1.4.1)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
ruby_parser (3.8.2) ruby_parser (3.8.4)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
...@@ -736,10 +741,10 @@ GEM ...@@ -736,10 +741,10 @@ GEM
activesupport (>= 3.1) activesupport (>= 3.1)
select2-rails (3.5.9.3) select2-rails (3.5.9.3)
thor (~> 0.14) thor (~> 0.14)
sentry-raven (2.0.2) sentry-raven (2.4.0)
faraday (>= 0.7.6, < 0.10.x) faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9) settingslogic (2.0.9)
sexp_processor (4.7.0) sexp_processor (4.8.0)
sham_rack (1.3.6) sham_rack (1.3.6)
rack rack
shoulda-matchers (2.8.0) shoulda-matchers (2.8.0)
...@@ -760,7 +765,7 @@ GEM ...@@ -760,7 +765,7 @@ GEM
faraday (~> 0.9) faraday (~> 0.9)
jwt (~> 1.5) jwt (~> 1.5)
multi_json (~> 1.10) multi_json (~> 1.10)
simplecov (0.12.0) simplecov (0.14.1)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
...@@ -777,7 +782,8 @@ GEM ...@@ -777,7 +782,8 @@ GEM
spinach (>= 0.4) spinach (>= 0.4)
spinach-rerun-reporter (0.0.2) spinach-rerun-reporter (0.0.2)
spinach (~> 0.8) spinach (~> 0.8)
spring (1.7.2) spring (2.0.1)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4) spring-commands-rspec (1.0.4)
spring (>= 0.9.1) spring (>= 0.9.1)
spring-commands-spinach (1.1.0) spring-commands-spinach (1.1.0)
...@@ -849,14 +855,10 @@ GEM ...@@ -849,14 +855,10 @@ GEM
vmstat (2.3.0) vmstat (2.3.0)
warden (1.2.6) warden (1.2.6)
rack (>= 1.0) rack (>= 1.0)
web-console (2.3.0) webmock (1.24.6)
activemodel (>= 4.0)
binding_of_caller (>= 0.7.2)
railties (>= 4.0)
sprockets-rails (>= 2.0, < 4.0)
webmock (1.21.0)
addressable (>= 2.3.6) addressable (>= 2.3.6)
crack (>= 0.3.2) crack (>= 0.3.2)
hashdiff
webpack-rails (0.9.9) webpack-rails (0.9.9)
rails (>= 3.2.0) rails (>= 3.2.0)
websocket-driver (0.6.3) websocket-driver (0.6.3)
...@@ -891,12 +893,12 @@ DEPENDENCIES ...@@ -891,12 +893,12 @@ DEPENDENCIES
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
benchmark-ips (~> 2.3.0) benchmark-ips (~> 2.3.0)
better_errors (~> 1.0.1) better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0) bootstrap-sass (~> 3.3.0)
brakeman (~> 3.4.0) brakeman (~> 3.6.0)
browser (~> 2.2) browser (~> 2.2)
bullet (~> 5.2.0) bullet (~> 5.5.0)
bundler-audit (~> 0.5.0) bundler-audit (~> 0.5.0)
capybara (~> 2.6.2) capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0) capybara-screenshot (~> 1.0.0)
...@@ -926,7 +928,7 @@ DEPENDENCIES ...@@ -926,7 +928,7 @@ DEPENDENCIES
factory_girl_rails (~> 4.7.0) factory_girl_rails (~> 4.7.0)
faraday_middleware-aws-signers-v4 faraday_middleware-aws-signers-v4
ffaker (~> 2.4) ffaker (~> 2.4)
flay (~> 2.6.1) flay (~> 2.8.0)
fog-aws (~> 0.9) fog-aws (~> 0.9)
fog-core (~> 1.40) fog-core (~> 1.40)
fog-google (~> 0.5) fog-google (~> 0.5)
...@@ -1036,18 +1038,18 @@ DEPENDENCIES ...@@ -1036,18 +1038,18 @@ DEPENDENCIES
scss_lint (~> 0.47.0) scss_lint (~> 0.47.0)
seed-fu (~> 2.3.5) seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
sentry-raven (~> 2.0.0) sentry-raven (~> 2.4.0)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6) sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2.7) sidekiq (~> 4.2.7)
sidekiq-cron (~> 0.4.4) sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4) sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0) simplecov (~> 0.14.0)
slack-notifier (~> 1.5.1) slack-notifier (~> 1.5.1)
spinach-rails (~> 0.2.1) spinach-rails (~> 0.2.1)
spinach-rerun-reporter (~> 0.0.2) spinach-rerun-reporter (~> 0.0.2)
spring (~> 1.7.0) spring (~> 2.0.0)
spring-commands-rspec (~> 1.0.4) spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.1.0) spring-commands-spinach (~> 1.1.0)
sprockets (~> 3.7.0) sprockets (~> 3.7.0)
...@@ -1068,8 +1070,7 @@ DEPENDENCIES ...@@ -1068,8 +1070,7 @@ DEPENDENCIES
version_sorter (~> 2.1.0) version_sorter (~> 2.1.0)
virtus (~> 1.0.1) virtus (~> 1.0.1)
vmstat (~> 2.3.0) vmstat (~> 2.3.0)
web-console (~> 2.0) webmock (~> 1.24.0)
webmock (~> 1.21.0)
webpack-rails (~> 0.9.9) webpack-rails (~> 0.9.9)
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
......
# GitLab # GitLab
<<<<<<< HEAD
[![Build status](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ee/commits/master) [![Build status](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ee/commits/master)
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) [![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
=======
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Overall test coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-ce/pipelines)
>>>>>>> ce/master
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42) [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
## Test coverage
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=rake+karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
## Canonical source ## Canonical source
The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/). The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/).
......
...@@ -14,6 +14,8 @@ export default { ...@@ -14,6 +14,8 @@ export default {
this.filteredSearch = new FilteredSearchBoards(this.store); this.filteredSearch = new FilteredSearchBoards(this.store);
this.filteredSearch.removeTokens(); this.filteredSearch.removeTokens();
this.filteredSearch.handleInputPlaceholder();
this.filteredSearch.toggleClearSearchButton();
}, },
destroyed() { destroyed() {
this.filteredSearch.cleanup(); this.filteredSearch.cleanup();
......
...@@ -29,6 +29,8 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager { ...@@ -29,6 +29,8 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
[].forEach.call(tokens, (el) => { [].forEach.call(tokens, (el) => {
el.parentNode.removeChild(el); el.parentNode.removeChild(el);
}); });
this.filteredSearchInput.value = '';
} }
updateTokens() { updateTokens() {
......
/* eslint-disable no-new*/
/* global Flash */
import Vue from 'vue'; import Vue from 'vue';
import PipelinesTableComponent from '../../vue_shared/components/pipelines_table'; import PipelinesTableComponent from '../../vue_shared/components/pipelines_table';
import PipelinesService from '../../vue_pipelines_index/services/pipelines_service'; import PipelinesService from '../../vue_pipelines_index/services/pipelines_service';
import PipelineStore from '../../vue_pipelines_index/stores/pipelines_store'; import PipelineStore from '../../vue_pipelines_index/stores/pipelines_store';
import eventHub from '../../vue_pipelines_index/event_hub'; import eventHub from '../../vue_pipelines_index/event_hub';
import EmptyState from '../../vue_pipelines_index/components/empty_state';
import ErrorState from '../../vue_pipelines_index/components/error_state';
import '../../lib/utils/common_utils'; import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor'; import '../../vue_shared/vue_resource_interceptor';
...@@ -22,6 +22,8 @@ import '../../vue_shared/vue_resource_interceptor'; ...@@ -22,6 +22,8 @@ import '../../vue_shared/vue_resource_interceptor';
export default Vue.component('pipelines-table', { export default Vue.component('pipelines-table', {
components: { components: {
'pipelines-table-component': PipelinesTableComponent, 'pipelines-table-component': PipelinesTableComponent,
'error-state': ErrorState,
'empty-state': EmptyState,
}, },
/** /**
...@@ -36,12 +38,24 @@ export default Vue.component('pipelines-table', { ...@@ -36,12 +38,24 @@ export default Vue.component('pipelines-table', {
return { return {
endpoint: pipelinesTableData.endpoint, endpoint: pipelinesTableData.endpoint,
helpPagePath: pipelinesTableData.helpPagePath,
store, store,
state: store.state, state: store.state,
isLoading: false, isLoading: false,
hasError: false,
}; };
}, },
computed: {
shouldRenderErrorState() {
return this.hasError && !this.isLoading;
},
shouldRenderEmptyState() {
return !this.state.pipelines.length && !this.isLoading;
},
},
/** /**
* When the component is about to be mounted, tell the service to fetch the data * When the component is about to be mounted, tell the service to fetch the data
* *
...@@ -80,26 +94,25 @@ export default Vue.component('pipelines-table', { ...@@ -80,26 +94,25 @@ export default Vue.component('pipelines-table', {
this.isLoading = false; this.isLoading = false;
}) })
.catch(() => { .catch(() => {
this.hasError = true;
this.isLoading = false; this.isLoading = false;
new Flash('An error occurred while fetching the pipelines, please reload the page again.');
}); });
}, },
}, },
template: ` template: `
<div class="pipelines"> <div class="content-list pipelines">
<div class="realtime-loading" v-if="isLoading"> <div class="realtime-loading" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i> <i class="fa fa-spinner fa-spin"></i>
</div> </div>
<div class="blank-state blank-state-no-icon" <empty-state
v-if="!isLoading && state.pipelines.length === 0"> v-if="shouldRenderEmptyState"
<h2 class="blank-state-title js-blank-state-title"> :help-page-path="helpPagePath" />
No pipelines to show
</h2> <error-state v-if="shouldRenderErrorState" />
</div>
<div class="table-holder pipelines" <div class="table-holder"
v-if="!isLoading && state.pipelines.length > 0"> v-if="!isLoading && state.pipelines.length > 0">
<pipelines-table-component <pipelines-table-component
:pipelines="state.pipelines" :pipelines="state.pipelines"
......
// ECMAScript polyfills // ECMAScript polyfills
import 'core-js/fn/array/find'; import 'core-js/fn/array/find';
import 'core-js/fn/array/from';
import 'core-js/fn/object/assign'; import 'core-js/fn/object/assign';
import 'core-js/fn/promise'; import 'core-js/fn/promise';
import 'core-js/fn/string/code-point-at'; import 'core-js/fn/string/code-point-at';
import 'core-js/fn/string/from-code-point'; import 'core-js/fn/string/from-code-point';
import 'core-js/fn/symbol';
// Browser polyfills // Browser polyfills
import './polyfills/custom_event'; import './polyfills/custom_event';
......
export default {
props: {
count: {
type: Number,
required: true,
},
},
template: `
<span v-if="count === 50" class="events-info pull-right">
<i class="fa fa-warning has-tooltip"
aria-hidden="true"
title="Limited to showing 50 events at most"
data-placement="top"></i>
Showing 50 events
</span>
`,
};
...@@ -14,6 +14,7 @@ import Vue from 'vue'; ...@@ -14,6 +14,7 @@ import Vue from 'vue';
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" />
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item"> <li v-for="mergeRequest in items" class="stage-event-item">
......
...@@ -14,6 +14,7 @@ import Vue from 'vue'; ...@@ -14,6 +14,7 @@ import Vue from 'vue';
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" />
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item"> <li v-for="issue in items" class="stage-event-item">
......
...@@ -19,12 +19,7 @@ import iconCommit from '../svg/icon_commit.svg'; ...@@ -19,12 +19,7 @@ import iconCommit from '../svg/icon_commit.svg';
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<span v-if="items.length === 50" class="events-info pull-right"> <limit-warning :count="items.length" />
<i class="fa fa-warning has-tooltip"
title="Limited to showing 50 events at most"
data-placement="top"></i>
Showing 50 events
</span>
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="commit in items" class="stage-event-item"> <li v-for="commit in items" class="stage-event-item">
......
...@@ -14,6 +14,7 @@ import Vue from 'vue'; ...@@ -14,6 +14,7 @@ import Vue from 'vue';
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" />
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item"> <li v-for="issue in items" class="stage-event-item">
......
...@@ -14,6 +14,7 @@ import Vue from 'vue'; ...@@ -14,6 +14,7 @@ import Vue from 'vue';
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" />
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item"> <li v-for="mergeRequest in items" class="stage-event-item">
......
...@@ -17,6 +17,7 @@ import iconBranch from '../svg/icon_branch.svg'; ...@@ -17,6 +17,7 @@ import iconBranch from '../svg/icon_branch.svg';
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" />
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component"> <li v-for="build in items" class="stage-event-item item-build-component">
......
...@@ -18,6 +18,7 @@ import iconBranch from '../svg/icon_branch.svg'; ...@@ -18,6 +18,7 @@ import iconBranch from '../svg/icon_branch.svg';
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" />
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component"> <li v-for="build in items" class="stage-event-item item-build-component">
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import Vue from 'vue'; import Vue from 'vue';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import LimitWarningComponent from './components/limit_warning_component';
require('./components/stage_code_component'); require('./components/stage_code_component');
require('./components/stage_issue_component'); require('./components/stage_issue_component');
...@@ -130,5 +131,6 @@ $(() => { ...@@ -130,5 +131,6 @@ $(() => {
}); });
// Register global components // Register global components
Vue.component('limit-warning', LimitWarningComponent);
Vue.component('total-time', gl.cycleAnalytics.TotalTimeComponent); Vue.component('total-time', gl.cycleAnalytics.TotalTimeComponent);
}); });
...@@ -33,11 +33,7 @@ class Diff { ...@@ -33,11 +33,7 @@ class Diff {
handleClickUnfold(e) { handleClickUnfold(e) {
const $target = $(e.target); const $target = $(e.target);
// current babel config relies on iterators implementation, so we cannot simply do: const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
// const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
const ref = this.lineNumbers($target.parent());
const oldLineNumber = ref[0];
const newLineNumber = ref[1];
const offset = newLineNumber - oldLineNumber; const offset = newLineNumber - oldLineNumber;
const bottom = $target.hasClass('js-unfold-bottom'); const bottom = $target.hasClass('js-unfold-bottom');
let since; let since;
...@@ -105,10 +101,11 @@ class Diff { ...@@ -105,10 +101,11 @@ class Diff {
} }
lineNumbers(line) { lineNumbers(line) {
if (!line.children().length) { const children = line.find('.diff-line-num').toArray();
if (children.length !== 2) {
return [0, 0]; return [0, 0];
} }
return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10)); return children.map(elm => parseInt($(elm).data('linenumber'), 10) || 0);
} }
highlightSelectedLine() { highlightSelectedLine() {
......
...@@ -56,10 +56,12 @@ require('../window')(function(w){ ...@@ -56,10 +56,12 @@ require('../window')(function(w){
this.hookInput = hookInput; this.hookInput = hookInput;
this.hookInput.trigger.addEventListener('keyup.dl', this.keydownWrapper); this.hookInput.trigger.addEventListener('keyup.dl', this.keydownWrapper);
this.hookInput.trigger.addEventListener('mousedown.dl', this.keydownWrapper);
}, },
destroy: function destroy(){ destroy: function destroy(){
this.hookInput.trigger.removeEventListener('keyup.dl', this.keydownWrapper); this.hookInput.trigger.removeEventListener('keyup.dl', this.keydownWrapper);
this.hookInput.trigger.removeEventListener('mousedown.dl', this.keydownWrapper);
} }
}; };
}); });
......
const GROUP_LIMIT = 2;
import _ from 'underscore';
export default class GroupName { export default class GroupName {
constructor() { constructor() {
this.titleContainer = document.querySelector('.title'); this.titleContainer = document.querySelector('.title-container');
this.groups = document.querySelectorAll('.group-path'); this.title = document.querySelector('.title');
this.titleWidth = this.title.offsetWidth;
this.groupTitle = document.querySelector('.group-title'); this.groupTitle = document.querySelector('.group-title');
this.groups = document.querySelectorAll('.group-path');
this.toggle = null; this.toggle = null;
this.isHidden = false; this.isHidden = false;
this.init(); this.init();
} }
init() { init() {
if (this.groups.length > GROUP_LIMIT) { if (this.groups.length > 0) {
this.groups[this.groups.length - 1].classList.remove('hidable'); this.groups[this.groups.length - 1].classList.remove('hidable');
this.addToggle(); this.toggleHandler();
window.addEventListener('resize', _.debounce(this.toggleHandler.bind(this), 100));
} }
this.render(); this.render();
} }
addToggle() { toggleHandler() {
const header = document.querySelector('.header-content'); if (this.titleWidth > this.titleContainer.offsetWidth) {
if (!this.toggle) this.createToggle();
this.showToggle();
} else if (this.toggle) {
this.hideToggle();
}
}
createToggle() {
this.toggle = document.createElement('button'); this.toggle = document.createElement('button');
this.toggle.className = 'text-expander group-name-toggle'; this.toggle.className = 'text-expander group-name-toggle';
this.toggle.setAttribute('aria-label', 'Toggle full path'); this.toggle.setAttribute('aria-label', 'Toggle full path');
this.toggle.innerHTML = '...'; this.toggle.innerHTML = '...';
this.toggle.addEventListener('click', this.toggleGroups.bind(this)); this.toggle.addEventListener('click', this.toggleGroups.bind(this));
header.insertBefore(this.toggle, this.titleContainer); this.titleContainer.insertBefore(this.toggle, this.title);
this.toggleGroups(); this.toggleGroups();
} }
showToggle() {
this.title.classList.add('wrap');
this.toggle.classList.remove('hidden');
if (this.isHidden) this.groupTitle.classList.add('is-hidden');
}
hideToggle() {
this.title.classList.remove('wrap');
this.toggle.classList.add('hidden');
if (this.isHidden) this.groupTitle.classList.remove('is-hidden');
}
toggleGroups() { toggleGroups() {
this.isHidden = !this.isHidden; this.isHidden = !this.isHidden;
this.groupTitle.classList.toggle('is-hidden'); this.groupTitle.classList.toggle('is-hidden');
} }
render() { render() {
this.titleContainer.classList.remove('initializing'); this.title.classList.remove('initializing');
} }
} }
...@@ -36,20 +36,21 @@ export default class Poll { ...@@ -36,20 +36,21 @@ export default class Poll {
this.options.data = options.data || {}; this.options.data = options.data || {};
this.intervalHeader = 'POLL-INTERVAL'; this.intervalHeader = 'POLL-INTERVAL';
this.timeoutID = null;
this.canPoll = true;
} }
checkConditions(response) { checkConditions(response) {
const headers = gl.utils.normalizeHeaders(response.headers); const headers = gl.utils.normalizeHeaders(response.headers);
const pollInterval = headers[this.intervalHeader]; const pollInterval = headers[this.intervalHeader];
if (pollInterval > 0 && response.status === httpStatusCodes.OK) { if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
this.options.successCallback(response); this.timeoutID = setTimeout(() => {
setTimeout(() => {
this.makeRequest(); this.makeRequest();
}, pollInterval); }, pollInterval);
} else {
this.options.successCallback(response);
} }
this.options.successCallback(response);
} }
makeRequest() { makeRequest() {
...@@ -59,4 +60,14 @@ export default class Poll { ...@@ -59,4 +60,14 @@ export default class Poll {
.then(response => this.checkConditions(response)) .then(response => this.checkConditions(response))
.catch(error => errorCallback(error)); .catch(error => errorCallback(error));
} }
/**
* Stops the polling recursive chain
* and guarantees if the timeout is already running it won't make another request by
* cancelling the previously established timeout.
*/
stop() {
this.canPoll = false;
clearTimeout(this.timeoutID);
}
} }
...@@ -127,9 +127,6 @@ require('./flash'); ...@@ -127,9 +127,6 @@ require('./flash');
if (this.diffViewType() === 'parallel') { if (this.diffViewType() === 'parallel') {
this.expandViewContainer(); this.expandViewContainer();
} }
$.scrollTo('.merge-request-details .merge-request-tabs', {
offset: 0,
});
} else if (action === 'pipelines') { } else if (action === 'pipelines') {
if (this.pipelinesLoaded) { if (this.pipelinesLoaded) {
return; return;
......
...@@ -200,7 +200,7 @@ import Cookies from 'js-cookie'; ...@@ -200,7 +200,7 @@ import Cookies from 'js-cookie';
Sidebar.prototype.setSidebarHeight = function() { Sidebar.prototype.setSidebarHeight = function() {
const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight(); const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
const $rightSidebar = $('.js-right-sidebar'); const $rightSidebar = $('.js-right-sidebar');
const diff = $navHeight - $('body').scrollTop(); const diff = $navHeight - $(window).scrollTop();
if (diff > 0) { if (diff > 0) {
$rightSidebar.outerHeight($(window).height() - diff); $rightSidebar.outerHeight($(window).height() - diff);
} else { } else {
......
import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg';
export default {
props: {
helpPagePath: {
type: String,
required: true,
},
},
template: `
<div class="row empty-state">
<div class="col-xs-12">
<div class="svg-content">
${pipelinesEmptyStateSVG}
</div>
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>Build with confidence</h4>
<p>
Continous Integration can help catch bugs by running your tests automatically,
while Continuous Deployment can help you deliver code to your product environment.
</p>
<a :href="helpPagePath" class="btn btn-info">
Get started with Pipelines
</a>
</div>
</div>
</div>
`,
};
import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg';
export default {
template: `
<div class="row empty-state js-pipelines-error-state">
<div class="col-xs-12">
<div class="svg-content">
${pipelinesErrorStateSVG}
</div>
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>The API failed to fetch the pipelines.</h4>
</div>
</div>
</div>
`,
};
export default {
props: {
newPipelinePath: {
type: String,
required: true,
},
hasCiEnabled: {
type: Boolean,
required: true,
},
helpPagePath: {
type: String,
required: true,
},
ciLintPath: {
type: String,
required: true,
},
canCreatePipeline: {
type: Boolean,
required: true,
},
},
template: `
<div class="nav-controls">
<a
v-if="canCreatePipeline"
:href="newPipelinePath"
class="btn btn-create">
Run Pipeline
</a>
<a
v-if="!hasCiEnabled"
:href="helpPagePath"
class="btn btn-info">
Get started with Pipelines
</a>
<a
:href="ciLintPath"
class="btn btn-default">
CI Lint
</a>
</div>
`,
};
export default {
props: {
scope: {
type: String,
required: true,
},
count: {
type: Object,
required: true,
},
paths: {
type: Object,
required: true,
},
},
template: `
<ul class="nav-links">
<li
class="js-pipelines-tab-all"
:class="{ 'active': scope === 'all'}">
<a :href="paths.allPath">
All
<span class="badge js-totalbuilds-count">
{{count.all}}
</span>
</a>
</li>
<li class="js-pipelines-tab-pending"
:class="{ 'active': scope === 'pending'}">
<a :href="paths.pendingPath">
Pending
<span class="badge">
{{count.pending}}
</span>
</a>
</li>
<li class="js-pipelines-tab-running"
:class="{ 'active': scope === 'running'}">
<a :href="paths.runningPath">
Running
<span class="badge">
{{count.running}}
</span>
</a>
</li>
<li class="js-pipelines-tab-finished"
:class="{ 'active': scope === 'finished'}">
<a :href="paths.finishedPath">
Finished
<span class="badge">
{{count.finished}}
</span>
</a>
</li>
<li class="js-pipelines-tab-branches"
:class="{ 'active': scope === 'branches'}">
<a :href="paths.branchesPath">Branches</a>
</li>
<li class="js-pipelines-tab-tags"
:class="{ 'active': scope === 'tags'}">
<a :href="paths.tagsPath">Tags</a>
</li>
</ul>
`,
};
...@@ -4,23 +4,19 @@ import PipelinesComponent from './pipelines'; ...@@ -4,23 +4,19 @@ import PipelinesComponent from './pipelines';
import '../vue_shared/vue_resource_interceptor'; import '../vue_shared/vue_resource_interceptor';
$(() => new Vue({ $(() => new Vue({
el: document.querySelector('.vue-pipelines-index'), el: document.querySelector('#pipelines-list-vue'),
data() { data() {
const project = document.querySelector('.pipelines');
const store = new PipelinesStore(); const store = new PipelinesStore();
return { return {
store, store,
endpoint: project.dataset.url,
}; };
}, },
components: { components: {
'vue-pipelines': PipelinesComponent, 'vue-pipelines': PipelinesComponent,
}, },
template: ` template: `
<vue-pipelines <vue-pipelines :store="store" />
:endpoint="endpoint"
:store="store" />
`, `,
})); }));
/* global Flash */
/* eslint-disable no-new */
import '~/flash';
import Vue from 'vue'; import Vue from 'vue';
import PipelinesService from './services/pipelines_service'; import PipelinesService from './services/pipelines_service';
import eventHub from './event_hub'; import eventHub from './event_hub';
import PipelinesTableComponent from '../vue_shared/components/pipelines_table'; import PipelinesTableComponent from '../vue_shared/components/pipelines_table';
import TablePaginationComponent from '../vue_shared/components/table_pagination'; import TablePaginationComponent from '../vue_shared/components/table_pagination';
import EmptyState from './components/empty_state';
import ErrorState from './components/error_state';
import NavigationTabs from './components/navigation_tabs';
import NavigationControls from './components/nav_controls';
export default { export default {
props: { props: {
endpoint: {
type: String,
required: true,
},
store: { store: {
type: Object, type: Object,
required: true, required: true,
...@@ -23,17 +19,109 @@ export default { ...@@ -23,17 +19,109 @@ export default {
components: { components: {
'gl-pagination': TablePaginationComponent, 'gl-pagination': TablePaginationComponent,
'pipelines-table-component': PipelinesTableComponent, 'pipelines-table-component': PipelinesTableComponent,
'empty-state': EmptyState,
'error-state': ErrorState,
'navigation-tabs': NavigationTabs,
'navigation-controls': NavigationControls,
}, },
data() { data() {
const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
return { return {
endpoint: pipelinesData.endpoint,
cssClass: pipelinesData.cssClass,
helpPagePath: pipelinesData.helpPagePath,
newPipelinePath: pipelinesData.newPipelinePath,
canCreatePipeline: pipelinesData.canCreatePipeline,
allPath: pipelinesData.allPath,
pendingPath: pipelinesData.pendingPath,
runningPath: pipelinesData.runningPath,
finishedPath: pipelinesData.finishedPath,
branchesPath: pipelinesData.branchesPath,
tagsPath: pipelinesData.tagsPath,
hasCi: pipelinesData.hasCi,
ciLintPath: pipelinesData.ciLintPath,
state: this.store.state, state: this.store.state,
apiScope: 'all', apiScope: 'all',
pagenum: 1, pagenum: 1,
pageRequest: false, isLoading: false,
hasError: false,
}; };
}, },
computed: {
canCreatePipelineParsed() {
return gl.utils.convertPermissionToBoolean(this.canCreatePipeline);
},
scope() {
const scope = gl.utils.getParameterByName('scope');
return scope === null ? 'all' : scope;
},
shouldRenderErrorState() {
return this.hasError && !this.isLoading;
},
/**
* The empty state should only be rendered when the request is made to fetch all pipelines
* and none is returned.
*
* @return {Boolean}
*/
shouldRenderEmptyState() {
return !this.isLoading &&
!this.hasError &&
!this.state.pipelines.length &&
(this.scope === 'all' || this.scope === null);
},
/**
* When a specific scope does not have pipelines we render a message.
*
* @return {Boolean}
*/
shouldRenderNoPipelinesMessage() {
return !this.isLoading &&
!this.hasError &&
!this.state.pipelines.length &&
this.scope !== 'all' &&
this.scope !== null;
},
shouldRenderTable() {
return !this.hasError &&
!this.isLoading && this.state.pipelines.length;
},
/**
* Pagination should only be rendered when there is more than one page.
*
* @return {Boolean}
*/
shouldRenderPagination() {
return !this.isLoading &&
this.state.pipelines.length &&
this.state.pageInfo.total > this.state.pageInfo.perPage;
},
hasCiEnabled() {
return this.hasCi !== undefined;
},
paths() {
return {
allPath: this.allPath,
pendingPath: this.pendingPath,
finishedPath: this.finishedPath,
runningPath: this.runningPath,
branchesPath: this.branchesPath,
tagsPath: this.tagsPath,
};
},
},
created() { created() {
this.service = new PipelinesService(this.endpoint); this.service = new PipelinesService(this.endpoint);
...@@ -69,7 +157,7 @@ export default { ...@@ -69,7 +157,7 @@ export default {
const pageNumber = gl.utils.getParameterByName('page') || this.pagenum; const pageNumber = gl.utils.getParameterByName('page') || this.pagenum;
const scope = gl.utils.getParameterByName('scope') || this.apiScope; const scope = gl.utils.getParameterByName('scope') || this.apiScope;
this.pageRequest = true; this.isLoading = true;
return this.service.getPipelines(scope, pageNumber) return this.service.getPipelines(scope, pageNumber)
.then(resp => ({ .then(resp => ({
headers: resp.headers, headers: resp.headers,
...@@ -81,41 +169,72 @@ export default { ...@@ -81,41 +169,72 @@ export default {
this.store.storePagination(response.headers); this.store.storePagination(response.headers);
}) })
.then(() => { .then(() => {
this.pageRequest = false; this.isLoading = false;
}) })
.catch(() => { .catch(() => {
this.pageRequest = false; this.hasError = true;
new Flash('An error occurred while fetching the pipelines, please reload the page again.'); this.isLoading = false;
}); });
}, },
}, },
template: `
<div>
<div class="pipelines realtime-loading" v-if="pageRequest">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
<div class="blank-state blank-state-no-icon" template: `
v-if="!pageRequest && state.pipelines.length === 0"> <div :class="cssClass">
<h2 class="blank-state-title js-blank-state-title">
No pipelines to show <div
</h2> class="top-area"
v-if="!isLoading && !shouldRenderEmptyState">
<navigation-tabs
:scope="scope"
:count="state.count"
:paths="paths" />
<navigation-controls
:new-pipeline-path="newPipelinePath"
:has-ci-enabled="hasCiEnabled"
:help-page-path="helpPagePath"
:ciLintPath="ciLintPath"
:can-create-pipeline="canCreatePipelineParsed " />
</div> </div>
<div class="table-holder" v-if="!pageRequest && state.pipelines.length"> <div class="content-list pipelines">
<pipelines-table-component
:pipelines="state.pipelines" <div
:service="service"/> class="realtime-loading"
v-if="isLoading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</div>
<empty-state
v-if="shouldRenderEmptyState"
:help-page-path="helpPagePath" />
<error-state v-if="shouldRenderErrorState" />
<div
class="blank-state blank-state-no-icon"
v-if="shouldRenderNoPipelinesMessage">
<h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
</div>
<div
class="table-holder"
v-if="shouldRenderTable">
<pipelines-table-component
:pipelines="state.pipelines"
:service="service"/>
</div>
<gl-pagination
v-if="shouldRenderPagination"
:pagenum="pagenum"
:change="change"
:count="state.count.all"
:pageInfo="state.pageInfo"/>
</div> </div>
<gl-pagination
v-if="!pageRequest && state.pipelines.length && state.pageInfo.total > state.pageInfo.perPage"
:pagenum="pagenum"
:change="change"
:count="state.count.all"
:pageInfo="state.pageInfo"
>
</gl-pagination>
</div> </div>
`, `,
}; };
...@@ -211,3 +211,11 @@ label { ...@@ -211,3 +211,11 @@ label {
color: $gl-text-color; color: $gl-text-color;
} }
} }
@media(max-width: $screen-xs-max) {
.remember-me {
.remember-me-checkbox {
margin-top: 0;
}
}
}
...@@ -26,7 +26,7 @@ header { ...@@ -26,7 +26,7 @@ header {
padding: 0 16px; padding: 0 16px;
z-index: 100; z-index: 100;
margin-bottom: 0; margin-bottom: 0;
height: $header-height; min-height: $header-height;
background-color: $gray-light; background-color: $gray-light;
border: none; border: none;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
...@@ -85,7 +85,7 @@ header { ...@@ -85,7 +85,7 @@ header {
.navbar-toggle { .navbar-toggle {
color: $nav-toggle-gray; color: $nav-toggle-gray;
margin: 6px 0; margin: 7px 0;
border-radius: 0; border-radius: 0;
position: absolute; position: absolute;
right: -10px; right: -10px;
...@@ -135,12 +135,14 @@ header { ...@@ -135,12 +135,14 @@ header {
} }
.header-content { .header-content {
display: flex;
justify-content: space-between;
position: relative; position: relative;
height: $header-height; min-height: $header-height;
padding-left: 30px; padding-left: 30px;
@media (min-width: $screen-sm-min) { @media (max-width: $screen-sm-max) {
padding-right: 0; padding-right: 20px;
} }
.dropdown-menu { .dropdown-menu {
...@@ -165,8 +167,7 @@ header { ...@@ -165,8 +167,7 @@ header {
} }
.group-name-toggle { .group-name-toggle {
margin: 0 5px; margin: 3px 5px;
vertical-align: sub;
} }
.group-title { .group-title {
...@@ -177,39 +178,32 @@ header { ...@@ -177,39 +178,32 @@ header {
} }
} }
.title-container {
display: flex;
align-items: flex-start;
flex: 1 1 auto;
padding-top: (($header-height - 19) / 2);
overflow: hidden;
}
.title { .title {
position: relative; position: relative;
padding-right: 20px; padding-right: 20px;
margin: 0; margin: 0;
font-size: 18px; font-size: 18px;
max-width: 385px; line-height: 22px;
display: inline-block; display: inline-block;
line-height: $header-height;
font-weight: normal; font-weight: normal;
color: $gl-text-color; color: $gl-text-color;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top; vertical-align: top;
white-space: nowrap; white-space: nowrap;
&.initializing { &.wrap {
display: none; white-space: normal;
}
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
max-width: 300px;
}
@media (max-width: $screen-xs-max) {
max-width: 190px;
}
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
max-width: 428px;
} }
@media (min-width: $screen-lg-min) { &.initializing {
max-width: 685px; opacity: 0;
} }
a { a {
...@@ -226,10 +220,10 @@ header { ...@@ -226,10 +220,10 @@ header {
border: transparent; border: transparent;
background: transparent; background: transparent;
position: absolute; position: absolute;
top: 2px;
right: 3px; right: 3px;
width: 12px; width: 12px;
line-height: 19px; line-height: 19px;
margin-top: (($header-height - 19) / 2);
padding: 0; padding: 0;
font-size: 10px; font-size: 10px;
text-align: center; text-align: center;
...@@ -247,7 +241,7 @@ header { ...@@ -247,7 +241,7 @@ header {
} }
.navbar-collapse { .navbar-collapse {
float: right; flex: 0 0 auto;
border-top: none; border-top: none;
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {
...@@ -255,7 +249,7 @@ header { ...@@ -255,7 +249,7 @@ header {
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
float: none; flex: 1 1 auto;
} }
} }
} }
......
...@@ -416,14 +416,16 @@ ...@@ -416,14 +416,16 @@
.page-with-layout-nav { .page-with-layout-nav {
.right-sidebar { .right-sidebar {
top: ($header-height * 2) + 2; top: ($header-height + 1) * 2;
} }
.build-sidebar { &.page-with-sub-nav {
top: ($header-height * 3) + 3; .right-sidebar {
top: ($header-height + 1) * 3;
&.affix { &.affix {
top: 0; top: 0;
}
} }
} }
} }
......
...@@ -176,6 +176,10 @@ summary { ...@@ -176,6 +176,10 @@ summary {
&.panel-without-border { &.panel-without-border {
border: 0; border: 0;
} }
&.panel-without-margin {
margin: 0;
}
} }
.panel-succes .panel-heading, .panel-succes .panel-heading,
......
...@@ -366,9 +366,3 @@ ...@@ -366,9 +366,3 @@
right: 0; right: 0;
margin-top: -17px; margin-top: -17px;
} }
@media (min-width: $screen-md-min) {
.sub-nav.build {
width: calc(100% + #{$gutter_width});
}
}
...@@ -142,7 +142,9 @@ ...@@ -142,7 +142,9 @@
border: 1px solid $border-gray-dark; border: 1px solid $border-gray-dark;
border-radius: $border-radius-default; border-radius: $border-radius-default;
margin-left: 5px; margin-left: 5px;
line-height: 1; font-size: $gl-font-size;
line-height: $gl-font-size;
outline: none;
&:hover { &:hover {
background-color: darken($gray-light, 10%); background-color: darken($gray-light, 10%);
......
...@@ -18,7 +18,10 @@ ...@@ -18,7 +18,10 @@
.environments-container { .environments-container {
.table-holder { .table-holder {
width: 100%; width: 100%;
overflow: auto;
@media (max-width: $screen-sm-max) {
overflow: auto;
}
} }
.table.ci-table { .table.ci-table {
......
...@@ -9,9 +9,17 @@ ...@@ -9,9 +9,17 @@
} }
} }
<<<<<<< HEAD
.content-list .group-name { .content-list .group-name {
font-weight: 600; font-weight: 600;
color: $pages-group-name-color; color: $pages-group-name-color;
=======
.group-root-path {
max-width: 40vw;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: nowrap;
>>>>>>> ce/master
} }
.group-row { .group-row {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
.realtime-loading { .realtime-loading {
font-size: 40px; font-size: 40px;
text-align: center; text-align: center;
margin: 0 auto;
} }
.stage { .stage {
...@@ -13,9 +14,16 @@ ...@@ -13,9 +14,16 @@
white-space: nowrap; white-space: nowrap;
} }
.empty-state {
margin: 5% auto 0;
}
.table-holder { .table-holder {
width: 100%; width: 100%;
overflow: auto;
@media (max-width: $screen-sm-max) {
overflow: auto;
}
} }
.commit-title { .commit-title {
...@@ -99,8 +107,6 @@ ...@@ -99,8 +107,6 @@
@media (max-width: $screen-md-max) { @media (max-width: $screen-md-max) {
.content-list { .content-list {
&.pipelines,
&.environments-container,
&.builds-content-list { &.builds-content-list {
width: 100%; width: 100%;
overflow: auto; overflow: auto;
......
...@@ -582,54 +582,55 @@ pre.light-well { ...@@ -582,54 +582,55 @@ pre.light-well {
/* /*
* Projects list rendered on dashboard and user page * Projects list rendered on dashboard and user page
*/ */
.projects-list { .projects-list {
@include basic-list; @include basic-list;
display: flex;
flex-direction: column;
.project-row { .project-row {
border-color: $white-normal; display: flex;
align-items: center;
.project-full-name { }
@include str-truncated;
@media (max-width: $screen-xs-max) { h3 {
max-width: 50%; font-size: $gl-font-size;
} }
}
.controls { a {
line-height: $list-text-height; color: $gl-text-color;
}
.badge { .avatar-container,
@media (max-width: $screen-xs-max) { .controls {
display: none; flex: 0 0 auto;
} }
}
a:hover { .avatar-container {
text-decoration: none; align-self: flex-start;
} }
> span { .project-details {
margin-left: 10px; min-width: 0;
}
svg { p,
position: relative; .commit-row-message {
top: 2px; @include str-truncated(100%);
} margin-bottom: 0;
} }
}
.description p { .controls {
@media (max-width: $screen-xs-max) { margin-left: auto;
max-width: 50%;
}
}
} }
.bottom { .ci-status-link {
padding-top: $gl-padding; display: inline-block;
padding-bottom: 0; line-height: 17px;
vertical-align: middle;
&:hover {
text-decoration: none;
}
} }
} }
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
.todo-avatar, .todo-avatar,
.todo-actions { .todo-actions {
@include transition(opacity);
-webkit-flex: 0 0 auto; -webkit-flex: 0 0 auto;
flex: 0 0 auto; flex: 0 0 auto;
} }
...@@ -67,21 +68,34 @@ ...@@ -67,21 +68,34 @@
flex: 0 1 100%; flex: 0 1 100%;
min-width: 0; min-width: 0;
} }
}
.todos-list > .todo.todo-pending.done-reversible { &.todo-pending.done-reversible {
background-color: $gray-light; background-color: $white-light;
&:hover { &:hover {
border-color: $border-color; border-color: $white-dark;
} background-color: $gray-light;
.title { .todo-avatar,
font-weight: normal; .todo-item {
opacity: .6;
}
}
.todo-avatar,
.todo-item {
opacity: .2;
}
.btn {
background-color: $gray-light;
}
} }
} }
.todo-item { .todo-item {
@include transition(opacity);
.todo-title { .todo-title {
display: flex; display: flex;
......
...@@ -95,18 +95,14 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -95,18 +95,14 @@ class Admin::UsersController < Admin::ApplicationController
def create def create
opts = { opts = {
force_random_password: true, reset_password: true,
password_expires_at: nil skip_confirmation: true
} }
@user = User.new(user_params.merge(opts)) @user = Users::CreateService.new(current_user, user_params.merge(opts)).execute
@user.created_by_id = current_user.id
@user.generate_password
@user.generate_reset_token
@user.skip_confirmation!
respond_to do |format| respond_to do |format|
if @user.save if @user.persisted?
format.html { redirect_to [:admin, @user], notice: 'User was successfully created.' } format.html { redirect_to [:admin, @user], notice: 'User was successfully created.' }
format.json { render json: @user, status: :created, location: @user } format.json { render json: @user, status: :created, location: @user }
else else
......
class Projects::BuildsController < Projects::ApplicationController class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all] before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry, :play] before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry, :play]
before_action :authorize_update_build!, except: [:index, :show, :status, :raw] before_action :authorize_update_build!, except: [:index, :show, :status, :raw, :trace]
layout 'project' layout 'project'
def index def index
...@@ -74,7 +74,9 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -74,7 +74,9 @@ class Projects::BuildsController < Projects::ApplicationController
end end
def status def status
render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) render json: BuildSerializer
.new(project: @project, user: @current_user)
.represent_status(@build)
end end
def erase def erase
......
...@@ -35,10 +35,16 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -35,10 +35,16 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def disable def disable
<<<<<<< HEAD
load_key load_key
@project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy
log_audit_event(@key.title, action: :destroy) log_audit_event(@key.title, action: :destroy)
=======
deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
return render_404 unless deploy_key_project
>>>>>>> ce/master
deploy_key_project.destroy!
redirect_to_repository_settings(@project) redirect_to_repository_settings(@project)
end end
......
...@@ -273,4 +273,13 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -273,4 +273,13 @@ class Projects::IssuesController < Projects::ApplicationController
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: [] :milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
) )
end end
def authenticate_user!
return if current_user
notice = "Please sign in to create the new issue."
store_location_for :user, request.fullpath
redirect_to new_user_session_path, notice: notice
end
end end
...@@ -10,9 +10,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -10,9 +10,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled before_action :module_enabled
before_action :merge_request, only: [ before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check, :edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check,
<<<<<<< HEAD
:ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues, :ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues,
# EE # EE
:approve, :approvals, :unapprove, :rebase :approve, :approvals, :unapprove, :rebase
=======
:ci_status, :pipeline_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues
>>>>>>> ce/master
] ]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines] before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines]
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines] before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
...@@ -100,31 +104,31 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -100,31 +104,31 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diffs def diffs
apply_diff_view_cookie! apply_diff_view_cookie!
@merge_request_diff =
if params[:diff_id]
@merge_request.merge_request_diffs.viewable.find(params[:diff_id])
else
@merge_request.merge_request_diff
end
@merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
@comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
if params[:start_sha].present?
@start_sha = params[:start_sha]
@start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
unless @start_version
@start_sha = @merge_request_diff.head_commit_sha
@start_version = @merge_request_diff
end
end
@environment = @merge_request.environments_for(current_user).last
respond_to do |format| respond_to do |format|
format.html { define_discussion_vars } format.html { define_discussion_vars }
format.json do format.json do
@merge_request_diff =
if params[:diff_id]
@merge_request.merge_request_diffs.viewable.find(params[:diff_id])
else
@merge_request.merge_request_diff
end
@merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
@comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
if params[:start_sha].present?
@start_sha = params[:start_sha]
@start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
unless @start_version
@start_sha = @merge_request_diff.head_commit_sha
@start_version = @merge_request_diff
end
end
@environment = @merge_request.environments_for(current_user).last
if @start_sha if @start_sha
compared_diff_version compared_diff_version
else else
...@@ -428,7 +432,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -428,7 +432,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if params[:ref].present? if params[:ref].present?
@ref = params[:ref] @ref = params[:ref]
@commit = @repository.commit(@ref) @commit = @repository.commit("refs/heads/#{@ref}")
end end
render layout: false render layout: false
...@@ -439,7 +443,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -439,7 +443,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if params[:ref].present? if params[:ref].present?
@ref = params[:ref] @ref = params[:ref]
@commit = @target_project.commit(@ref) @commit = @target_project.commit("refs/heads/#{@ref}")
end end
render layout: false render layout: false
...@@ -499,6 +503,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -499,6 +503,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render json: response render json: response
end end
def pipeline_status
render json: PipelineSerializer
.new(project: @project, user: @current_user)
.represent_status(@merge_request.head_pipeline)
end
def ci_environments_status def ci_environments_status
environments = environments =
begin begin
......
...@@ -13,11 +13,14 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -13,11 +13,14 @@ class Projects::MilestonesController < Projects::ApplicationController
def index def index
@milestones = @milestones =
case params[:state] case params[:state]
when 'all' then @project.milestones.reorder(due_date: :desc, title: :asc) when 'all' then @project.milestones
when 'closed' then @project.milestones.closed.reorder(due_date: :desc, title: :asc) when 'closed' then @project.milestones.closed
else @project.milestones.active.reorder(due_date: :asc, title: :asc) else @project.milestones.active
end end
@sort = params[:sort] || 'due_date_asc'
@milestones = @milestones.sort(@sort)
@milestones = @milestones.includes(:project) @milestones = @milestones.includes(:project)
respond_to do |format| respond_to do |format|
format.html do format.html do
......
...@@ -72,6 +72,12 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -72,6 +72,12 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
end end
def status
render json: PipelineSerializer
.new(project: @project, user: @current_user)
.represent_status(@pipeline)
end
def stage def stage
@stage = pipeline.stage(params[:stage]) @stage = pipeline.stage(params[:stage])
return not_found unless @stage return not_found unless @stage
......
class RegistrationsController < Devise::RegistrationsController class RegistrationsController < Devise::RegistrationsController
before_action :signup_enabled?
include Recaptcha::Verify include Recaptcha::Verify
def new def new
...@@ -21,6 +20,8 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -21,6 +20,8 @@ class RegistrationsController < Devise::RegistrationsController
flash.delete :recaptcha_error flash.delete :recaptcha_error
render action: 'new' render action: 'new'
end end
rescue Gitlab::Access::AccessDeniedError
redirect_to(new_user_session_path)
end end
def destroy def destroy
...@@ -50,12 +51,6 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -50,12 +51,6 @@ class RegistrationsController < Devise::RegistrationsController
private private
def signup_enabled?
unless current_application_settings.signup_enabled?
redirect_to(new_user_session_path)
end
end
def sign_up_params def sign_up_params
params.require(:user).permit(:username, :email, :email_confirmation, :name, :password) params.require(:user).permit(:username, :email, :email_confirmation, :name, :password)
end end
...@@ -65,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -65,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController
end end
def resource def resource
@resource ||= User.new(sign_up_params) @resource ||= Users::CreateService.new(current_user, sign_up_params).build
end end
def devise_mapping def devise_mapping
......
...@@ -20,8 +20,17 @@ class LabelsFinder < UnionFinder ...@@ -20,8 +20,17 @@ class LabelsFinder < UnionFinder
if project? if project?
if project if project
label_ids << project.group.labels if project.group.present? if project.group.present?
label_ids << project.labels labels_table = Label.arel_table
label_ids << Label.where(
labels_table[:type].eq('GroupLabel').and(labels_table[:group_id].eq(project.group.id)).or(
labels_table[:type].eq('ProjectLabel').and(labels_table[:project_id].eq(project.id))
)
)
else
label_ids << project.labels
end
end end
else else
label_ids << Label.where(group_id: projects.group_ids) label_ids << Label.where(group_id: projects.group_ids)
......
...@@ -89,10 +89,12 @@ module MilestonesHelper ...@@ -89,10 +89,12 @@ module MilestonesHelper
content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" } content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
content.slice!("about ") content.slice!("about ")
content << " remaining" content << " remaining"
content.html_safe
elsif milestone.start_date && milestone.start_date.past? elsif milestone.start_date && milestone.start_date.past?
days = milestone.elapsed_days days = milestone.elapsed_days
content = content_tag(:strong, days) content = content_tag(:strong, days)
content << " #{'day'.pluralize(days)} elapsed" content << " #{'day'.pluralize(days)} elapsed"
content.html_safe
end end
end end
......
...@@ -6,7 +6,13 @@ module NamespacesHelper ...@@ -6,7 +6,13 @@ module NamespacesHelper
def namespaces_options(selected = :current_user, display_path: false, extra_group: nil) def namespaces_options(selected = :current_user, display_path: false, extra_group: nil)
groups = current_user.owned_groups + current_user.masters_groups groups = current_user.owned_groups + current_user.masters_groups
groups << extra_group if extra_group && !Group.exists?(name: extra_group.name) unless extra_group.nil? || extra_group.is_a?(Group)
extra_group = Group.find(extra_group) if Namespace.find(extra_group).kind == 'group'
end
if extra_group && extra_group.is_a?(Group) && (!Group.exists?(name: extra_group.name) || Ability.allowed?(current_user, :read_group, extra_group))
groups |= [extra_group]
end
users = [current_user.namespace] users = [current_user.namespace]
......
...@@ -31,7 +31,11 @@ module NavHelper ...@@ -31,7 +31,11 @@ module NavHelper
end end
def layout_nav_class def layout_nav_class
"page-with-layout-nav" if defined?(nav) && nav class_name = ''
class_name << " page-with-layout-nav" if defined?(nav) && nav
class_name << " page-with-sub-nav" if content_for?(:sub_nav)
class_name
end end
def nav_control_class def nav_control_class
......
...@@ -2,6 +2,7 @@ module SortingHelper ...@@ -2,6 +2,7 @@ module SortingHelper
def sort_options_hash def sort_options_hash
{ {
sort_value_name => sort_title_name, sort_value_name => sort_title_name,
sort_value_name_desc => sort_title_name_desc,
sort_value_recently_updated => sort_title_recently_updated, sort_value_recently_updated => sort_title_recently_updated,
sort_value_oldest_updated => sort_title_oldest_updated, sort_value_oldest_updated => sort_title_oldest_updated,
sort_value_recently_created => sort_title_recently_created, sort_value_recently_created => sort_title_recently_created,
...@@ -52,6 +53,17 @@ module SortingHelper ...@@ -52,6 +53,17 @@ module SortingHelper
} }
end end
def milestone_sort_options_hash
{
sort_value_name => sort_title_name_asc,
sort_value_name_desc => sort_title_name_desc,
sort_value_due_date_soon => sort_title_due_date_soon,
sort_value_due_date_later => sort_title_due_date_later,
sort_value_start_date_soon => sort_title_start_date_soon,
sort_value_start_date_later => sort_title_start_date_later,
}
end
def sort_title_priority def sort_title_priority
'Priority' 'Priority'
end end
...@@ -92,6 +104,14 @@ module SortingHelper ...@@ -92,6 +104,14 @@ module SortingHelper
'Due later' 'Due later'
end end
def sort_title_start_date_soon
'Start soon'
end
def sort_title_start_date_later
'Start later'
end
def sort_title_name def sort_title_name
'Name' 'Name'
end end
...@@ -212,6 +232,14 @@ module SortingHelper ...@@ -212,6 +232,14 @@ module SortingHelper
'due_date_desc' 'due_date_desc'
end end
def sort_value_start_date_soon
'start_date_asc'
end
def sort_value_start_date_later
'start_date_desc'
end
def sort_value_name def sort_value_name
'name_asc' 'name_asc'
end end
......
module UsersHelper
def user_link(user)
link_to(user.name, user_path(user),
title: user.email,
class: 'has-tooltip commit-committer-link')
end
end
...@@ -161,8 +161,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -161,8 +161,10 @@ class MergeRequest < ActiveRecord::Base
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def self.in_projects(relation) def self.in_projects(relation)
source = where(source_project_id: relation).select(:id) # unscoping unnecessary conditions that'll be applied
target = where(target_project_id: relation).select(:id) # when executing `where("merge_requests.id IN (#{union.to_sql})")`
source = unscoped.where(source_project_id: relation).select(:id)
target = unscoped.where(target_project_id: relation).select(:id)
union = Gitlab::SQL::Union.new([source, target]) union = Gitlab::SQL::Union.new([source, target])
where("merge_requests.id IN (#{union.to_sql})") where("merge_requests.id IN (#{union.to_sql})")
......
...@@ -109,6 +109,21 @@ class Milestone < ActiveRecord::Base ...@@ -109,6 +109,21 @@ class Milestone < ActiveRecord::Base
end end
end end
def self.sort(method)
case method.to_s
when 'due_date_asc'
reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC'))
when 'due_date_desc'
reorder(Gitlab::Database.nulls_last_order('due_date', 'DESC'))
when 'start_date_asc'
reorder(Gitlab::Database.nulls_last_order('start_date', 'ASC'))
when 'start_date_desc'
reorder(Gitlab::Database.nulls_last_order('start_date', 'DESC'))
else
order_by(method)
end
end
## ##
# Returns the String necessary to reference this Milestone in Markdown # Returns the String necessary to reference this Milestone in Markdown
# #
......
...@@ -356,20 +356,15 @@ class Project < ActiveRecord::Base ...@@ -356,20 +356,15 @@ class Project < ActiveRecord::Base
ntable = Namespace.arel_table ntable = Namespace.arel_table
pattern = "%#{query}%" pattern = "%#{query}%"
projects = select(:id).where( # unscoping unnecessary conditions that'll be applied
# when executing `where("projects.id IN (#{union.to_sql})")`
projects = unscoped.select(:id).where(
ptable[:path].matches(pattern). ptable[:path].matches(pattern).
or(ptable[:name].matches(pattern)). or(ptable[:name].matches(pattern)).
or(ptable[:description].matches(pattern)) or(ptable[:description].matches(pattern))
) )
# We explicitly remove any eager loading clauses as they're: namespaces = unscoped.select(:id).
#
# 1. Not needed by this query
# 2. Combined with .joins(:namespace) lead to all columns from the
# projects & namespaces tables being selected, leading to a SQL error
# due to the columns of all UNION'd queries no longer being the same.
namespaces = select(:id).
except(:includes).
joins(:namespace). joins(:namespace).
where(ntable[:name].matches(pattern)) where(ntable[:name].matches(pattern))
......
...@@ -126,7 +126,9 @@ class User < ActiveRecord::Base ...@@ -126,7 +126,9 @@ class User < ActiveRecord::Base
validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email } validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :projects_limit,
presence: true,
numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE }
validates :username, validates :username,
namespace: true, namespace: true,
presence: true, presence: true,
...@@ -137,10 +139,9 @@ class User < ActiveRecord::Base ...@@ -137,10 +139,9 @@ class User < ActiveRecord::Base
validate :unique_email, if: ->(user) { user.email_changed? } validate :unique_email, if: ->(user) { user.email_changed? }
validate :owns_notification_email, if: ->(user) { user.notification_email_changed? } validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
validate :owns_public_email, if: ->(user) { user.public_email_changed? } validate :owns_public_email, if: ->(user) { user.public_email_changed? }
validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :generate_password, on: :create
before_validation :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
before_validation :sanitize_attrs before_validation :sanitize_attrs
before_validation :set_notification_email, if: ->(user) { user.email_changed? } before_validation :set_notification_email, if: ->(user) { user.email_changed? }
before_validation :set_public_email, if: ->(user) { user.public_email_changed? } before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
...@@ -150,8 +151,6 @@ class User < ActiveRecord::Base ...@@ -150,8 +151,6 @@ class User < ActiveRecord::Base
before_save :ensure_external_user_rights before_save :ensure_external_user_rights
after_save :ensure_namespace_correct after_save :ensure_namespace_correct
after_initialize :set_projects_limit after_initialize :set_projects_limit
before_create :check_confirmation_email
after_create :post_create_hook
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
# User's Layout preference # User's Layout preference
...@@ -409,10 +408,8 @@ class User < ActiveRecord::Base ...@@ -409,10 +408,8 @@ class User < ActiveRecord::Base
"#{self.class.reference_prefix}#{username}" "#{self.class.reference_prefix}#{username}"
end end
def generate_password def skip_confirmation=(bool)
if force_random_password skip_confirmation! if bool
self.password = self.password_confirmation = Devise.friendly_token.first(Devise.password_length.min)
end
end end
def generate_reset_token def generate_reset_token
...@@ -424,10 +421,6 @@ class User < ActiveRecord::Base ...@@ -424,10 +421,6 @@ class User < ActiveRecord::Base
@reset_token @reset_token
end end
def check_confirmation_email
skip_confirmation! unless current_application_settings.send_user_confirmation_email
end
def recently_sent_password_reset? def recently_sent_password_reset?
reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago
end end
...@@ -822,12 +815,6 @@ class User < ActiveRecord::Base ...@@ -822,12 +815,6 @@ class User < ActiveRecord::Base
end end
end end
def post_create_hook
log_info("User \"#{name}\" (#{email}) was created")
notification_service.new_user(self, @reset_token) if created_by_id
system_hook_service.execute_hooks_for(self, :create)
end
def post_destroy_hook def post_destroy_hook
log_info("User \"#{name}\" (#{email}) was removed") log_info("User \"#{name}\" (#{email}) was removed")
system_hook_service.execute_hooks_for(self, :destroy) system_hook_service.execute_hooks_for(self, :destroy)
......
...@@ -18,10 +18,17 @@ class BuildEntity < Grape::Entity ...@@ -18,10 +18,17 @@ class BuildEntity < Grape::Entity
expose :created_at expose :created_at
expose :updated_at expose :updated_at
expose :detailed_status, as: :status, with: StatusEntity
private private
alias_method :build, :object
def path_to(route, build) def path_to(route, build)
send("#{route}_path", build.project.namespace, build.project, build) send("#{route}_path", build.project.namespace, build.project, build)
end end
def detailed_status
build.detailed_status(request.user)
end
end end
class BuildSerializer < BaseSerializer
entity BuildEntity
def represent_status(resource)
data = represent(resource, { only: [:status] })
data.fetch(:status, {})
end
end
...@@ -12,12 +12,7 @@ class PipelineEntity < Grape::Entity ...@@ -12,12 +12,7 @@ class PipelineEntity < Grape::Entity
end end
expose :details do expose :details do
expose :status do |pipeline, options| expose :detailed_status, as: :status, with: StatusEntity
StatusEntity.represent(
pipeline.detailed_status(request.user),
options)
end
expose :duration expose :duration
expose :finished_at expose :finished_at
expose :stages, using: StageEntity expose :stages, using: StageEntity
...@@ -82,4 +77,8 @@ class PipelineEntity < Grape::Entity ...@@ -82,4 +77,8 @@ class PipelineEntity < Grape::Entity
pipeline.cancelable? && pipeline.cancelable? &&
can?(request.user, :update_pipeline, pipeline) can?(request.user, :update_pipeline, pipeline)
end end
def detailed_status
pipeline.detailed_status(request.user)
end
end end
...@@ -22,4 +22,11 @@ class PipelineSerializer < BaseSerializer ...@@ -22,4 +22,11 @@ class PipelineSerializer < BaseSerializer
super(resource, opts) super(resource, opts)
end end
end end
def represent_status(resource)
return {} unless resource.present?
data = represent(resource, { only: [{ details: [:status] }] })
data.dig(:details, :status) || {}
end
end end
class StatusEntity < Grape::Entity class StatusEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
expose :icon, :text, :label, :group expose :icon, :favicon, :text, :label, :group
expose :has_details?, as: :has_details expose :has_details?, as: :has_details
expose :details_path expose :details_path
......
...@@ -7,14 +7,14 @@ module Ci ...@@ -7,14 +7,14 @@ module Ci
raise Gitlab::Access::AccessDeniedError raise Gitlab::Access::AccessDeniedError
end end
pipeline.builds.failed_or_canceled.find_each do |build| pipeline.builds.latest.failed_or_canceled.find_each do |build|
next unless build.retryable? next unless build.retryable?
Ci::RetryBuildService.new(project, current_user) Ci::RetryBuildService.new(project, current_user)
.reprocess(build) .reprocess(build)
end end
pipeline.builds.skipped.find_each do |skipped| pipeline.builds.latest.skipped.find_each do |skipped|
retry_optimistic_lock(skipped) { |build| build.process } retry_optimistic_lock(skipped) { |build| build.process }
end end
......
module Users
# Service for creating a new user.
class CreateService < BaseService
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
end
def build
raise Gitlab::Access::AccessDeniedError unless can_create_user?
user = User.new(build_user_params)
if current_user&.is_admin?
if params[:reset_password]
@reset_token = user.generate_reset_token
params[:force_random_password] = true
end
if params[:force_random_password]
random_password = Devise.friendly_token.first(Devise.password_length.min)
user.password = user.password_confirmation = random_password
end
end
identity_attrs = params.slice(:extern_uid, :provider)
if identity_attrs.any?
user.identities.build(identity_attrs)
end
user
end
def execute
user = build
if user.save
log_info("User \"#{user.name}\" (#{user.email}) was created")
notification_service.new_user(user, @reset_token) if @reset_token
system_hook_service.execute_hooks_for(user, :create)
end
user
end
private
def can_create_user?
(current_user.nil? && current_application_settings.signup_enabled?) || current_user&.is_admin?
end
# Allowed params for creating a user (admins only)
def admin_create_params
[
:access_level,
:admin,
:avatar,
:bio,
:can_create_group,
:color_scheme_id,
:email,
:external,
:force_random_password,
:hide_no_password,
:hide_no_ssh_key,
:key_id,
:linkedin,
:name,
:password,
:password_expires_at,
:projects_limit,
:remember_me,
:skip_confirmation,
:skype,
:theme_id,
:twitter,
:username,
:website_url
]
end
# Allowed params for user signup
def signup_params
[
:email,
:email_confirmation,
:name,
:password,
:username
]
end
def build_user_params
if current_user&.is_admin?
user_params = params.slice(*admin_create_params)
user_params[:created_by_id] = current_user.id
if params[:reset_password]
user_params.merge!(force_random_password: true, password_expires_at: nil)
end
else
user_params = params.slice(*signup_params)
user_params[:skip_confirmation] = !current_application_settings.send_user_confirmation_email
end
user_params
end
end
end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%legend Access %legend Access
.form-group .form-group
= f.label :projects_limit, class: 'control-label' = f.label :projects_limit, class: 'control-label'
.col-sm-10= f.number_field :projects_limit, min: 0, class: 'form-control' .col-sm-10= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control'
.form-group .form-group
= f.label :can_create_group, class: 'control-label' = f.label :can_create_group, class: 'control-label'
......
...@@ -68,12 +68,11 @@ ...@@ -68,12 +68,11 @@
= link_to todos_filter_path(sort: sort_value_oldest_created) do = link_to todos_filter_path(sort: sort_value_oldest_created) do
= sort_title_oldest_created = sort_title_oldest_created
.js-todos-all .js-todos-all
- if @todos.any? - if @todos.any?
.js-todos-list-container .js-todos-list-container
.js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } } .js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } }
.panel.panel-default.panel-small.panel-without-border .panel.panel-default.panel-without-border.panel-without-margin
%ul.content-list.todos-list %ul.content-list.todos-list
= render @todos = render @todos
= paginate @todos, theme: "gitlab" = paginate @todos, theme: "gitlab"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- if devise_mapping.rememberable? - if devise_mapping.rememberable?
.remember-me.checkbox .remember-me.checkbox
%label{ for: "user_remember_me" } %label{ for: "user_remember_me" }
= f.check_box :remember_me = f.check_box :remember_me, class: 'remember-me-checkbox'
%span Remember me %span Remember me
.pull-right.forgot-password .pull-right.forgot-password
= link_to "Forgot your password?", new_password_path(resource_name) = link_to "Forgot your password?", new_password_path(resource_name)
......
...@@ -17,24 +17,3 @@ ...@@ -17,24 +17,3 @@
= link_to filter_projects_path(visibility_level: level) do = link_to filter_projects_path(visibility_level: level) do
= visibility_level_icon(level) = visibility_level_icon(level)
= visibility_level_label(level) = visibility_level_label(level)
- if @tags.present?
.dropdown
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" }
= icon('tags')
%span.light Tags:
- if params[:tag].present?
= params[:tag]
- else
Any
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_projects_path(tag: nil) do
Any
- @tags.each do |tag|
%li{ class: active_when(tag.name == params[:tag]) || 'light' }
= link_to filter_projects_path(tag: tag.name) do
= icon('tag')
= tag.name
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
.layout-nav .layout-nav
.container-fluid .container-fluid
= render "layouts/nav/#{nav}" = render "layouts/nav/#{nav}"
.content-wrapper{ class: "#{layout_nav_class}" } - if content_for?(:sub_nav)
= yield :sub_nav = yield :sub_nav
.content-wrapper{ class: layout_nav_class }
.alert-wrapper .alert-wrapper
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/flash" = render "layouts/flash"
......
...@@ -15,6 +15,13 @@ ...@@ -15,6 +15,13 @@
%span.sr-only Toggle navigation %span.sr-only Toggle navigation
= icon('ellipsis-v') = icon('ellipsis-v')
.header-logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
= brand_header_logo
.title-container
%h1.title{ class: ('initializing' if @has_group_title) }= title
.navbar-collapse.collapse .navbar-collapse.collapse
%ul.nav.navbar-nav %ul.nav.navbar-nav
%li.hidden-sm.hidden-xs %li.hidden-sm.hidden-xs
...@@ -69,12 +76,6 @@ ...@@ -69,12 +76,6 @@
%div %div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
.header-logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
= brand_header_logo
%h1.title{ class: ('initializing' if @has_group_title) }= title
= yield :header_content = yield :header_content
= render 'shared/outdated_browser' = render 'shared/outdated_browser'
......
- builds = @build.pipeline.builds.to_a - builds = @build.pipeline.builds.to_a
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "151", "spy" => "affix" } } %aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "153", "spy" => "affix" } }
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default .block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
Job Job
%strong ##{@build.id} %strong ##{@build.id}
...@@ -137,3 +137,6 @@ ...@@ -137,3 +137,6 @@
= build.id = build.id
- if build.retried? - if build.retried?
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' } %i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
:javascript
new Sidebar();
- @no_container = true - @no_container = true
- page_title "#{@build.name} (##{@build.id})", "Jobs" - page_title "#{@build.name} (##{@build.id})", "Jobs"
= render "projects/pipelines/head", build_subnav: true = render "projects/pipelines/head"
%div{ class: container_class } %div{ class: container_class }
.build-page .build-page
......
- disable_initialization = local_assigns.fetch(:disable_initialization, false) - disable_initialization = local_assigns.fetch(:disable_initialization, false)
#commit-pipeline-table-view{ data: { disable_initialization: disable_initialization, #commit-pipeline-table-view{ data: { disable_initialization: disable_initialization,
endpoint: endpoint, endpoint: endpoint,
"help-page-path" => help_page_path('ci/quick_start/README'),
} } } }
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
.col-sm-6 .col-sm-6
.nav-controls .nav-controls
= link_to @environment.external_url, class: 'btn btn-default' do = link_to @environment.external_url, class: 'btn btn-default', target: '_blank', rel: 'noopener noreferrer nofollow' do
= icon('external-link') = icon('external-link')
= render 'projects/deployments/actions', deployment: @environment.last_deployment = render 'projects/deployments/actions', deployment: @environment.last_deployment
......
- case @status - case @status
- when :success - when :success
- remove_source_branch = params[:should_remove_source_branch] == '1' || @merge_request.remove_source_branch?
:plain :plain
merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'}); merge_request_widget.mergeInProgress(#{remove_source_branch});
- when :merge_when_pipeline_succeeds - when :merge_when_pipeline_succeeds
:plain :plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_pipeline_succeeds'))}"); $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_pipeline_succeeds'))}");
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
= render 'shared/milestones_filter', counts: milestone_counts(@project.milestones) = render 'shared/milestones_filter', counts: milestone_counts(@project.milestones)
.nav-controls .nav-controls
= render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @project) - if can?(current_user, :admin_milestone, @project)
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New Milestone' do = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New Milestone' do
New Milestone New Milestone
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
- if current_user.can_select_namespace? - if current_user.can_select_namespace?
.input-group-addon .input-group-addon
= root_url = root_url
= f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1} = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), {}, { class: 'select2 js-select-namespace', tabindex: 1}
- else - else
.input-group-addon.static-namespace .input-group-addon.static-namespace
......
= content_for :sub_nav do = content_for :sub_nav do
.scrolling-tabs-container.sub-nav-scroll .scrolling-tabs-container.sub-nav-scroll
= render 'shared/nav_scroll' = render 'shared/nav_scroll'
.nav-links.sub-nav.scrolling-tabs{ class: ('build' if local_assigns.fetch(:build_subnav, false)) } .nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) } %ul{ class: (container_class) }
- if project_nav_tab? :pipelines - if project_nav_tab? :pipelines
= nav_link(path: 'pipelines#index', controller: :pipelines) do = nav_link(path: 'pipelines#index', controller: :pipelines) do
......
.page-content-header .page-content-header
.header-main-content .header-main-content
= render 'ci/status/badge', status: @pipeline.detailed_status(current_user) = render 'ci/status/badge', status: @pipeline.detailed_status(current_user)
%strong Pipeline ##{@commit.pipelines.last.id} %strong Pipeline ##{@pipeline.id}
triggered #{time_ago_with_tooltip(@commit.authored_date)} by triggered #{time_ago_with_tooltip(@pipeline.created_at)}
= author_avatar(@commit, size: 24) - if @pipeline.user
= commit_author_link(@commit) by
= user_avatar(user: @pipeline.user, size: 24)
= user_link(@pipeline.user)
.header-action-buttons .header-action-buttons
- if can?(current_user, :update_pipeline, @pipeline.project) - if can?(current_user, :update_pipeline, @pipeline.project)
- if @pipeline.retryable? - if @pipeline.retryable?
......
...@@ -6,53 +6,19 @@ ...@@ -6,53 +6,19 @@
= render "projects/pipelines/head" = render "projects/pipelines/head"
%div{ class: container_class } #pipelines-list-vue{ data: { endpoint: namespace_project_pipelines_path(@project.namespace, @project, format: :json),
.top-area "css-class" => container_class,
%ul.nav-links "help-page-path" => help_page_path('ci/quick_start/README'),
%li.js-pipelines-tab-all{ class: active_when(@scope.nil?) }> "new-pipeline-path" => new_namespace_project_pipeline_path(@project.namespace, @project),
= link_to project_pipelines_path(@project) do "can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s,
All "all-path" => project_pipelines_path(@project),
%span.badge.js-totalbuilds-count "pending-path" => project_pipelines_path(@project, scope: :pending),
= number_with_delimiter(@pipelines_count) "running-path" => project_pipelines_path(@project, scope: :running),
"finished-path" => project_pipelines_path(@project, scope: :finished),
%li.js-pipelines-tab-pending{ class: active_when(@scope == 'pending') }> "branches-path" => project_pipelines_path(@project, scope: :branches),
= link_to project_pipelines_path(@project, scope: :pending) do "tags-path" => project_pipelines_path(@project, scope: :tags),
Pending "has-ci" => @repository.gitlab_ci_yml,
%span.badge "ci-lint-path" => ci_lint_path } }
= number_with_delimiter(@pending_count)
%li.js-pipelines-tab-running{ class: active_when(@scope == 'running') }>
= link_to project_pipelines_path(@project, scope: :running) do
Running
%span.badge.js-running-count
= number_with_delimiter(@running_count)
%li.js-pipelines-tab-finished{ class: active_when(@scope == 'finished') }>
= link_to project_pipelines_path(@project, scope: :finished) do
Finished
%span.badge
= number_with_delimiter(@finished_count)
%li.js-pipelines-tab-branches{ class: active_when(@scope == 'branches') }>
= link_to project_pipelines_path(@project, scope: :branches) do
Branches
%li.js-pipelines-tab-tags{ class: active_when(@scope == 'tags') }>
= link_to project_pipelines_path(@project, scope: :tags) do
Tags
.nav-controls
- if can? current_user, :create_pipeline, @project
= link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
Run pipeline
- unless @repository.gitlab_ci_yml
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
= link_to ci_lint_path, class: 'btn btn-default' do
%span CI Lint
.content-list.pipelines{ data: { url: namespace_project_pipelines_path(@project.namespace, @project, format: :json) } }
.vue-pipelines-index
= page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('vue_pipelines') = page_specific_javascript_bundle_tag('vue_pipelines')
%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } } %aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "102", "spy" => "affix" } }
.block.wiki-sidebar-header.append-bottom-default .block.wiki-sidebar-header.append-bottom-default
%a.gutter-toggle.pull-right.visible-xs-block.visible-sm-block.js-sidebar-wiki-toggle{ href: "#" } %a.gutter-toggle.pull-right.visible-xs-block.visible-sm-block.js-sidebar-wiki-toggle{ href: "#" }
= icon('angle-double-right') = icon('angle-double-right')
...@@ -19,3 +19,6 @@ ...@@ -19,3 +19,6 @@
More Pages More Pages
= render 'projects/wikis/new' = render 'projects/wikis/new'
:javascript
new Sidebar();
- parent = Group.find_by(id: params[:parent_id] || @group.parent_id) - parent = Group.find_by(id: params[:parent_id] || @group.parent_id)
- group_path = root_url
- group_path << parent.full_path + '/' if parent
- if @group.persisted? - if @group.persisted?
.form-group .form-group
= f.label :name, class: 'control-label' do = f.label :name, class: 'control-label' do
...@@ -11,7 +13,7 @@ ...@@ -11,7 +13,7 @@
Group path Group path
.col-sm-10 .col-sm-10
.input-group.gl-field-error-anchor .input-group.gl-field-error-anchor
.input-group-addon .group-root-path.input-group-addon.has-tooltip{ title: group_path, :'data-placement' => 'bottom' }
%span>= root_url %span>= root_url
- if parent - if parent
%strong= parent.full_path + '/' %strong= parent.full_path + '/'
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
Open Open
%span.badge= counts[:opened] %span.badge= counts[:opened]
%li{ class: milestone_class_for_state(params[:state], 'closed') }> %li{ class: milestone_class_for_state(params[:state], 'closed') }>
= link_to milestones_filter_path(state: 'closed') do = link_to milestones_filter_path(state: 'closed', sort: 'due_date_desc') do
Closed Closed
%span.badge= counts[:closed] %span.badge= counts[:closed]
%li{ class: milestone_class_for_state(params[:state], 'all') }> %li{ class: milestone_class_for_state(params[:state], 'all') }>
= link_to milestones_filter_path(state: 'all') do = link_to milestones_filter_path(state: 'all', sort: 'due_date_desc') do
All All
%span.badge= counts[:all] %span.badge= counts[:all]
.dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } }
%span.light
- if @sort.present?
= milestone_sort_options_hash[@sort]
- else
= sort_title_due_date_soon
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
%li
= link_to page_filter_path(sort: sort_value_due_date_soon, label: true) do
= sort_title_due_date_soon
= link_to page_filter_path(sort: sort_value_due_date_later, label: true) do
= sort_title_due_date_later
= link_to page_filter_path(sort: sort_value_start_date_soon, label: true) do
= sort_title_start_date_soon
= link_to page_filter_path(sort: sort_value_start_date_later, label: true) do
= sort_title_start_date_later
= link_to page_filter_path(sort: sort_value_name, label: true) do
= sort_title_name_asc
= link_to page_filter_path(sort: sort_value_name_desc, label: true) do
= sort_title_name_desc
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 150"><g fill="none" fill-rule="evenodd" transform="translate(0-3)"><g transform="translate(0 105)"><g fill="#e5e5e5"><rect width="78" height="4" x="34" y="21" opacity=".5" rx="2"/><path d="m152 23c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2"/></g><g transform="translate(0 4)"><path fill="#98d7b2" fill-rule="nonzero" d="m19 38c-10.493 0-19-8.507-19-19 0-10.493 8.507-19 19-19 10.493 0 19 8.507 19 19 0 10.493-8.507 19-19 19m0-4c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15-8.284 0-15 6.716-15 15 0 8.284 6.716 15 15 15"/><path fill="#31af64" d="m17.07 21.02l-2.829-2.829c-.786-.786-2.047-.781-2.828 0-.786.786-.781 2.047 0 2.828l4.243 4.243c.392.392.902.587 1.412.588.512.002 1.021-.193 1.41-.582l7.79-7.79c.777-.777.775-2.042-.006-2.823-.786-.786-2.045-.784-2.823-.006l-6.37 6.37"/></g><g fill="#e52c5a" transform="translate(102)"><path fill-rule="nonzero" d="m24 47.5c-12.979 0-23.5-10.521-23.5-23.5 0-12.979 10.521-23.5 23.5-23.5 12.979 0 23.5 10.521 23.5 23.5 0 12.979-10.521 23.5-23.5 23.5m0-5c10.217 0 18.5-8.283 18.5-18.5 0-10.217-8.283-18.5-18.5-18.5-10.217 0-18.5 8.283-18.5 18.5 0 10.217 8.283 18.5 18.5 18.5"/><path d="m28.24 24l2.833-2.833c1.167-1.167 1.167-3.067-.004-4.239-1.169-1.169-3.069-1.173-4.239-.004l-2.833 2.833-2.833-2.833c-1.167-1.167-3.067-1.167-4.239.004-1.169 1.169-1.173 3.069-.004 4.239l2.833 2.833-2.833 2.833c-1.167 1.167-1.167 3.067.004 4.239 1.169 1.169 3.069 1.173 4.239.004l2.833-2.833 2.833 2.833c1.167 1.167 3.067 1.167 4.239-.004 1.169-1.169 1.173-3.069.004-4.239l-2.833-2.833"/></g><path fill="#e5e5e5" fill-rule="nonzero" d="m236 37c-7.732 0-14-6.268-14-14 0-7.732 6.268-14 14-14 7.732 0 14 6.268 14 14 0 7.732-6.268 14-14 14m0-4c5.523 0 10-4.477 10-10 0-5.523-4.477-10-10-10-5.523 0-10 4.477-10 10 0 5.523 4.477 10 10 10"/></g><g transform="translate(69 3)"><path fill="#e5e5e5" fill-rule="nonzero" d="m4 11.99v60.02c0 4.413 3.583 7.99 8 7.99h89.991c4.419 0 8-3.579 8-7.99v-60.02c0-4.413-3.583-7.99-8-7.99h-89.991c-4.419 0-8 3.579-8 7.99m-4 0c0-6.622 5.378-11.99 12-11.99h89.991c6.629 0 12 5.367 12 11.99v60.02c0 6.622-5.378 11.99-12 11.99h-89.991c-6.629 0-12-5.367-12-11.99v-60.02m52.874 80.3l-13.253-15.292h34.76l-13.253 15.292c-2.237 2.582-6.01 2.585-8.253 0m3.02-2.62c.644.743 1.564.743 2.207 0l7.516-8.673h-17.24l7.516 8.673"/><rect width="18" height="6" x="15" y="23" fill="#fc8a51" rx="3"/><rect width="18" height="6" x="39" y="39" fill="#e52c5a" rx="3"/><rect width="18" height="6" x="33" y="55" fill="#e5e5e5" rx="3"/><rect width="12" height="6" x="39" y="23" fill="#fde5d8" rx="3"/><rect width="12" height="6" x="57" y="55" fill="#e52c5a" rx="3"/><rect width="12" height="6" x="15" y="55" fill="#b5a7dd" rx="3"/><rect width="18" height="6" x="81" y="23" fill="#fc8a51" rx="3"/><rect width="18" height="6" x="15" y="39" fill="#fde5d8" rx="3"/><rect width="6" height="6" x="57" y="23" fill="#e52c5a" rx="3"/><g fill="#fde5d8"><rect width="6" height="6" x="69" y="23" rx="3"/><rect width="6" height="6" x="75" y="39" rx="3"/></g><rect width="6" height="6" x="63" y="39" fill="#e52c5a" rx="3"/></g><g transform="matrix(.70711-.70711.70711.70711 84.34 52.5)"><path fill="#6b4fbb" fill-rule="nonzero" d="m28.02 67.48c-15.927-2.825-28.02-16.738-28.02-33.476 0-18.778 15.222-34 34-34 18.778 0 34 15.222 34 34 0 16.738-12.1 30.652-28.02 33.476.015.173.023.347.023.524v21.999c0 3.314-2.693 6-6 6-3.314 0-6-2.682-6-6v-21.999c0-.177.008-.351.023-.524m5.977-7.476c14.359 0 26-11.641 26-26 0-14.359-11.641-26-26-26-14.359 0-26 11.641-26 26 0 14.359 11.641 26 26 26"/><path fill="#fff" fill-opacity=".3" stroke="#6b4fbb" stroke-width="8" d="m31 71c16.569 0 30-13.431 30-30 0-16.569-13.431-30-30-30" transform="matrix(.86603.5-.5.86603 26.663-17.507)"/></g></g></svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 446 249" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="0" d="m260.03 114h23.972v-.013c19.972-.53 36-16.887 36-36.987 0-20.435-16.565-37-37-37-.993 0-1.977.039-2.95.116-4.95-14.605-18.773-25.12-35.05-25.12-5.464 0-10.652 1.185-15.32 3.311-6.649-9.841-17.909-16.311-30.68-16.311-20.435 0-37 16.565-37 37 0 .701.019 1.397.058 2.088-16.11 3.999-28.06 18.561-28.06 35.912 0 20.435 16.565 37 37 37 .324 0 .646-.004.968-.012"/><ellipse id="2" cx="41" cy="41" rx="41" ry="41"/><mask id="1" width="186" height="112" x="0" y="0" fill="#fff"><use xlink:href="#0"/></mask><mask id="3" width="82" height="82" x="0" y="0" fill="#fff"><use xlink:href="#2"/></mask></defs><g fill="none" fill-rule="evenodd"><g transform="matrix(.86603.5-.5.86603 228.11 137.43)"><path stroke="#b5a7dd" stroke-width="4" d="m.445.161c15.89 10.636 34.998 16.839 55.55 16.839"/><g transform="translate(56 4)"><path fill="#fb722e" d="m16 8c0-1.105.902-2 2.01-2h7.983c1.109 0 2.01.888 2.01 2 0 1.105-.902 2-2.01 2h-7.983c-1.109 0-2.01-.888-2.01-2m0 10c0-1.105.902-2 2.01-2h7.983c1.109 0 2.01.888 2.01 2 0 1.105-.902 2-2.01 2h-7.983c-1.109 0-2.01-.888-2.01-2"/><path fill="#fde5d8" fill-rule="nonzero" d="m4 22h6c3.315 0 6-2.685 6-5.997v-6.01c0-3.315-2.684-5.997-6-5.997h-6v18m-4-18.992c0-1.661 1.343-3.01 2.994-3.01h7.01c5.523 0 10 4.47 10 9.997v6.01c0 5.521-4.476 9.997-10 9.997h-7.01c-1.654 0-2.994-1.343-2.994-3.01v-19.984"/></g></g><g fill-rule="nonzero" transform="translate(257)"><path fill="#e5e5e5" d="m3.597 18.747c5.611-9.09 15.519-14.747 26.403-14.747 17.12 0 31 13.879 31 31 0 7.02-2.34 13.685-6.58 19.1l3.149 2.466c4.786-6.111 7.431-13.639 7.431-21.565 0-19.33-15.67-35-35-35-12.286 0-23.476 6.384-29.808 16.647l3.404 2.1"/><g transform="matrix(.96593.25882-.25882.96593 15.98 9.578)"><path fill="#b5a7dd" d="m12.426 11.592l-2.142 1.768-3.664-2.116c-.186-.107-.43-.042-.543.154l-1.229 2.129c-.116.2-.052.438.138.547l3.658 2.112-.455 2.735c-.109.657-.165 1.327-.165 2.01 0 .678.055 1.348.165 2.01l.455 2.735-3.658 2.112c-.186.107-.251.351-.138.547l1.229 2.129c.116.2.353.264.543.154l3.664-2.116 2.142 1.768c1.036.855 2.205 1.533 3.462 2l2.6.972v4.225c0 .215.179.393.405.393h2.458c.231 0 .405-.174.405-.393v-4.225l2.6-.972c1.257-.47 2.426-1.147 3.462-2l2.142-1.768 3.664 2.116c.186.107.43.042.543-.154l1.229-2.129c.116-.2.052-.438-.138-.547l-3.658-2.112.455-2.735c.109-.657.165-1.327.165-2.01 0-.678-.055-1.348-.165-2.01l-.455-2.735 3.658-2.112c.186-.107.251-.351.138-.547l-1.229-2.129c-.116-.2-.353-.264-.543-.154l-3.664 2.116-2.142-1.768c-1.036-.855-2.205-1.533-3.462-2l-2.6-.972v-4.225c0-.215-.179-.393-.405-.393h-2.458c-.231 0-.405.174-.405.393v4.225l-2.6.972c-1.257.47-2.426 1.147-3.462 2m2.062-5.749v-1.45c0-2.426 1.963-4.393 4.405-4.393h2.458c2.433 0 4.405 1.967 4.405 4.393v1.45c1.689.631 3.243 1.538 4.608 2.665l1.259-.727c2.101-1.213 4.786-.497 6.01 1.618l1.229 2.129c1.216 2.107.499 4.798-1.602 6.01l-1.257.726c.144.866.219 1.755.219 2.662 0 .907-.075 1.796-.219 2.662l1.257.726c2.101 1.213 2.823 3.896 1.602 6.01l-1.229 2.129c-1.216 2.107-3.906 2.832-6.01 1.618l-1.259-.727c-1.365 1.127-2.92 2.034-4.608 2.665v1.45c0 2.426-1.963 4.393-4.405 4.393h-2.458c-2.433 0-4.405-1.967-4.405-4.393v-1.45c-1.689-.631-3.243-1.538-4.608-2.665l-1.259.727c-2.101 1.213-4.786.497-6.01-1.618l-1.229-2.129c-1.216-2.107-.499-4.798 1.602-6.01l1.257-.726c-.144-.866-.219-1.755-.219-2.662 0-.907.075-1.796.219-2.662l-1.257-.726c-2.101-1.213-2.823-3.896-1.602-6.01l1.229-2.129c1.216-2.107 3.906-2.832 6.01-1.618l1.259.727c1.365-1.127 2.92-2.034 4.608-2.665"/><path fill="#6b4fbb" d="m20.12 23.366c1.347 0 2.439-1.092 2.439-2.439 0-1.347-1.092-2.439-2.439-2.439-1.347 0-2.439 1.092-2.439 2.439 0 1.347 1.092 2.439 2.439 2.439m0 4c-3.556 0-6.439-2.883-6.439-6.439 0-3.556 2.883-6.439 6.439-6.439 3.556 0 6.439 2.883 6.439 6.439 0 3.556-2.883 6.439-6.439 6.439"/></g></g><use fill="#fff" stroke="#e5e5e5" stroke-width="8" mask="url(#1)" stroke-linejoin="round" xlink:href="#0"/><g transform="translate(175 58)"><use fill="#fff" stroke="#e5e5e5" stroke-width="8" mask="url(#3)" xlink:href="#2"/><g fill-rule="nonzero"><path fill="#e5e5e5" d="m41 78c20.435 0 37-16.565 37-37 0-20.435-16.565-37-37-37-20.435 0-37 16.565-37 37 0 20.435 16.565 37 37 37m0 4c-22.644 0-41-18.356-41-41 0-22.644 18.356-41 41-41 22.644 0 41 18.356 41 41 0 22.644-18.356 41-41 41"/><g transform="matrix(.96593.25882-.25882.96593 23.581 9.415)"><path fill="#b5a7dd" d="m14.821 13.655l-2.142 1.768-3.933-2.271c-.72-.416-1.634-.171-2.046.543l-1.507 2.61c-.409.708-.161 1.631.553 2.043l3.926 2.267-.455 2.735c-.145.869-.218 1.754-.218 2.65 0 .896.073 1.782.218 2.65l.455 2.735-3.926 2.267c-.72.416-.965 1.329-.553 2.043l1.507 2.61c.409.708 1.332.955 2.046.543l3.933-2.271 2.142 1.768c1.369 1.131 2.916 2.027 4.579 2.648l2.6.972v4.534c0 .831.669 1.5 1.493 1.5h3.01c.817 0 1.493-.676 1.493-1.5v-4.534l2.6-.972c1.663-.621 3.21-1.518 4.579-2.648l2.142-1.768 3.933 2.271c.72.416 1.634.171 2.046-.543l1.507-2.61c.409-.708.161-1.631-.553-2.043l-3.926-2.267.455-2.735c.145-.869.218-1.754.218-2.65 0-.896-.073-1.782-.218-2.65l-.455-2.735 3.926-2.267c.72-.416.965-1.329.553-2.043l-1.507-2.61c-.409-.708-1.332-.955-2.046-.543l-3.933 2.271-2.142-1.768c-1.369-1.131-2.916-2.027-4.579-2.648l-2.6-.972v-4.534c0-.831-.669-1.5-1.493-1.5h-3.01c-.817 0-1.493.676-1.493 1.5v4.534l-2.6.972c-1.663.621-3.21 1.518-4.579 2.648m3.179-6.395v-1.759c0-3.038 2.471-5.5 5.493-5.5h3.01c3.034 0 5.493 2.46 5.493 5.5v1.759c2.098.784 4.03 1.91 5.725 3.311l1.528-.882c2.631-1.519 5.999-.61 7.51 2.01l1.507 2.61c1.517 2.627.616 5.987-2.02 7.507l-1.525.881c.179 1.076.272 2.18.272 3.307 0 1.127-.093 2.231-.272 3.307l1.525.881c2.631 1.519 3.528 4.89 2.02 7.507l-1.507 2.61c-1.517 2.627-4.877 3.527-7.51 2.01l-1.528-.882c-1.696 1.401-3.627 2.527-5.725 3.311v1.759c0 3.038-2.471 5.5-5.493 5.5h-3.01c-3.034 0-5.493-2.46-5.493-5.5v-1.759c-2.098-.784-4.03-1.91-5.725-3.311l-1.528.882c-2.631 1.519-5.999.61-7.51-2.01l-1.507-2.61c-1.517-2.627-.616-5.987 2.02-7.507l1.525-.881c-.179-1.076-.272-2.18-.272-3.307 0-1.127.093-2.231.272-3.307l-1.525-.881c-2.631-1.519-3.528-4.89-2.02-7.507l1.507-2.61c1.517-2.627 4.877-3.527 7.51-2.01l1.528.882c1.696-1.401 3.627-2.527 5.725-3.311"/><path fill="#6b4fbb" d="m25 30c2.209 0 4-1.791 4-4 0-2.209-1.791-4-4-4-2.209 0-4 1.791-4 4 0 2.209 1.791 4 4 4m0 4c-4.418 0-8-3.582-8-8 0-4.418 3.582-8 8-8 4.418 0 8 3.582 8 8 0 4.418-3.582 8-8 8"/></g></g></g><g transform="translate(140 161)"><path fill="#e5e5e5" fill-rule="nonzero" d="m4 8.541v30.01c0 2.202 1.793 3.995 4 3.995h20c2.209 0 4-1.789 4-3.995v-30.01c0-2.202-1.793-3.995-4-3.995h-20c-2.209 0-4 1.789-4 3.995m-4 0c0-4.416 3.583-7.995 8-7.995h20c4.416 0 8 3.584 8 7.995v30.01c0 4.416-3.583 7.995-8 7.995h-20c-4.416 0-8-3.584-8-7.995v-30.01"/><g fill="#fb722e"><rect width="4" height="11" x="10" y="18.545" rx="2"/><rect width="4" height="11" x="21" y="18.545" rx="2"/></g></g><path fill="#e5e5e5" fill-rule="nonzero" d="m445.16 245.34c-16.874-11.778-110.62-20.336-222.14-20.336-111.61 0-205.4 8.571-222.18 20.364-.904.635-1.121 1.883-.486 2.786.635.904 1.883 1.121 2.786.486 15.756-11.07 109.46-19.636 219.88-19.636 110.34 0 203.99 8.55 219.85 19.617.906.632 2.153.41 2.785-.495.632-.906.41-2.153-.495-2.785"/></g></svg>
\ No newline at end of file
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
.stats .stats
%span %span
= icon('bookmark') = icon('bookmark')
= number_with_delimiter(group.projects.count) = number_with_delimiter(group.projects.non_archived.count)
%span %span
= icon('users') = icon('users')
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('issuable') = page_specific_javascript_bundle_tag('issuable')
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' } %aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => "102", "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar .issuable-sidebar
- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.block.issuable-sidebar-header .block.issuable-sidebar-header
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.js-projects-list-holder .js-projects-list-holder
- if projects.any? - if projects.any?
%ul.projects-list.content-list %ul.projects-list
- projects.each_with_index do |project, i| - projects.each_with_index do |project, i|
- css_class = (i >= projects_limit) ? 'hide' : nil - css_class = (i >= projects_limit) ? 'hide' : nil
= render "shared/projects/project", project: project, skip_namespace: skip_namespace, = render "shared/projects/project", project: project, skip_namespace: skip_namespace,
......
...@@ -10,44 +10,44 @@ ...@@ -10,44 +10,44 @@
%li.project-row{ class: css_class } %li.project-row{ class: css_class }
= cache(cache_key) do = cache(cache_key) do
- if avatar
.avatar-container.s40
- if use_creator_avatar
= image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
- else
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
.project-details
%h3.prepend-top-0.append-bottom-0
= link_to project_path(project), class: dom_class(project) do
%span.project-full-name
%span.namespace-name
- if project.namespace && !skip_namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
= project.name
- if show_last_commit_as_description
.description.prepend-top-5
= link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit),
class: "commit-row-message"
- elsif project.description.present?
.description.prepend-top-5
= markdown_field(project, :description)
.controls .controls
- if project.archived - if project.archived
%span.label.label-warning archived %span.prepend-left-10.label.label-warning archived
- if project.pipeline_status.has_status? - if project.pipeline_status.has_status?
%span %span.prepend-left-10
= render_project_pipeline_status(project.pipeline_status) = render_project_pipeline_status(project.pipeline_status)
- if forks - if forks
%span %span.prepend-left-10
= icon('code-fork') = icon('code-fork')
= number_with_delimiter(project.forks_count) = number_with_delimiter(project.forks_count)
- if stars - if stars
%span %span.prepend-left-10
= icon('star') = icon('star')
= number_with_delimiter(project.star_count) = number_with_delimiter(project.star_count)
%span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project) } %span.prepend-left-10.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project) }
= visibility_level_icon(project.visibility_level, fw: true) = visibility_level_icon(project.visibility_level, fw: true)
.title
= link_to project_path(project), class: dom_class(project) do
- if avatar
.dash-project-avatar
.avatar-container.s40
- if use_creator_avatar
= image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
- else
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
%span.project-full-name
%span.namespace-name
- if project.namespace && !skip_namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
= project.name
- if show_last_commit_as_description
.description
= link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit),
class: "commit-row-message"
- elsif project.description.present?
.description
= markdown_field(project, :description)
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
.cover-block.user-cover-block .cover-block.user-cover-block
.cover-controls .cover-controls
- if @user == current_user - if @user == current_user
= link_to profile_path, class: 'btn btn-gray' do = link_to profile_path, class: 'btn btn-gray has-tooltip', title: 'Edit profile', 'aria-label': 'Edit profile' do
= icon('pencil') = icon('pencil')
- elsif current_user - elsif current_user
- if @user.abuse_report - if @user.abuse_report
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray', = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle') = icon('exclamation-circle')
= link_to user_path(@user, rss_url_options), class: 'btn btn-gray' do = link_to user_path(@user, rss_url_options), class: 'btn btn-gray has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
= icon('rss') = icon('rss')
- if current_user && current_user.admin? - if current_user && current_user.admin?
= link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area', = link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area',
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
%span.middle-dot-divider %span.middle-dot-divider
@#{@user.username} @#{@user.username}
%span.middle-dot-divider %span.middle-dot-divider
Member since #{@user.created_at.to_s(:medium)} Member since #{@user.created_at.to_date.to_s(:long)}
.cover-desc .cover-desc
- unless @user.public_email.blank? - unless @user.public_email.blank?
...@@ -97,7 +97,8 @@ ...@@ -97,7 +97,8 @@
Snippets Snippets
%div{ class: container_class } %div{ class: container_class }
.user-callout{ 'callout-svg' => custom_icon('icon_customization') } - if @user == current_user
.user-callout{ 'callout-svg' => custom_icon('icon_customization') }
.tab-content .tab-content
#activity.tab-pane #activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs .row-content-block.calender-block.white.second-block.hidden-xs
......
---
title: Expose CI/CD status API endpoints with Gitlab::Ci::Status facility on pipeline,
job and merge request for favicon
merge_request: 9561
author: dosuken123
---
title: Resolve "404 when requesting build trace"
merge_request: 9759
author: dosuken123
---
title: Adding non_archived scope for counting projects
merge_request: 8305
author: Naveen Kumar
---
title: Add ECMAScript polyfills for Symbol and Array.find
merge_request: 10120
author:
---
title: Adds empty and error state to pipelines
merge_request:
author:
---
title: Implement user create service
merge_request: 9220
author: George Andrinopoulos
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.
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.
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.
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.
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.
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.
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