Commit 4d2be5bb authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into sidebar-fly-out-sub-nav

parents 1a2d180e d4c4dec8
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6"
cache: .default-cache: &default-cache
key: "ruby-233-with-yarn" key: "ruby-233-with-yarn"
paths: paths:
- vendor/ruby - vendor/ruby
- .yarn-cache/ - .yarn-cache/
.push-cache: &push-cache
cache:
<<: *default-cache
policy: push
.pull-cache: &pull-cache
cache:
<<: *default-cache
policy: pull
variables: variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1" MYSQL_ALLOW_EMPTY_PASSWORD: "1"
RAILS_ENV: "test" RAILS_ENV: "test"
...@@ -24,11 +34,11 @@ before_script: ...@@ -24,11 +34,11 @@ before_script:
- source scripts/prepare_build.sh - source scripts/prepare_build.sh
stages: stages:
- build - build
- prepare - prepare
- test - test
- post-test - post-test
- pages - pages
# Predefined scopes # Predefined scopes
.dedicated-runner: &dedicated-runner .dedicated-runner: &dedicated-runner
...@@ -41,10 +51,6 @@ stages: ...@@ -41,10 +51,6 @@ stages:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
KNAPSACK_S3_BUCKET: "gitlab-ce-cache" KNAPSACK_S3_BUCKET: "gitlab-ce-cache"
cache:
key: "knapsack"
paths:
- knapsack/
artifacts: artifacts:
expire_in: 31d expire_in: 31d
paths: paths:
...@@ -79,8 +85,9 @@ stages: ...@@ -79,8 +85,9 @@ stages:
- /(^docs[\/-].*|.*-docs$)/ - /(^docs[\/-].*|.*-docs$)/
.rspec-knapsack: &rspec-knapsack .rspec-knapsack: &rspec-knapsack
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache
stage: test
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[-2]} - export CI_NODE_INDEX=${JOB_NAME[-2]}
...@@ -110,8 +117,9 @@ stages: ...@@ -110,8 +117,9 @@ stages:
<<: *except-docs <<: *except-docs
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache
stage: test
script: script:
- JOB_NAME=( $CI_JOB_NAME ) - JOB_NAME=( $CI_JOB_NAME )
- export CI_NODE_INDEX=${JOB_NAME[-2]} - export CI_NODE_INDEX=${JOB_NAME[-2]}
...@@ -157,9 +165,13 @@ build-package: ...@@ -157,9 +165,13 @@ build-package:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "false" USE_BUNDLE_INSTALL: "false"
stage: build stage: build
cache: {}
when: manual when: manual
script: script:
- scripts/trigger-build - scripts/trigger-build
only:
- //@gitlab-org/gitlab-ce
- //@gitlab-org/gitlab-ee
# Prepare and merge knapsack tests # Prepare and merge knapsack tests
knapsack: knapsack:
...@@ -167,6 +179,11 @@ knapsack: ...@@ -167,6 +179,11 @@ knapsack:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
stage: prepare stage: prepare
cache:
key: knapsack
paths:
- knapsack/
policy: pull
script: script:
- mkdir -p knapsack/${CI_PROJECT_NAME}/ - mkdir -p knapsack/${CI_PROJECT_NAME}/
- wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH - wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH
...@@ -179,6 +196,11 @@ update-knapsack: ...@@ -179,6 +196,11 @@ update-knapsack:
<<: *dedicated-runner <<: *dedicated-runner
<<: *only-canonical-masters <<: *only-canonical-masters
stage: post-test stage: post-test
cache:
key: knapsack
paths:
- knapsack/
policy: push
script: script:
- retry gem install fog-aws mime-types - retry gem install fog-aws mime-types
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
...@@ -191,6 +213,8 @@ setup-test-env: ...@@ -191,6 +213,8 @@ setup-test-env:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
stage: prepare stage: prepare
cache:
<<: *default-cache
script: script:
- node --version - node --version
- yarn install --pure-lockfile --cache-folder .yarn-cache - yarn install --pure-lockfile --cache-folder .yarn-cache
...@@ -270,6 +294,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql ...@@ -270,6 +294,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql
# Static analysis jobs # Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis .ruby-static-analysis: &ruby-static-analysis
<<: *pull-cache
variables: variables:
SIMPLECOV: "false" SIMPLECOV: "false"
SETUP_DB: "false" SETUP_DB: "false"
...@@ -278,6 +303,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql ...@@ -278,6 +303,7 @@ spinach-mysql 4 5: *spinach-knapsack-mysql
<<: *ruby-static-analysis <<: *ruby-static-analysis
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test stage: test
script: script:
- bundle exec rake $CI_JOB_NAME - bundle exec rake $CI_JOB_NAME
...@@ -294,9 +320,9 @@ static-analysis: ...@@ -294,9 +320,9 @@ static-analysis:
# - Check validity of relative links # - Check validity of relative links
# - Make sure cURL examples in API docs use the full switches # - Make sure cURL examples in API docs use the full switches
docs lint: docs lint:
<<: *dedicated-runner
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
stage: test stage: test
<<: *dedicated-runner
cache: {} cache: {}
dependencies: [] dependencies: []
before_script: [] before_script: []
...@@ -339,9 +365,10 @@ ee_compat_check: ...@@ -339,9 +365,10 @@ ee_compat_check:
# DB migration, rollback, and seed jobs # DB migration, rollback, and seed jobs
.db-migrate-reset: &db-migrate-reset .db-migrate-reset: &db-migrate-reset
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
script: script:
- bundle exec rake db:migrate:reset - bundle exec rake db:migrate:reset
...@@ -354,11 +381,12 @@ db:migrate:reset-mysql: ...@@ -354,11 +381,12 @@ db:migrate:reset-mysql:
<<: *use-mysql <<: *use-mysql
.migration-paths: &migration-paths .migration-paths: &migration-paths
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *only-canonical-masters
<<: *pull-cache
stage: test
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
<<: *only-canonical-masters
script: script:
- git fetch origin v8.14.10 - git fetch origin v8.14.10
- git checkout -f FETCH_HEAD - git checkout -f FETCH_HEAD
...@@ -379,9 +407,10 @@ migration:path-mysql: ...@@ -379,9 +407,10 @@ migration:path-mysql:
<<: *use-mysql <<: *use-mysql
.db-rollback: &db-rollback .db-rollback: &db-rollback
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
script: script:
- bundle exec rake db:rollback STEP=120 - bundle exec rake db:rollback STEP=120
- bundle exec rake db:migrate - bundle exec rake db:migrate
...@@ -395,9 +424,10 @@ db:rollback-mysql: ...@@ -395,9 +424,10 @@ db:rollback-mysql:
<<: *use-mysql <<: *use-mysql
.db-seed_fu: &db-seed_fu .db-seed_fu: &db-seed_fu
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
variables: variables:
SIZE: "1" SIZE: "1"
SETUP_DB: "false" SETUP_DB: "false"
...@@ -422,9 +452,10 @@ db:seed_fu-mysql: ...@@ -422,9 +452,10 @@ db:seed_fu-mysql:
# Frontend-related jobs # Frontend-related jobs
gitlab:assets:compile: gitlab:assets:compile:
stage: test
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: test
dependencies: [] dependencies: []
variables: variables:
NODE_ENV: "production" NODE_ENV: "production"
...@@ -445,11 +476,12 @@ gitlab:assets:compile: ...@@ -445,11 +476,12 @@ gitlab:assets:compile:
- webpack-report/ - webpack-report/
karma: karma:
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
stage: test
<<: *use-pg <<: *use-pg
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
stage: test
variables: variables:
BABEL_ENV: "coverage" BABEL_ENV: "coverage"
CHROME_LOG_FILE: "chrome_debug.log" CHROME_LOG_FILE: "chrome_debug.log"
...@@ -467,6 +499,7 @@ karma: ...@@ -467,6 +499,7 @@ karma:
codeclimate: codeclimate:
<<: *except-docs <<: *except-docs
<<: *pull-cache
before_script: [] before_script: []
image: docker:latest image: docker:latest
stage: test stage: test
...@@ -482,10 +515,11 @@ codeclimate: ...@@ -482,10 +515,11 @@ codeclimate:
paths: [codeclimate.json] paths: [codeclimate.json]
coverage: coverage:
stage: post-test
services: []
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: post-test
services: []
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
USE_BUNDLE_INSTALL: "true" USE_BUNDLE_INSTALL: "true"
...@@ -502,7 +536,10 @@ coverage: ...@@ -502,7 +536,10 @@ coverage:
lint:javascript:report: lint:javascript:report:
<<: *dedicated-runner <<: *dedicated-runner
<<: *except-docs <<: *except-docs
<<: *pull-cache
stage: post-test stage: post-test
dependencies:
- setup-test-env
before_script: [] before_script: []
script: script:
- find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files - find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
...@@ -514,9 +551,10 @@ lint:javascript:report: ...@@ -514,9 +551,10 @@ lint:javascript:report:
- eslint-report.html - eslint-report.html
pages: pages:
<<: *dedicated-runner
<<: *pull-cache
before_script: [] before_script: []
stage: pages stage: pages
<<: *dedicated-runner
dependencies: dependencies:
- coverage - coverage
- karma - karma
...@@ -540,6 +578,7 @@ pages: ...@@ -540,6 +578,7 @@ pages:
# rubygems.org in the future. # rubygems.org in the future.
cache gems: cache gems:
<<: *dedicated-runner <<: *dedicated-runner
<<: *pull-cache
only: only:
- tags - tags
variables: variables:
...@@ -554,8 +593,9 @@ cache gems: ...@@ -554,8 +593,9 @@ cache gems:
- master@gitlab-org/gitlab-ee - master@gitlab-org/gitlab-ee
gitlab_git_test: gitlab_git_test:
<<: *pull-cache
<<: *except-docs
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
script: script:
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes - spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
<<: *except-docs
This diff is collapsed.
...@@ -37,7 +37,7 @@ gem 'omniauth-saml', '~> 1.7.0' ...@@ -37,7 +37,7 @@ gem 'omniauth-saml', '~> 1.7.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.0' gem 'omniauth-authentiq', '~> 0.3.1'
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt', '~> 1.5.6' gem 'jwt', '~> 1.5.6'
...@@ -71,7 +71,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false ...@@ -71,7 +71,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist' gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.19.0' gem 'grape', '~> 0.19.2'
gem 'grape-entity', '~> 0.6.0' gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
...@@ -163,6 +163,9 @@ gem 'rainbow', '~> 2.2' ...@@ -163,6 +163,9 @@ gem 'rainbow', '~> 2.2'
# GitLab settings # GitLab settings
gem 'settingslogic', '~> 2.0.9' gem 'settingslogic', '~> 2.0.9'
# Linear-time regex library for untrusted regular expressions
gem 're2', '~> 1.0.0'
# Misc # Misc
gem 'version_sorter', '~> 2.1.0' gem 'version_sorter', '~> 2.1.0'
...@@ -281,7 +284,7 @@ group :metrics do ...@@ -281,7 +284,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false gem 'influxdb', '~> 0.2', require: false
# Prometheus # Prometheus
gem 'prometheus-client-mmap', '~>0.7.0.beta5' gem 'prometheus-client-mmap', '~>0.7.0.beta9'
gem 'raindrops', '~> 0.18' gem 'raindrops', '~> 0.18'
end end
...@@ -383,7 +386,7 @@ gem 'vmstat', '~> 2.3.0' ...@@ -383,7 +386,7 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6' gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly', '~> 0.14.0' gem 'gitaly', '~> 0.18.0'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -269,7 +269,7 @@ GEM ...@@ -269,7 +269,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly (0.14.0) gitaly (0.18.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -332,13 +332,13 @@ GEM ...@@ -332,13 +332,13 @@ GEM
multi_json (~> 1.11) multi_json (~> 1.11)
os (~> 0.9) os (~> 0.9)
signet (~> 0.7) signet (~> 0.7)
grape (0.19.1) grape (0.19.2)
activesupport activesupport
builder builder
hashie (>= 2.1.0) hashie (>= 2.1.0)
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
mustermann-grape (~> 0.4.0) mustermann-grape (~> 1.0.0)
rack (>= 1.3.0) rack (>= 1.3.0)
rack-accept rack-accept
virtus (>= 1.0.0) virtus (>= 1.0.0)
...@@ -463,10 +463,9 @@ GEM ...@@ -463,10 +463,9 @@ GEM
multi_json (1.12.1) multi_json (1.12.1)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
mustermann (0.4.0) mustermann (1.0.0)
tool (~> 0.2) mustermann-grape (1.0.0)
mustermann-grape (0.4.0) mustermann (~> 1.0.0)
mustermann (= 0.4.0)
mysql2 (0.4.5) mysql2 (0.4.5)
net-ldap (0.12.1) net-ldap (0.12.1)
netrc (0.11.0) netrc (0.11.0)
...@@ -488,7 +487,7 @@ GEM ...@@ -488,7 +487,7 @@ GEM
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1) omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1) omniauth-oauth2 (~> 1.1)
omniauth-authentiq (0.3.0) omniauth-authentiq (0.3.1)
omniauth-oauth2 (~> 1.3, >= 1.3.1) omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.6) omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0) jwt (~> 1.0)
...@@ -592,7 +591,7 @@ GEM ...@@ -592,7 +591,7 @@ GEM
premailer-rails (1.9.7) premailer-rails (1.9.7)
actionmailer (>= 3, < 6) actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9) premailer (~> 1.7, >= 1.7.9)
prometheus-client-mmap (0.7.0.beta8) prometheus-client-mmap (0.7.0.beta9)
mmap2 (~> 2.2, >= 2.2.7) mmap2 (~> 2.2, >= 2.2.7)
pry (0.10.4) pry (0.10.4)
coderay (~> 1.1.0) coderay (~> 1.1.0)
...@@ -657,6 +656,7 @@ GEM ...@@ -657,6 +656,7 @@ GEM
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rdoc (4.2.2) rdoc (4.2.2)
json (~> 1.4) json (~> 1.4)
re2 (1.0.0)
recaptcha (3.0.0) recaptcha (3.0.0)
json json
recursive-open-struct (1.0.0) recursive-open-struct (1.0.0)
...@@ -849,7 +849,6 @@ GEM ...@@ -849,7 +849,6 @@ GEM
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
toml-rb (0.3.15) toml-rb (0.3.15)
citrus (~> 3.0, > 3.0) citrus (~> 3.0, > 3.0)
tool (0.2.3)
truncato (0.7.8) truncato (0.7.8)
htmlentities (~> 4.3.1) htmlentities (~> 4.3.1)
nokogiri (~> 1.6.1) nokogiri (~> 1.6.1)
...@@ -971,7 +970,7 @@ DEPENDENCIES ...@@ -971,7 +970,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly (~> 0.14.0) gitaly (~> 0.18.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.5.1) gitlab-markup (~> 1.5.1)
...@@ -980,7 +979,7 @@ DEPENDENCIES ...@@ -980,7 +979,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.4) gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.8.6)
grape (~> 0.19.0) grape (~> 0.19.2)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
haml_lint (~> 0.21.0) haml_lint (~> 0.21.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
...@@ -1015,7 +1014,7 @@ DEPENDENCIES ...@@ -1015,7 +1014,7 @@ DEPENDENCIES
oj (~> 2.17.4) oj (~> 2.17.4)
omniauth (~> 1.4.2) omniauth (~> 1.4.2)
omniauth-auth0 (~> 1.4.1) omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.3.0) omniauth-authentiq (~> 0.3.1)
omniauth-azure-oauth2 (~> 0.0.6) omniauth-azure-oauth2 (~> 0.0.6)
omniauth-cas3 (~> 1.1.2) omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 4.0.0) omniauth-facebook (~> 4.0.0)
...@@ -1042,7 +1041,7 @@ DEPENDENCIES ...@@ -1042,7 +1041,7 @@ DEPENDENCIES
pg (~> 0.18.2) pg (~> 0.18.2)
poltergeist (~> 1.9.0) poltergeist (~> 1.9.0)
premailer-rails (~> 1.9.7) premailer-rails (~> 1.9.7)
prometheus-client-mmap (~> 0.7.0.beta5) prometheus-client-mmap (~> 0.7.0.beta9)
pry-byebug (~> 3.4.1) pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1) rack-attack (~> 4.4.1)
...@@ -1056,6 +1055,7 @@ DEPENDENCIES ...@@ -1056,6 +1055,7 @@ DEPENDENCIES
raindrops (~> 0.18) raindrops (~> 0.18)
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
rdoc (~> 4.2) rdoc (~> 4.2)
re2 (~> 1.0.0)
recaptcha (~> 3.0) recaptcha (~> 3.0)
redcarpet (~> 3.4) redcarpet (~> 3.4)
redis (~> 3.2) redis (~> 3.2)
......
9.4.0-pre 9.5.0-pre
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
/* global ProjectSelect */
/* global ShortcutsNavigation */ /* global ShortcutsNavigation */
/* global IssuableIndex */ /* global IssuableIndex */
/* global ShortcutsIssuable */ /* global ShortcutsIssuable */
...@@ -40,7 +41,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater'; ...@@ -40,7 +41,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
import Landing from './landing'; import Landing from './landing';
import BlobForkSuggestion from './blob/blob_fork_suggestion'; import BlobForkSuggestion from './blob/blob_fork_suggestion';
import UserCallout from './user_callout'; import UserCallout from './user_callout';
import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags';
import ShortcutsWiki from './shortcuts_wiki'; import ShortcutsWiki from './shortcuts_wiki';
import Pipelines from './pipelines'; import Pipelines from './pipelines';
import BlobViewer from './blob/viewer/index'; import BlobViewer from './blob/viewer/index';
...@@ -157,6 +157,9 @@ import PerformanceBar from './performance_bar'; ...@@ -157,6 +157,9 @@ import PerformanceBar from './performance_bar';
shortcut_handler = new ShortcutsIssuable(); shortcut_handler = new ShortcutsIssuable();
new ZenMode(); new ZenMode();
break; break;
case 'dashboard:milestones:index':
new ProjectSelect();
break;
case 'projects:milestones:show': case 'projects:milestones:show':
case 'groups:milestones:show': case 'groups:milestones:show':
case 'dashboard:milestones:show': case 'dashboard:milestones:show':
...@@ -166,6 +169,7 @@ import PerformanceBar from './performance_bar'; ...@@ -166,6 +169,7 @@ import PerformanceBar from './performance_bar';
case 'groups:issues': case 'groups:issues':
case 'groups:merge_requests': case 'groups:merge_requests':
new UsersSelect(); new UsersSelect();
new ProjectSelect();
break; break;
case 'dashboard:todos:index': case 'dashboard:todos:index':
new Todos(); new Todos();
...@@ -259,6 +263,7 @@ import PerformanceBar from './performance_bar'; ...@@ -259,6 +263,7 @@ import PerformanceBar from './performance_bar';
break; break;
case 'dashboard:issues': case 'dashboard:issues':
case 'dashboard:merge_requests': case 'dashboard:merge_requests':
new ProjectSelect();
new UsersSelect(); new UsersSelect();
break; break;
case 'projects:commit:show': case 'projects:commit:show':
...@@ -390,12 +395,6 @@ import PerformanceBar from './performance_bar'; ...@@ -390,12 +395,6 @@ import PerformanceBar from './performance_bar';
new Search(); new Search();
break; break;
case 'projects:settings:repository:show': case 'projects:settings:repository:show':
// Initialize Protected Branch Settings
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
// Initialize Protected Tag Settings
new ProtectedTagCreate();
new ProtectedTagEditList();
// Initialize expandable settings panels // Initialize expandable settings panels
initSettingsPanels(); initSettingsPanels();
break; break;
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
/* global dateFormat */ /* global dateFormat */
/* global Pikaday */ /* global Pikaday */
import DateFix from './lib/utils/datefix';
class DueDateSelect { class DueDateSelect {
constructor({ $dropdown, $loading } = {}) { constructor({ $dropdown, $loading } = {}) {
const $dropdownParent = $dropdown.closest('.dropdown'); const $dropdownParent = $dropdown.closest('.dropdown');
...@@ -43,14 +45,13 @@ class DueDateSelect { ...@@ -43,14 +45,13 @@ class DueDateSelect {
initDatePicker() { initDatePicker() {
const $dueDateInput = $(`input[name='${this.fieldName}']`); const $dueDateInput = $(`input[name='${this.fieldName}']`);
const dateFix = DateFix.dashedFix($dueDateInput.val());
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $dueDateInput.get(0), field: $dueDateInput.get(0),
theme: 'gitlab-theme', theme: 'gitlab-theme',
format: 'yyyy-mm-dd', format: 'yyyy-mm-dd',
onSelect: (dateText) => { onSelect: (dateText) => {
const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd'); const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd');
$dueDateInput.val(formattedDate); $dueDateInput.val(formattedDate);
if (this.$dropdown.hasClass('js-issue-boards-due-date')) { if (this.$dropdown.hasClass('js-issue-boards-due-date')) {
...@@ -62,7 +63,7 @@ class DueDateSelect { ...@@ -62,7 +63,7 @@ class DueDateSelect {
} }
}); });
calendar.setDate(new Date($dueDateInput.val())); calendar.setDate(dateFix);
this.$datePicker.append(calendar.el); this.$datePicker.append(calendar.el);
this.$datePicker.data('pikaday', calendar); this.$datePicker.data('pikaday', calendar);
} }
...@@ -168,6 +169,7 @@ class DueDateSelectors { ...@@ -168,6 +169,7 @@ class DueDateSelectors {
initMilestoneDatePicker() { initMilestoneDatePicker() {
$('.datepicker').each(function() { $('.datepicker').each(function() {
const $datePicker = $(this); const $datePicker = $(this);
const dateFix = DateFix.dashedFix($datePicker.val());
const calendar = new Pikaday({ const calendar = new Pikaday({
field: $datePicker.get(0), field: $datePicker.get(0),
theme: 'gitlab-theme animate-picker', theme: 'gitlab-theme animate-picker',
...@@ -177,7 +179,8 @@ class DueDateSelectors { ...@@ -177,7 +179,8 @@ class DueDateSelectors {
$datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd'));
} }
}); });
calendar.setDate(new Date($datePicker.val()));
calendar.setDate(dateFix);
$datePicker.data('pikaday', calendar); $datePicker.data('pikaday', calendar);
}); });
......
...@@ -2,8 +2,9 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -2,8 +2,9 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown'; import './filtered_search_dropdown';
class DropdownHint extends gl.FilteredSearchDropdown { class DropdownHint extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, tokenKeys, filter) { constructor(options = {}) {
super(droplab, dropdown, input, filter); const { input, tokenKeys } = options;
super(options);
this.config = { this.config = {
Filter: { Filter: {
template: 'hint', template: 'hint',
......
...@@ -5,8 +5,9 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -5,8 +5,9 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown'; import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown { class DropdownNonUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, tokenKeys, filter, endpoint, symbol) { constructor(options = {}) {
super(droplab, dropdown, input, filter); const { input, endpoint, symbol } = options;
super(options);
this.symbol = symbol; this.symbol = symbol;
this.config = { this.config = {
Ajax: { Ajax: {
......
...@@ -5,8 +5,9 @@ import './filtered_search_dropdown'; ...@@ -5,8 +5,9 @@ import './filtered_search_dropdown';
import { addClassIfElementExists } from '../lib/utils/dom_utils'; import { addClassIfElementExists } from '../lib/utils/dom_utils';
class DropdownUser extends gl.FilteredSearchDropdown { class DropdownUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, tokenKeys, filter) { constructor(options = {}) {
super(droplab, dropdown, input, filter); const { tokenKeys } = options;
super(options);
this.config = { this.config = {
AjaxFilter: { AjaxFilter: {
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`, endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
......
const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger'; const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
class FilteredSearchDropdown { class FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) { constructor({ droplab, dropdown, input, filter }) {
this.droplab = droplab; this.droplab = droplab;
this.hookId = input && input.id; this.hookId = input && input.id;
this.input = input; this.input = input;
......
...@@ -42,13 +42,19 @@ class FilteredSearchDropdownManager { ...@@ -42,13 +42,19 @@ class FilteredSearchDropdownManager {
milestone: { milestone: {
reference: null, reference: null,
gl: 'DropdownNonUser', gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/milestones.json`, '%'], extraArguments: {
endpoint: `${this.baseEndpoint}/milestones.json`,
symbol: '%',
},
element: this.container.querySelector('#js-dropdown-milestone'), element: this.container.querySelector('#js-dropdown-milestone'),
}, },
label: { label: {
reference: null, reference: null,
gl: 'DropdownNonUser', gl: 'DropdownNonUser',
extraArguments: [`${this.baseEndpoint}/labels.json`, '~'], extraArguments: {
endpoint: `${this.baseEndpoint}/labels.json`,
symbol: '~',
},
element: this.container.querySelector('#js-dropdown-label'), element: this.container.querySelector('#js-dropdown-label'),
}, },
hint: { hint: {
...@@ -97,13 +103,19 @@ class FilteredSearchDropdownManager { ...@@ -97,13 +103,19 @@ class FilteredSearchDropdownManager {
let forceShowList = false; let forceShowList = false;
if (!mappingKey.reference) { if (!mappingKey.reference) {
const dl = this.droplab; const defaultArguments = {
const defaultArguments = droplab: this.droplab,
[null, dl, element, this.filteredSearchInput, this.filteredSearchTokenKeys, key]; dropdown: element,
const glArguments = defaultArguments.concat(mappingKey.extraArguments || []); input: this.filteredSearchInput,
tokenKeys: this.filteredSearchTokenKeys,
filter: key,
};
const extraArguments = mappingKey.extraArguments || {};
const glArguments = Object.assign({}, defaultArguments, extraArguments);
// Passing glArguments to `new gl[glClass](<arguments>)` // Passing glArguments to `new gl[glClass](<arguments>)`
mappingKey.reference = new (Function.prototype.bind.apply(gl[glClass], glArguments))(); mappingKey.reference =
new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))();
} }
if (firstLoad) { if (firstLoad) {
......
...@@ -5,6 +5,8 @@ export default class GroupName { ...@@ -5,6 +5,8 @@ export default class GroupName {
constructor() { constructor() {
this.titleContainer = document.querySelector('.js-title-container'); this.titleContainer = document.querySelector('.js-title-container');
this.title = this.titleContainer.querySelector('.title'); this.title = this.titleContainer.querySelector('.title');
if (this.title) {
this.titleWidth = this.title.offsetWidth; this.titleWidth = this.title.offsetWidth;
this.groupTitle = this.titleContainer.querySelector('.group-title'); this.groupTitle = this.titleContainer.querySelector('.group-title');
this.groups = this.titleContainer.querySelectorAll('.group-path'); this.groups = this.titleContainer.querySelectorAll('.group-path');
...@@ -12,6 +14,7 @@ export default class GroupName { ...@@ -12,6 +14,7 @@ export default class GroupName {
this.isHidden = false; this.isHidden = false;
this.init(); this.init();
} }
}
init() { init() {
if (this.groups.length > 0) { if (this.groups.length > 0) {
......
...@@ -86,10 +86,23 @@ export default class IssuableBulkUpdateSidebar { ...@@ -86,10 +86,23 @@ export default class IssuableBulkUpdateSidebar {
this.toggleCheckboxDisplay(enable); this.toggleCheckboxDisplay(enable);
if (enable) { if (enable) {
this.initAffix();
SidebarHeightManager.init(); SidebarHeightManager.init();
} }
} }
initAffix() {
if (!this.$sidebar.hasClass('affix-top')) {
const offsetTop = $('.scrolling-tabs-container').outerHeight() + $('.sub-nav-scroll').outerHeight();
this.$sidebar.affix({
offset: {
top: offsetTop,
},
});
}
}
updateSelectedIssuableIds() { updateSelectedIssuableIds() {
this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds()); this.$issuableIdsInput.val(IssuableBulkUpdateSidebar.getCheckedIssueIds());
} }
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, vars-on-top, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, no-unused-vars, one-var, one-var-declaration-per-line, vars-on-top, max-len */
import Cookies from 'js-cookie';
import _ from 'underscore'; import _ from 'underscore';
import Cookies from 'js-cookie';
import NewNavSidebar from './new_sidebar';
import initFlyOutNav from './fly_out_nav'; import initFlyOutNav from './fly_out_nav';
(function() { (function() {
...@@ -55,10 +56,13 @@ import initFlyOutNav from './fly_out_nav'; ...@@ -55,10 +56,13 @@ import initFlyOutNav from './fly_out_nav';
} }
$(() => { $(() => {
$(window).on('scroll', _.throttle(applyScrollNavClass, 100));
if (Cookies.get('new_nav') === 'true') { if (Cookies.get('new_nav') === 'true') {
const newNavSidebar = new NewNavSidebar();
newNavSidebar.bindEvents();
initFlyOutNav(); initFlyOutNav();
} }
$(window).on('scroll', _.throttle(applyScrollNavClass, 100));
}); });
}).call(window); }).call(window);
const DateFix = {
dashedFix(val) {
const [y, m, d] = val.split('-');
return new Date(y, m - 1, d);
},
};
export default DateFix;
export default class NewNavSidebar {
constructor() {
this.initDomElements();
}
initDomElements() {
this.$sidebar = $('.nav-sidebar');
this.$overlay = $('.mobile-overlay');
this.$openSidebar = $('.toggle-mobile-nav');
this.$closeSidebar = $('.close-nav-button');
}
bindEvents() {
this.$openSidebar.on('click', () => this.toggleSidebarNav(true));
this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
this.$overlay.on('click', () => this.toggleSidebarNav(false));
}
toggleSidebarNav(show) {
this.$sidebar.toggleClass('nav-sidebar-expanded', show);
this.$overlay.toggleClass('mobile-nav-open', show);
}
}
...@@ -104,6 +104,14 @@ import Api from './api'; ...@@ -104,6 +104,14 @@ import Api from './api';
dropdownCssClass: "ajax-project-dropdown" dropdownCssClass: "ajax-project-dropdown"
}); });
}); });
$('.new-project-item-select-button').on('click', function() {
$('.project-item-select', this.parentNode).select2('open');
});
$('.project-item-select').on('click', function() {
window.location = `${$(this).val()}/${this.dataset.relativePath}`;
});
} }
return ProjectSelect; return ProjectSelect;
......
/* eslint-disable no-unused-vars */
import ProtectedBranchCreate from './protected_branch_create';
import ProtectedBranchEditList from './protected_branch_edit_list';
$(() => {
const protectedBranchCreate = new ProtectedBranchCreate();
const protectedBranchEditList = new ProtectedBranchEditList();
});
/* eslint-disable arrow-parens, no-param-reassign, object-shorthand, no-else-return, comma-dangle, max-len */ export default class ProtectedBranchAccessDropdown {
(global => {
global.gl = global.gl || {};
gl.ProtectedBranchAccessDropdown = class {
constructor(options) { constructor(options) {
const { $dropdown, data, onSelect } = options; this.options = options;
this.initDropdown();
}
initDropdown() {
const { $dropdown, data, onSelect } = this.options;
$dropdown.glDropdown({ $dropdown.glDropdown({
data: data, data,
selectable: true, selectable: true,
inputId: $dropdown.data('input-id'), inputId: $dropdown.data('input-id'),
fieldName: $dropdown.data('field-name'), fieldName: $dropdown.data('field-name'),
toggleLabel(item, el) { toggleLabel(item, $el) {
if (el.is('.is-active')) { if ($el.is('.is-active')) {
return item.text; return item.text;
} else {
return 'Select';
} }
return 'Select';
}, },
clicked(opts) { clicked(options) {
const { e } = opts; options.e.preventDefault();
e.preventDefault();
onSelect(); onSelect();
} },
}); });
} }
}; }
})(window);
/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
/* global ProtectedBranchDropdown */ import ProtectedBranchDropdown from './protected_branch_dropdown';
(global => { export default class ProtectedBranchCreate {
global.gl = global.gl || {};
gl.ProtectedBranchCreate = class {
constructor() { constructor() {
this.$wrap = this.$form = $('#new_protected_branch'); this.$form = $('.js-new-protected-branch');
this.buildDropdowns(); this.buildDropdowns();
} }
buildDropdowns() { buildDropdowns() {
const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge');
const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push');
// Cache callback // Cache callback
this.onSelectCallback = this.onSelect.bind(this); this.onSelectCallback = this.onSelect.bind(this);
// Allowed to Merge dropdown // Allowed to Merge dropdown
new gl.ProtectedBranchAccessDropdown({ this.protectedBranchMergeAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: $allowedToMergeDropdown, $dropdown: $allowedToMergeDropdown,
data: gon.merge_access_levels, data: gon.merge_access_levels,
onSelect: this.onSelectCallback onSelect: this.onSelectCallback,
}); });
// Allowed to Push dropdown // Allowed to Push dropdown
new gl.ProtectedBranchAccessDropdown({ this.protectedBranchPushAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: $allowedToPushDropdown, $dropdown: $allowedToPushDropdown,
data: gon.push_access_levels, data: gon.push_access_levels,
onSelect: this.onSelectCallback onSelect: this.onSelectCallback,
}); });
// Select default // Select default
...@@ -36,20 +33,19 @@ ...@@ -36,20 +33,19 @@
$allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
// Protected branch dropdown // Protected branch dropdown
new ProtectedBranchDropdown({ this.protectedBranchDropdown = new ProtectedBranchDropdown({
$dropdown: this.$wrap.find('.js-protected-branch-select'), $dropdown: this.$form.find('.js-protected-branch-select'),
onSelect: this.onSelectCallback onSelect: this.onSelectCallback,
}); });
} }
// This will run after clicked callback // This will run after clicked callback
onSelect() { onSelect() {
// Enable submit button // Enable submit button
const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]'); const $branchInput = this.$form.find('input[name="protected_branch[name]"]');
const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]');
const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]');
this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length)); this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length));
} }
}; }
})(window);
/* eslint-disable comma-dangle, no-unused-vars */ export default class ProtectedBranchDropdown {
/**
class ProtectedBranchDropdown { * @param {Object} options containing
* `$dropdown` target element
* `onSelect` event callback
* $dropdown must be an element created using `dropdown_branch()` rails helper
*/
constructor(options) { constructor(options) {
this.onSelect = options.onSelect; this.onSelect = options.onSelect;
this.$dropdown = options.$dropdown; this.$dropdown = options.$dropdown;
...@@ -12,7 +16,7 @@ class ProtectedBranchDropdown { ...@@ -12,7 +16,7 @@ class ProtectedBranchDropdown {
this.bindEvents(); this.bindEvents();
// Hide footer // Hide footer
this.$dropdownFooter.addClass('hidden'); this.toggleFooter(true);
} }
buildDropdown() { buildDropdown() {
...@@ -21,7 +25,7 @@ class ProtectedBranchDropdown { ...@@ -21,7 +25,7 @@ class ProtectedBranchDropdown {
filterable: true, filterable: true,
remote: false, remote: false,
search: { search: {
fields: ['title'] fields: ['title'],
}, },
selectable: true, selectable: true,
toggleLabel(selected) { toggleLabel(selected) {
...@@ -36,10 +40,9 @@ class ProtectedBranchDropdown { ...@@ -36,10 +40,9 @@ class ProtectedBranchDropdown {
}, },
onFilter: this.toggleCreateNewButton.bind(this), onFilter: this.toggleCreateNewButton.bind(this),
clicked: (options) => { clicked: (options) => {
const { $el, e } = options; options.e.preventDefault();
e.preventDefault();
this.onSelect(); this.onSelect();
} },
}); });
} }
...@@ -64,20 +67,22 @@ class ProtectedBranchDropdown { ...@@ -64,20 +67,22 @@ class ProtectedBranchDropdown {
} }
toggleCreateNewButton(branchName) { toggleCreateNewButton(branchName) {
if (branchName) {
this.selectedBranch = { this.selectedBranch = {
title: branchName, title: branchName,
id: branchName, id: branchName,
text: branchName text: branchName,
}; };
if (branchName) {
this.$dropdownContainer this.$dropdownContainer
.find('.js-create-new-protected-branch code') .find('.js-create-new-protected-branch code')
.text(branchName); .text(branchName);
} }
this.$dropdownFooter.toggleClass('hidden', !branchName); this.toggleFooter(!branchName);
} }
}
window.ProtectedBranchDropdown = ProtectedBranchDropdown; toggleFooter(toggleState) {
this.$dropdownFooter.toggleClass('hidden', toggleState);
}
}
/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ /* eslint-disable no-new */
/* global Flash */ /* global Flash */
(global => { import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown';
global.gl = global.gl || {};
gl.ProtectedBranchEdit = class { export default class ProtectedBranchEdit {
constructor(options) { constructor(options) {
this.$wrap = options.$wrap; this.$wrap = options.$wrap;
this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
this.onSelectCallback = this.onSelect.bind(this);
this.buildDropdowns(); this.buildDropdowns();
} }
buildDropdowns() { buildDropdowns() {
// Allowed to merge dropdown // Allowed to merge dropdown
new gl.ProtectedBranchAccessDropdown({ this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToMergeDropdown, $dropdown: this.$allowedToMergeDropdown,
data: gon.merge_access_levels, data: gon.merge_access_levels,
onSelect: this.onSelect.bind(this) onSelect: this.onSelectCallback,
}); });
// Allowed to push dropdown // Allowed to push dropdown
new gl.ProtectedBranchAccessDropdown({ this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({
$dropdown: this.$allowedToPushDropdown, $dropdown: this.$allowedToPushDropdown,
data: gon.push_access_levels, data: gon.push_access_levels,
onSelect: this.onSelect.bind(this) onSelect: this.onSelectCallback,
}); });
} }
...@@ -48,22 +48,20 @@ ...@@ -48,22 +48,20 @@
protected_branch: { protected_branch: {
merge_access_levels_attributes: [{ merge_access_levels_attributes: [{
id: this.$allowedToMergeDropdown.data('access-level-id'), id: this.$allowedToMergeDropdown.data('access-level-id'),
access_level: $allowedToMergeInput.val() access_level: $allowedToMergeInput.val(),
}], }],
push_access_levels_attributes: [{ push_access_levels_attributes: [{
id: this.$allowedToPushDropdown.data('access-level-id'), id: this.$allowedToPushDropdown.data('access-level-id'),
access_level: $allowedToPushInput.val() access_level: $allowedToPushInput.val(),
}] }],
} },
}, },
error() { error() {
$.scrollTo(0); new Flash('Failed to update branch!', null, $('.js-protected-branches-list'));
new Flash('Failed to update branch!'); },
}
}).always(() => { }).always(() => {
this.$allowedToMergeDropdown.enable(); this.$allowedToMergeDropdown.enable();
this.$allowedToPushDropdown.enable(); this.$allowedToPushDropdown.enable();
}); });
} }
}; }
})(window);
/* eslint-disable arrow-parens, no-param-reassign, no-new, comma-dangle */ /* eslint-disable no-new */
(global => { import ProtectedBranchEdit from './protected_branch_edit';
global.gl = global.gl || {};
gl.ProtectedBranchEditList = class { export default class ProtectedBranchEditList {
constructor() { constructor() {
this.$wrap = $('.protected-branches-list'); this.$wrap = $('.protected-branches-list');
this.initEditForm();
}
// Build edit forms initEditForm() {
this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
new gl.ProtectedBranchEdit({ new ProtectedBranchEdit({
$wrap: $(el) $wrap: $(el),
}); });
}); });
} }
}; }
})(window);
import './protected_branch_access_dropdown';
import './protected_branch_create';
import './protected_branch_dropdown';
import './protected_branch_edit';
import './protected_branch_edit_list';
export { default as ProtectedTagCreate } from './protected_tag_create'; /* eslint-disable no-unused-vars */
export { default as ProtectedTagEditList } from './protected_tag_edit_list';
import ProtectedTagCreate from './protected_tag_create';
import ProtectedTagEditList from './protected_tag_edit_list';
$(() => {
const protectedtTagCreate = new ProtectedTagCreate();
const protectedtTagEditList = new ProtectedTagEditList();
});
...@@ -190,14 +190,6 @@ ...@@ -190,14 +190,6 @@
display: none; display: none;
} }
.btn,
.dropdown,
.dropdown-toggle,
input,
form {
height: 35px;
}
input { input {
display: inline-block; display: inline-block;
position: relative; position: relative;
......
...@@ -92,7 +92,6 @@ ...@@ -92,7 +92,6 @@
@mixin maintain-sidebar-dimensions { @mixin maintain-sidebar-dimensions {
display: block; display: block;
width: $gutter-width; width: $gutter-width;
padding: 10px 0;
} }
.issues-bulk-update.right-sidebar { .issues-bulk-update.right-sidebar {
...@@ -104,6 +103,15 @@ ...@@ -104,6 +103,15 @@
&.right-sidebar-expanded { &.right-sidebar-expanded {
@include maintain-sidebar-dimensions; @include maintain-sidebar-dimensions;
width: $gutter-width; width: $gutter-width;
.issuable-sidebar-header {
// matches `.top-area .nav-controls` for issuable index pages
padding: 11px 0;
}
.block:last-of-type {
border: none;
}
} }
&.right-sidebar-collapsed { &.right-sidebar-collapsed {
......
...@@ -116,9 +116,12 @@ ...@@ -116,9 +116,12 @@
blockquote p { blockquote p {
color: $gl-grayish-blue !important; color: $gl-grayish-blue !important;
margin: 0;
font-size: inherit; font-size: inherit;
line-height: 1.5; line-height: 1.5;
&:last-child {
margin: 0;
}
} }
p { p {
......
...@@ -41,10 +41,22 @@ header.navbar-gitlab-new { ...@@ -41,10 +41,22 @@ header.navbar-gitlab-new {
} }
} }
.logo-text {
line-height: initial;
svg {
width: 55px;
height: 15px;
margin: 0;
fill: $white-light;
}
}
&:hover, &:hover,
&:focus { &:focus {
color: $tanuki-yellow; .logo-text svg {
text-decoration: none; fill: $tanuki-yellow;
}
} }
} }
} }
...@@ -274,9 +286,7 @@ header.navbar-gitlab-new { ...@@ -274,9 +286,7 @@ header.navbar-gitlab-new {
.breadcrumbs { .breadcrumbs {
display: flex; display: flex;
min-height: 60px; min-height: 61px;
padding-top: $gl-padding-top;
padding-bottom: $gl-padding-top;
color: $gl-text-color; color: $gl-text-color;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
...@@ -300,6 +310,7 @@ header.navbar-gitlab-new { ...@@ -300,6 +310,7 @@ header.navbar-gitlab-new {
display: flex; display: flex;
width: 100%; width: 100%;
position: relative; position: relative;
align-items: center;
.dropdown-menu-projects { .dropdown-menu-projects {
margin-top: -$gl-padding; margin-top: -$gl-padding;
...@@ -330,7 +341,7 @@ header.navbar-gitlab-new { ...@@ -330,7 +341,7 @@ header.navbar-gitlab-new {
white-space: nowrap; white-space: nowrap;
> a { > a {
&:last-of-type { &:last-of-type:not(:first-child) {
font-weight: 600; font-weight: 600;
} }
} }
...@@ -384,6 +395,7 @@ header.navbar-gitlab-new { ...@@ -384,6 +395,7 @@ header.navbar-gitlab-new {
&::after { &::after {
content: "/"; content: "/";
margin: 0 2px 0 5px; margin: 0 2px 0 5px;
color: rgba($black, .65);
} }
} }
...@@ -396,3 +408,13 @@ header.navbar-gitlab-new { ...@@ -396,3 +408,13 @@ header.navbar-gitlab-new {
color: $gl-text-color; color: $gl-text-color;
} }
} }
.top-area {
.nav-controls-new-nav {
.dropdown {
@media (min-width: $screen-sm-min) {
margin-right: 0;
}
}
}
}
...@@ -23,9 +23,16 @@ $new-sidebar-width: 220px; ...@@ -23,9 +23,16 @@ $new-sidebar-width: 220px;
position: fixed; position: fixed;
height: 100%; height: 100%;
} }
.issues-bulk-update.right-sidebar.right-sidebar-expanded .issuable-sidebar-header {
padding: 10px 0 15px;
}
} }
.context-header { .context-header {
position: relative;
a {
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
font-weight: 600; font-weight: 600;
display: flex; display: flex;
...@@ -33,9 +40,8 @@ $new-sidebar-width: 220px; ...@@ -33,9 +40,8 @@ $new-sidebar-width: 220px;
padding: 10px 16px 10px 10px; padding: 10px 16px 10px 10px;
color: $gl-text-color; color: $gl-text-color;
.avatar-container { @media (max-width: $screen-xs-max) {
flex: 0 0 40px; padding-right: 30px;
background-color: $white-light;
} }
&:hover { &:hover {
...@@ -55,12 +61,44 @@ $new-sidebar-width: 220px; ...@@ -55,12 +61,44 @@ $new-sidebar-width: 220px;
} }
} }
} }
}
.avatar-container {
flex: 0 0 40px;
background-color: $white-light;
}
.project-title, .project-title,
.group-title { .group-title {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
&:hover {
.close-nav-button {
color: $white-light;
}
}
.close-nav-button {
display: none;
position: absolute;
top: 0;
right: 0;
height: 100%;
background-color: transparent;
border: 0;
padding: 0 10px;
@media (max-width: $screen-xs-max) {
display: block;
}
&:hover {
color: $gl-text-color;
}
}
} }
.settings-avatar { .settings-avatar {
...@@ -79,7 +117,7 @@ $new-sidebar-width: 220px; ...@@ -79,7 +117,7 @@ $new-sidebar-width: 220px;
position: fixed; position: fixed;
z-index: 400; z-index: 400;
width: $new-sidebar-width; width: $new-sidebar-width;
transition: width $sidebar-transition-duration; transition: left $sidebar-transition-duration;
top: 50px; top: 50px;
bottom: 0; bottom: 0;
left: 0; left: 0;
...@@ -87,6 +125,10 @@ $new-sidebar-width: 220px; ...@@ -87,6 +125,10 @@ $new-sidebar-width: 220px;
background-color: $gray-normal; background-color: $gray-normal;
box-shadow: inset -2px 0 0 $border-color; box-shadow: inset -2px 0 0 $border-color;
&.nav-sidebar-expanded {
left: 0;
}
a { a {
transition: none; transition: none;
text-decoration: none; text-decoration: none;
...@@ -117,7 +159,7 @@ $new-sidebar-width: 220px; ...@@ -117,7 +159,7 @@ $new-sidebar-width: 220px;
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
width: 0; left: (-$new-sidebar-width);
} }
} }
...@@ -223,6 +265,38 @@ $new-sidebar-width: 220px; ...@@ -223,6 +265,38 @@ $new-sidebar-width: 220px;
} }
} }
.toggle-mobile-nav {
display: none;
background-color: transparent;
border: 0;
padding: 6px 16px;
margin: 0 16px 0 -15px;
height: 46px;
border-right: 1px solid $gl-text-color-quaternary;
i {
font-size: 20px;
color: $gl-text-color-secondary;
}
@media (max-width: $screen-xs-max) {
display: inline-block;
}
}
.mobile-overlay {
display: none;
&.mobile-nav-open {
display: block;
position: fixed;
background-color: $black-transparent;
height: 100%;
width: 100%;
z-index: 300;
}
}
// Make issue boards full-height now that sub-nav is gone // Make issue boards full-height now that sub-nav is gone
...@@ -232,7 +306,7 @@ $new-sidebar-width: 220px; ...@@ -232,7 +306,7 @@ $new-sidebar-width: 220px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS height: 475px; // Needed for PhantomJS
// scss-lint:disable DuplicateProperty // scss-lint:disable DuplicateProperty
height: calc(100vh - 120px); height: calc(100vh - 180px);
// scss-lint:enable DuplicateProperty // scss-lint:enable DuplicateProperty
} }
} }
......
...@@ -54,7 +54,11 @@ ...@@ -54,7 +54,11 @@
.mr-widget-pipeline-graph { .mr-widget-pipeline-graph {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
margin: 0 -6px 0 0; margin-right: 4px;
.stage-cell .stage-container {
margin: 3px 3px 3px 0;
}
.dropdown-menu { .dropdown-menu {
margin-top: 11px; margin-top: 11px;
......
...@@ -376,3 +376,18 @@ table.u2f-registrations { ...@@ -376,3 +376,18 @@ table.u2f-registrations {
} }
} }
} }
.nav-wip {
border: 1px solid $blue-500;
background: $blue-25;
padding: $gl-padding;
margin-bottom: $gl-padding;
a {
color: $blue-500;
}
p:last-child {
margin-bottom: 0;
}
}
...@@ -742,7 +742,8 @@ pre.light-well { ...@@ -742,7 +742,8 @@ pre.light-well {
} }
} }
.protected-tags-list { .protected-tags-list,
.protected-branches-list {
.dropdown-menu-toggle { .dropdown-menu-toggle {
width: 100%; width: 100%;
max-width: 300px; max-width: 300px;
......
...@@ -10,9 +10,9 @@ class Admin::HookLogsController < Admin::ApplicationController ...@@ -10,9 +10,9 @@ class Admin::HookLogsController < Admin::ApplicationController
end end
def retry def retry
status, message = hook.execute(hook_log.request_data, hook_log.trigger) result = hook.execute(hook_log.request_data, hook_log.trigger)
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
redirect_to edit_admin_hook_path(@hook) redirect_to edit_admin_hook_path(@hook)
end end
......
...@@ -38,9 +38,9 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -38,9 +38,9 @@ class Admin::HooksController < Admin::ApplicationController
end end
def test def test
status, message = hook.execute(sample_hook_data, 'system_hooks') result = TestHooks::SystemService.new(hook, current_user, params[:trigger]).execute
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
redirect_back_or_default redirect_back_or_default
end end
...@@ -66,15 +66,4 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -66,15 +66,4 @@ class Admin::HooksController < Admin::ApplicationController
:url :url
) )
end end
def sample_hook_data
{
event_name: "project_create",
name: "Ruby",
path: "ruby",
project_id: 1,
owner_name: "Someone",
owner_email: "example@gitlabhq.com"
}
end
end end
...@@ -3,11 +3,14 @@ module HooksExecution ...@@ -3,11 +3,14 @@ module HooksExecution
private private
def set_hook_execution_notice(status, message) def set_hook_execution_notice(result)
if status && status >= 200 && status < 400 http_status = result[:http_status]
flash[:notice] = "Hook executed successfully: HTTP #{status}" message = result[:message]
elsif status
flash[:alert] = "Hook executed successfully but returned HTTP #{status} #{message}" if http_status && http_status >= 200 && http_status < 400
flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
elsif http_status
flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
else else
flash[:alert] = "Hook execution failed: #{message}" flash[:alert] = "Hook execution failed: #{message}"
end end
......
...@@ -32,10 +32,10 @@ module IssuableCollections ...@@ -32,10 +32,10 @@ module IssuableCollections
def filter_params def filter_params
set_sort_order_from_cookie set_sort_order_from_cookie
set_default_scope
set_default_state set_default_state
@filter_params = params.dup # Skip irrelevant Rails routing params
@filter_params = params.dup.except(:controller, :action, :namespace_id)
@filter_params[:sort] ||= default_sort_order @filter_params[:sort] ||= default_sort_order
@sort = @filter_params[:sort] @sort = @filter_params[:sort]
...@@ -55,10 +55,6 @@ module IssuableCollections ...@@ -55,10 +55,6 @@ module IssuableCollections
@filter_params @filter_params
end end
def set_default_scope
params[:scope] = 'all' if params[:scope].blank?
end
def set_default_state def set_default_state
params[:state] = 'opened' if params[:state].blank? params[:state] = 'opened' if params[:state].blank?
end end
......
class Dashboard::TodosController < Dashboard::ApplicationController class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
before_action :authorize_read_project!, only: :index
before_action :find_todos, only: [:index, :destroy_all] before_action :find_todos, only: [:index, :destroy_all]
def index def index
...@@ -49,6 +50,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -49,6 +50,15 @@ class Dashboard::TodosController < Dashboard::ApplicationController
private private
def authorize_read_project!
project_id = params[:project_id]
if project_id.present?
project = Project.find(project_id)
render_404 unless can?(current_user, :read_project, project)
end
end
def find_todos def find_todos
@todos ||= TodosFinder.new(current_user, params).execute @todos ||= TodosFinder.new(current_user, params).execute
end end
......
...@@ -3,11 +3,11 @@ class Projects::BadgesController < Projects::ApplicationController ...@@ -3,11 +3,11 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:index] before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index] before_action :no_cache_headers, except: [:index]
def build def pipeline
build_status = Gitlab::Badge::Build::Status pipeline_status = Gitlab::Badge::Pipeline::Status
.new(project, params[:ref]) .new(project, params[:ref])
render_badge build_status render_badge pipeline_status
end end
def coverage def coverage
......
...@@ -14,9 +14,9 @@ class Projects::HookLogsController < Projects::ApplicationController ...@@ -14,9 +14,9 @@ class Projects::HookLogsController < Projects::ApplicationController
end end
def retry def retry
status, message = hook.execute(hook_log.request_data, hook_log.trigger) result = hook.execute(hook_log.request_data, hook_log.trigger)
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
redirect_to edit_project_hook_path(@project, @hook) redirect_to edit_project_hook_path(@project, @hook)
end end
......
...@@ -9,6 +9,10 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -9,6 +9,10 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings" layout "project_settings"
def index
redirect_to project_settings_integrations_path(@project)
end
def create def create
@hook = @project.hooks.new(hook_params) @hook = @project.hooks.new(hook_params)
@hook.save @hook.save
...@@ -33,13 +37,9 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -33,13 +37,9 @@ class Projects::HooksController < Projects::ApplicationController
end end
def test def test
if !@project.empty_repo? result = TestHooks::ProjectService.new(hook, current_user, params[:trigger]).execute
status, message = TestHookService.new.execute(hook, current_user)
set_hook_execution_notice(status, message) set_hook_execution_notice(result)
else
flash[:alert] = 'Hook execution failed. Ensure the project has commits.'
end
redirect_back_or_default(default: { action: 'index' }) redirect_back_or_default(default: { action: 'index' })
end end
......
...@@ -266,7 +266,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -266,7 +266,7 @@ class Projects::IssuesController < Projects::ApplicationController
if action_name == 'new' if action_name == 'new'
redirect_to external.new_issue_path redirect_to external.new_issue_path
else else
redirect_to external.project_path redirect_to external.issue_tracker_path
end end
end end
......
...@@ -35,7 +35,7 @@ module Projects ...@@ -35,7 +35,7 @@ module Projects
def define_badges_variables def define_badges_variables
@ref = params[:ref] || @project.default_branch || 'master' @ref = params[:ref] || @project.default_branch || 'master'
@badges = [Gitlab::Badge::Build::Status, @badges = [Gitlab::Badge::Pipeline::Status,
Gitlab::Badge::Coverage::Report] Gitlab::Badge::Coverage::Report]
@badges.map! do |badge| @badges.map! do |badge|
......
...@@ -22,7 +22,7 @@ class IssuableFinder ...@@ -22,7 +22,7 @@ class IssuableFinder
include CreatedAtFilter include CreatedAtFilter
NONE = '0'.freeze NONE = '0'.freeze
IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page].freeze IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page state].freeze
attr_accessor :current_user, :params attr_accessor :current_user, :params
...@@ -89,8 +89,14 @@ class IssuableFinder ...@@ -89,8 +89,14 @@ class IssuableFinder
execute.find_by!(*params) execute.find_by!(*params)
end end
def state_counter_cache_key(state) def state_counter_cache_key
Digest::SHA1.hexdigest(state_counter_cache_key_components(state).flatten.join('-')) cache_key(state_counter_cache_key_components)
end
def clear_caches!
state_counter_cache_key_components_permutations.each do |components|
Rails.cache.delete(cache_key(components))
end
end end
def group def group
...@@ -417,12 +423,19 @@ class IssuableFinder ...@@ -417,12 +423,19 @@ class IssuableFinder
params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
end end
def state_counter_cache_key_components(state) def state_counter_cache_key_components
opts = params.with_indifferent_access opts = params.with_indifferent_access
opts[:state] = state
opts.except!(*IRRELEVANT_PARAMS_FOR_CACHE_KEY) opts.except!(*IRRELEVANT_PARAMS_FOR_CACHE_KEY)
opts.delete_if { |_, value| value.blank? } opts.delete_if { |_, value| value.blank? }
['issuables_count', klass.to_ability_name, opts.sort] ['issuables_count', klass.to_ability_name, opts.sort]
end end
def state_counter_cache_key_components_permutations
[state_counter_cache_key_components]
end
def cache_key(components)
Digest::SHA1.hexdigest(components.flatten.join('-'))
end
end end
...@@ -75,7 +75,7 @@ class IssuesFinder < IssuableFinder ...@@ -75,7 +75,7 @@ class IssuesFinder < IssuableFinder
current_user.blank? || for_counting || params[:for_counting] current_user.blank? || for_counting || params[:for_counting]
end end
def state_counter_cache_key_components(state) def state_counter_cache_key_components
extra_components = [ extra_components = [
user_can_see_all_confidential_issues?, user_can_see_all_confidential_issues?,
user_cannot_see_confidential_issues?(for_counting: true) user_cannot_see_confidential_issues?(for_counting: true)
...@@ -84,6 +84,16 @@ class IssuesFinder < IssuableFinder ...@@ -84,6 +84,16 @@ class IssuesFinder < IssuableFinder
super + extra_components super + extra_components
end end
def state_counter_cache_key_components_permutations
# Ignore the last two, as we'll provide both options for them.
components = super.first[0..-3]
[
components + [false, true],
components + [true, false]
]
end
def by_assignee(items) def by_assignee(items)
if assignee if assignee
items.assigned_to(assignee) items.assigned_to(assignee)
......
module BreadcrumbsHelper
def add_to_breadcrumbs(text, link)
@breadcrumbs_extra_links ||= []
@breadcrumbs_extra_links.push({
text: text,
link: link
})
end
def breadcrumb_title_link
return @breadcrumb_link if @breadcrumb_link
if controller.available_action?(:index)
url_for(action: "index")
else
request.path
end
end
def breadcrumb_title(title)
return if defined?(@breadcrumb_title)
@breadcrumb_title = title
end
end
module HooksHelper
def link_to_test_hook(hook, trigger)
path = case hook
when ProjectHook
project = hook.project
test_project_hook_path(project, hook, trigger: trigger)
when SystemHook
test_admin_hook_path(hook, trigger: trigger)
end
trigger_human_name = trigger.to_s.tr('_', ' ').camelize
link_to path, rel: 'nofollow' do
content_tag(:span, trigger_human_name)
end
end
end
...@@ -235,7 +235,7 @@ module IssuablesHelper ...@@ -235,7 +235,7 @@ module IssuablesHelper
def issuables_count_for_state(issuable_type, state, finder: nil) def issuables_count_for_state(issuable_type, state, finder: nil)
finder ||= public_send("#{issuable_type}_finder") finder ||= public_send("#{issuable_type}_finder")
cache_key = finder.state_counter_cache_key(state) cache_key = finder.state_counter_cache_key
@counts ||= {} @counts ||= {}
@counts[cache_key] ||= Rails.cache.fetch(cache_key, expires_in: 2.minutes) do @counts[cache_key] ||= Rails.cache.fetch(cache_key, expires_in: 2.minutes) do
......
...@@ -4,6 +4,10 @@ module PageLayoutHelper ...@@ -4,6 +4,10 @@ module PageLayoutHelper
@page_title.push(*titles.compact) if titles.any? @page_title.push(*titles.compact) if titles.any?
if show_new_nav? && titles.any? && !defined?(@breadcrumb_title)
@breadcrumb_title = @page_title.last
end
# Segments are seperated by middot # Segments are seperated by middot
@page_title.join(" \u00b7 ") @page_title.join(" \u00b7 ")
end end
......
...@@ -195,7 +195,7 @@ module ProjectsHelper ...@@ -195,7 +195,7 @@ module ProjectsHelper
controller.controller_name, controller.controller_name,
controller.action_name, controller.action_name,
current_application_settings.cache_key, current_application_settings.cache_key,
'v2.4' 'v2.5'
] ]
key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status? key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
......
...@@ -8,6 +8,6 @@ module TriggersHelper ...@@ -8,6 +8,6 @@ module TriggersHelper
end end
def service_trigger_url(service) def service_trigger_url(service)
"#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger" "#{Settings.gitlab.url}/api/v4/projects/#{service.project_id}/services/#{service.to_param}/trigger"
end end
end end
...@@ -96,6 +96,14 @@ module Ci ...@@ -96,6 +96,14 @@ module Ci
BuildSuccessWorker.perform_async(id) BuildSuccessWorker.perform_async(id)
end end
end end
before_transition any => [:failed] do |build|
next if build.retries_max.zero?
if build.retries_count < build.retries_max
Ci::Build.retry(build, build.user)
end
end
end end
def detailed_status(current_user) def detailed_status(current_user)
...@@ -130,6 +138,14 @@ module Ci ...@@ -130,6 +138,14 @@ module Ci
success? || failed? || canceled? success? || failed? || canceled?
end end
def retries_count
pipeline.builds.retried.where(name: self.name).count
end
def retries_max
self.options.fetch(:retry, 0).to_i
end
def latest? def latest?
!retried? !retried?
end end
......
...@@ -4,4 +4,8 @@ module Editable ...@@ -4,4 +4,8 @@ module Editable
def is_edited? def is_edited?
last_edited_at.present? && last_edited_at != created_at last_edited_at.present? && last_edited_at != created_at
end end
def last_edited_by
super || User.ghost
end
end end
class ProjectHook < WebHook class ProjectHook < WebHook
belongs_to :project TRIGGERS = {
push_hooks: :push_events,
tag_push_hooks: :tag_push_events,
issue_hooks: :issues_events,
confidential_issue_hooks: :confidential_issues_events,
note_hooks: :note_events,
merge_request_hooks: :merge_requests_events,
job_hooks: :job_events,
pipeline_hooks: :pipeline_events,
wiki_page_hooks: :wiki_page_events
}.freeze
TRIGGERS.each do |trigger, event|
scope trigger, -> { where(event => true) }
end
scope :issue_hooks, -> { where(issues_events: true) } belongs_to :project
scope :confidential_issue_hooks, -> { where(confidential_issues_events: true) } validates :project, presence: true
scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :job_hooks, -> { where(job_events: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
end end
class ServiceHook < WebHook class ServiceHook < WebHook
belongs_to :service belongs_to :service
validates :service, presence: true
def execute(data) def execute(data)
WebHookService.new(self, data, 'service_hook').execute WebHookService.new(self, data, 'service_hook').execute
......
class SystemHook < WebHook class SystemHook < WebHook
scope :repository_update_hooks, -> { where(repository_update_events: true) } TRIGGERS = {
repository_update_hooks: :repository_update_events,
push_hooks: :push_events,
tag_push_hooks: :tag_push_events
}.freeze
TRIGGERS.each do |trigger, event|
scope trigger, -> { where(event => true) }
end
default_value_for :push_events, false default_value_for :push_events, false
default_value_for :repository_update_events, true default_value_for :repository_update_events, true
......
class WebHook < ActiveRecord::Base class WebHook < ActiveRecord::Base
include Sortable include Sortable
default_value_for :push_events, true
default_value_for :issues_events, false
default_value_for :confidential_issues_events, false
default_value_for :note_events, false
default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false
default_value_for :job_events, false
default_value_for :pipeline_events, false
default_value_for :repository_update_events, false
default_value_for :enable_ssl_verification, true
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
validates :url, presence: true, url: true validates :url, presence: true, url: true
def execute(data, hook_name) def execute(data, hook_name)
......
...@@ -23,7 +23,7 @@ class GitlabIssueTrackerService < IssueTrackerService ...@@ -23,7 +23,7 @@ class GitlabIssueTrackerService < IssueTrackerService
project_issue_url(project, id: iid) project_issue_url(project, id: iid)
end end
def project_path def issue_tracker_path
project_issues_path(project) project_issues_path(project)
end end
......
...@@ -20,8 +20,8 @@ class IssueTrackerService < Service ...@@ -20,8 +20,8 @@ class IssueTrackerService < Service
self.issues_url.gsub(':id', iid.to_s) self.issues_url.gsub(':id', iid.to_s)
end end
def project_path def issue_tracker_path
read_attribute(:project_url) project_url
end end
def new_issue_path def new_issue_path
......
...@@ -58,22 +58,22 @@ class KubernetesService < DeploymentService ...@@ -58,22 +58,22 @@ class KubernetesService < DeploymentService
def fields def fields
[ [
{ type: 'text',
name: 'namespace',
title: 'Kubernetes namespace',
placeholder: namespace_placeholder },
{ type: 'text', { type: 'text',
name: 'api_url', name: 'api_url',
title: 'API URL', title: 'API URL',
placeholder: 'Kubernetes API URL, like https://kube.example.com/' }, placeholder: 'Kubernetes API URL, like https://kube.example.com/' },
{ type: 'text',
name: 'token',
title: 'Service token',
placeholder: 'Service token' },
{ type: 'textarea', { type: 'textarea',
name: 'ca_pem', name: 'ca_pem',
title: 'Custom CA bundle', title: 'CA Certificate',
placeholder: 'Certificate Authority bundle (PEM format)' } placeholder: 'Certificate Authority bundle (PEM format)' },
{ type: 'text',
name: 'namespace',
title: 'Project namespace (optional/unique)',
placeholder: namespace_placeholder },
{ type: 'text',
name: 'token',
title: 'Token',
placeholder: 'Service token' }
] ]
end end
......
...@@ -385,9 +385,11 @@ class User < ActiveRecord::Base ...@@ -385,9 +385,11 @@ class User < ActiveRecord::Base
# Return (create if necessary) the ghost user. The ghost user # Return (create if necessary) the ghost user. The ghost user
# owns records previously belonging to deleted users. # owns records previously belonging to deleted users.
def ghost def ghost
unique_internal(where(ghost: true), 'ghost', 'ghost%s@example.com') do |u| email = 'ghost%s@example.com'
unique_internal(where(ghost: true), 'ghost', email) do |u|
u.bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.' u.bio = 'This is a "Ghost User", created to hold all issues authored by users that have since been deleted. This user cannot be removed.'
u.name = 'Ghost User' u.name = 'Ghost User'
u.notification_email = email
end end
end end
end end
......
...@@ -33,17 +33,12 @@ module Boards ...@@ -33,17 +33,12 @@ module Boards
end end
def filter_params def filter_params
set_default_scope
set_project set_project
set_state set_state
params params
end end
def set_default_scope
params[:scope] = 'all'
end
def set_project def set_project
params[:project_id] = project.id params[:project_id] = project.id
end end
......
...@@ -135,7 +135,7 @@ module Ci ...@@ -135,7 +135,7 @@ module Ci
end end
def pipeline_created_counter def pipeline_created_counter
@pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_count, "Pipelines created count") @pipeline_created_counter ||= Gitlab::Metrics.counter(:pipelines_created_total, "Counter of pipelines created")
end end
end end
end end
...@@ -183,7 +183,7 @@ class IssuableBaseService < BaseService ...@@ -183,7 +183,7 @@ class IssuableBaseService < BaseService
after_create(issuable) after_create(issuable)
issuable.create_cross_references!(current_user) issuable.create_cross_references!(current_user)
execute_hooks(issuable) execute_hooks(issuable)
invalidate_cache_counts(issuable.assignees, issuable) invalidate_cache_counts(issuable, users: issuable.assignees)
end end
issuable issuable
...@@ -240,12 +240,12 @@ class IssuableBaseService < BaseService ...@@ -240,12 +240,12 @@ class IssuableBaseService < BaseService
old_assignees: old_assignees old_assignees: old_assignees
) )
if old_assignees != issuable.assignees
new_assignees = issuable.assignees.to_a new_assignees = issuable.assignees.to_a
affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees) affected_assignees = (old_assignees + new_assignees) - (old_assignees & new_assignees)
invalidate_cache_counts(affected_assignees.compact, issuable)
end
# Don't clear the project cache, because it will be handled by the
# appropriate service (close / reopen / merge / etc.).
invalidate_cache_counts(issuable, users: affected_assignees.compact, skip_project_cache: true)
after_update(issuable) after_update(issuable)
issuable.create_new_cross_references!(current_user) issuable.create_new_cross_references!(current_user)
execute_hooks(issuable, 'update') execute_hooks(issuable, 'update')
...@@ -339,9 +339,18 @@ class IssuableBaseService < BaseService ...@@ -339,9 +339,18 @@ class IssuableBaseService < BaseService
create_labels_note(issuable, old_labels) if issuable.labels != old_labels create_labels_note(issuable, old_labels) if issuable.labels != old_labels
end end
def invalidate_cache_counts(users, issuable) def invalidate_cache_counts(issuable, users: [], skip_project_cache: false)
users.each do |user| users.each do |user|
user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts") user.public_send("invalidate_#{issuable.model_name.singular}_cache_counts")
end end
unless skip_project_cache
case issuable
when Issue
IssuesFinder.new(nil, project_id: issuable.project_id).clear_caches!
when MergeRequest
MergeRequestsFinder.new(nil, project_id: issuable.target_project_id).clear_caches!
end
end
end end
end end
...@@ -28,7 +28,7 @@ module Issues ...@@ -28,7 +28,7 @@ module Issues
notification_service.close_issue(issue, current_user) if notifications notification_service.close_issue(issue, current_user) if notifications
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
execute_hooks(issue, 'close') execute_hooks(issue, 'close')
invalidate_cache_counts(issue.assignees, issue) invalidate_cache_counts(issue, users: issue.assignees)
end end
issue issue
......
...@@ -8,7 +8,7 @@ module Issues ...@@ -8,7 +8,7 @@ module Issues
create_note(issue) create_note(issue)
notification_service.reopen_issue(issue, current_user) notification_service.reopen_issue(issue, current_user)
execute_hooks(issue, 'reopen') execute_hooks(issue, 'reopen')
invalidate_cache_counts(issue.assignees, issue) invalidate_cache_counts(issue, users: issue.assignees)
end end
issue issue
......
...@@ -13,7 +13,7 @@ module MergeRequests ...@@ -13,7 +13,7 @@ module MergeRequests
notification_service.close_mr(merge_request, current_user) notification_service.close_mr(merge_request, current_user)
todo_service.close_merge_request(merge_request, current_user) todo_service.close_merge_request(merge_request, current_user)
execute_hooks(merge_request, 'close') execute_hooks(merge_request, 'close')
invalidate_cache_counts(merge_request.assignees, merge_request) invalidate_cache_counts(merge_request, users: merge_request.assignees)
end end
merge_request merge_request
......
...@@ -13,7 +13,7 @@ module MergeRequests ...@@ -13,7 +13,7 @@ module MergeRequests
create_note(merge_request) create_note(merge_request)
notification_service.merge_mr(merge_request, current_user) notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge') execute_hooks(merge_request, 'merge')
invalidate_cache_counts(merge_request.assignees, merge_request) invalidate_cache_counts(merge_request, users: merge_request.assignees)
end end
private private
......
...@@ -10,7 +10,7 @@ module MergeRequests ...@@ -10,7 +10,7 @@ module MergeRequests
execute_hooks(merge_request, 'reopen') execute_hooks(merge_request, 'reopen')
merge_request.reload_diff(current_user) merge_request.reload_diff(current_user)
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
invalidate_cache_counts(merge_request.assignees, merge_request) invalidate_cache_counts(merge_request, users: merge_request.assignees)
end end
merge_request merge_request
......
...@@ -31,6 +31,6 @@ class MetricsService ...@@ -31,6 +31,6 @@ class MetricsService
end end
def multiprocess_metrics_path def multiprocess_metrics_path
@multiprocess_metrics_path ||= Rails.root.join(ENV['prometheus_multiproc_dir']).freeze ::Prometheus::Client.configuration.multiprocess_files_dir
end end
end end
...@@ -4,7 +4,7 @@ class SystemHooksService ...@@ -4,7 +4,7 @@ class SystemHooksService
end end
def execute_hooks(data, hooks_scope = :all) def execute_hooks(data, hooks_scope = :all)
SystemHook.send(hooks_scope).each do |hook| SystemHook.public_send(hooks_scope).find_each do |hook|
hook.async_execute(data, 'system_hooks') hook.async_execute(data, 'system_hooks')
end end
end end
......
class TestHookService
def execute(hook, current_user)
data = Gitlab::DataBuilder::Push.build_sample(hook.project, current_user)
hook.execute(data, 'push_hooks')
end
end
module TestHooks
class BaseService
attr_accessor :hook, :current_user, :trigger
def initialize(hook, current_user, trigger)
@hook = hook
@current_user = current_user
@trigger = trigger
end
def execute
trigger_data_method = "#{trigger}_data"
if !self.respond_to?(trigger_data_method, true) ||
!hook.class::TRIGGERS.value?(trigger.to_sym)
return error('Testing not available for this hook')
end
error_message = catch(:validation_error) do
sample_data = self.__send__(trigger_data_method)
return hook.execute(sample_data, trigger)
end
error(error_message)
end
private
def error(message, http_status = nil)
result = {
message: message,
status: :error
}
result[:http_status] = http_status if http_status
result
end
end
end
module TestHooks
class ProjectService < TestHooks::BaseService
private
def project
@project ||= hook.project
end
def push_events_data
throw(:validation_error, 'Ensure the project has at least one commit.') if project.empty_repo?
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
alias_method :tag_push_events_data, :push_events_data
def note_events_data
note = project.notes.first
throw(:validation_error, 'Ensure the project has notes.') unless note.present?
Gitlab::DataBuilder::Note.build(note, current_user)
end
def issues_events_data
issue = project.issues.first
throw(:validation_error, 'Ensure the project has issues.') unless issue.present?
issue.to_hook_data(current_user)
end
alias_method :confidential_issues_events_data, :issues_events_data
def merge_requests_events_data
merge_request = project.merge_requests.first
throw(:validation_error, 'Ensure the project has merge requests.') unless merge_request.present?
merge_request.to_hook_data(current_user)
end
def job_events_data
build = project.builds.first
throw(:validation_error, 'Ensure the project has CI jobs.') unless build.present?
Gitlab::DataBuilder::Build.build(build)
end
def pipeline_events_data
pipeline = project.pipelines.first
throw(:validation_error, 'Ensure the project has CI pipelines.') unless pipeline.present?
Gitlab::DataBuilder::Pipeline.build(pipeline)
end
def wiki_page_events_data
page = project.wiki.pages.first
if !project.wiki_enabled? || page.blank?
throw(:validation_error, 'Ensure the wiki is enabled and has pages.')
end
Gitlab::DataBuilder::WikiPage.build(page, current_user, 'create')
end
end
end
module TestHooks
class SystemService < TestHooks::BaseService
private
def project
@project ||= begin
project = Project.first
throw(:validation_error, 'Ensure that at least one project exists.') unless project
project
end
end
def push_events_data
if project.empty_repo?
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
def tag_push_events_data
if project.repository.tags.empty?
throw(:validation_error, "Ensure project \"#{project.human_name}\" has tags.")
end
Gitlab::DataBuilder::Push.build_sample(project, current_user)
end
def repository_update_events_data
commit = project.commit
ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
unless commit
throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
end
change = Gitlab::DataBuilder::Repository.single_change(
commit.parent_id || Gitlab::Git::BLANK_SHA,
commit.id,
ref
)
Gitlab::DataBuilder::Repository.update(project, current_user, [change], [ref])
end
end
end
...@@ -50,10 +50,12 @@ module Users ...@@ -50,10 +50,12 @@ module Users
def migrate_issues def migrate_issues
user.issues.update_all(author_id: ghost_user.id) user.issues.update_all(author_id: ghost_user.id)
Issue.where(last_edited_by_id: user.id).update_all(last_edited_by_id: ghost_user.id)
end end
def migrate_merge_requests def migrate_merge_requests
user.merge_requests.update_all(author_id: ghost_user.id) user.merge_requests.update_all(author_id: ghost_user.id)
MergeRequest.where(merge_user_id: user.id).update_all(merge_user_id: ghost_user.id)
end end
def migrate_notes def migrate_notes
......
...@@ -39,7 +39,11 @@ class WebHookService ...@@ -39,7 +39,11 @@ class WebHookService
execution_duration: Time.now - start_time execution_duration: Time.now - start_time
) )
[response.code, response.to_s] {
status: :success,
http_status: response.code,
message: response.to_s
}
rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
log_execution( log_execution(
trigger: hook_name, trigger: hook_name,
...@@ -52,7 +56,10 @@ class WebHookService ...@@ -52,7 +56,10 @@ class WebHookService
Rails.logger.error("WebHook Error => #{e}") Rails.logger.error("WebHook Error => #{e}")
[nil, e.to_s] {
status: :error,
message: e.to_s
}
end end
def async_execute def async_execute
......
module WikiPages module WikiPages
class BaseService < ::BaseService class BaseService < ::BaseService
def hook_data(page, action)
hook_data = {
object_kind: page.class.name.underscore,
user: current_user.hook_attrs,
project: @project.hook_attrs,
wiki: @project.wiki.hook_attrs,
object_attributes: page.hook_attrs
}
page_url = Gitlab::UrlBuilder.build(page)
hook_data[:object_attributes].merge!(url: page_url, action: action)
hook_data
end
private private
def execute_hooks(page, action = 'create') def execute_hooks(page, action = 'create')
page_data = hook_data(page, action) page_data = Gitlab::DataBuilder::WikiPage.build(page, current_user, action)
@project.execute_hooks(page_data, :wiki_page_hooks) @project.execute_hooks(page_data, :wiki_page_hooks)
@project.execute_services(page_data, :wiki_page_hooks) @project.execute_services(page_data, :wiki_page_hooks)
end end
......
...@@ -16,7 +16,7 @@ class GitlabUploader < CarrierWave::Uploader::Base ...@@ -16,7 +16,7 @@ class GitlabUploader < CarrierWave::Uploader::Base
def self.base_dir def self.base_dir
return root_dir unless file_storage? return root_dir unless file_storage?
File.join(root_dir, 'system') File.join(root_dir, '-', 'system')
end end
def self.file_storage? def self.file_storage?
......
...@@ -3,6 +3,10 @@ class PersonalFileUploader < FileUploader ...@@ -3,6 +3,10 @@ class PersonalFileUploader < FileUploader
File.join(CarrierWave.root, model_path(model)) File.join(CarrierWave.root, model_path(model))
end end
def self.base_dir
File.join(root_dir, 'system')
end
private private
def secure_url def secure_url
......
...@@ -315,7 +315,9 @@ ...@@ -315,7 +315,9 @@
%fieldset %fieldset
%legend Metrics - Prometheus %legend Metrics - Prometheus
%p %p
Enable a Prometheus metrics endpoint at `#{metrics_path}` to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available Enable a Prometheus metrics endpoint at
%code= metrics_path
to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available
= link_to 'here', admin_health_check_path = link_to 'here', admin_health_check_path
\. This setting requires a \. This setting requires a
= link_to 'restart', help_page_path('administration/restart_gitlab') = link_to 'restart', help_page_path('administration/restart_gitlab')
...@@ -330,7 +332,10 @@ ...@@ -330,7 +332,10 @@
- unless Gitlab::Metrics.metrics_folder_present? - unless Gitlab::Metrics.metrics_folder_present?
.help-block .help-block
%strong.cred WARNING: %strong.cred WARNING:
Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory. Environment variable
%code prometheus_multiproc_dir
does not exist or is not pointing to a valid directory.
= link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory')
%fieldset %fieldset
%legend Profiling - Performance Bar %legend Profiling - Performance Bar
......
- page_title "Edit", @application.name, "Applications" - page_title "Edit", @application.name, "Applications"
%h3.page-title Edit application %h3.page-title Edit application
- @url = admin_application_path(@application) - @url = admin_application_path(@application)
= render 'form', application: @application = render 'form', application: @application
- breadcrumb_title "Applications"
- page_title "New Application" - page_title "New Application"
%h3.page-title New application %h3.page-title New application
- @url = admin_applications_path - @url = admin_applications_path
= render 'form', application: @application = render 'form', application: @application
- breadcrumb_title "Messages"
- page_title "Broadcast Messages" - page_title "Broadcast Messages"
= render 'form' = render 'form'
- breadcrumb_title "Messages"
- page_title "Broadcast Messages" - page_title "Broadcast Messages"
%h3.page-title %h3.page-title
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= render partial: 'form', locals: { form: f, hook: @hook } = render partial: 'form', locals: { form: f, hook: @hook }
.form-actions .form-actions
= f.submit 'Save changes', class: 'btn btn-create' = f.submit 'Save changes', class: 'btn btn-create'
= link_to 'Test hook', test_admin_hook_path(@hook), class: 'btn btn-default' = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: @hook
= link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' } = link_to 'Remove', admin_hook_path(@hook), method: :delete, class: 'btn btn-remove pull-right', data: { confirm: 'Are you sure?' }
%hr %hr
......
...@@ -22,12 +22,12 @@ ...@@ -22,12 +22,12 @@
- @hooks.each do |hook| - @hooks.each do |hook|
%li %li
.controls .controls
= link_to 'Test hook', test_admin_hook_path(hook), class: 'btn btn-sm' = render 'shared/web_hooks/test_button', triggers: SystemHook::TRIGGERS, hook: hook, button_class: 'btn-small'
= link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm' = link_to 'Edit', edit_admin_hook_path(hook), class: 'btn btn-sm'
= link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm' = link_to 'Remove', admin_hook_path(hook), data: { confirm: 'Are you sure?' }, method: :delete, class: 'btn btn-remove btn-sm'
.monospace= hook.url .monospace= hook.url
%div %div
- %w(repository_update_events push_events tag_push_events issues_events note_events merge_requests_events job_events).each do |trigger| - SystemHook::TRIGGERS.each_value do |event|
- if hook.send(trigger) - if hook.public_send(event)
%span.label.label-gray= trigger.titleize %span.label.label-gray= event.to_s.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'} %span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? 'enabled' : 'disabled'}
...@@ -2,26 +2,6 @@ ...@@ -2,26 +2,6 @@
= render "admin/dashboard/head" = render "admin/dashboard/head"
%div{ class: container_class } %div{ class: container_class }
%p.prepend-top-default
%span
To register a new Runner you should enter the following registration
token.
With this token the Runner will request a unique Runner token and use
that for future communication.
%br
Registration token is
%code#runners-token= current_application_settings.runners_registration_token
.bs-callout.clearfix
.pull-left
%p
You can reset runners registration token by pressing a button below.
.prepend-top-10
= button_to "Reset runners registration token", reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset registration token?' }
.bs-callout .bs-callout
%p %p
A 'Runner' is a process which runs a job. A 'Runner' is a process which runs a job.
...@@ -46,6 +26,19 @@ ...@@ -46,6 +26,19 @@
%span.label.label-danger paused %span.label.label-danger paused
\- Runner will not receive any new jobs \- Runner will not receive any new jobs
.bs-callout.clearfix
.pull-left
%p
You can reset runners registration token by pressing a button below.
.prepend-top-10
= button_to _("Reset runners registration token"), reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: _("Are you sure you want to reset registration token?") }
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: current_application_settings.runners_registration_token,
type: 'shared' }
.append-bottom-20.clearfix .append-bottom-20.clearfix
.pull-left .pull-left
= form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do = form_tag admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do
......
- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'
.bs-callout.help-callout
%h4= _("How to setup a #{type} Runner for a new project")
%ol
%li
= _("Install a Runner compatible with GitLab CI")
= (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
%li
= _("Specify the following URL during the Runner setup:")
%code= root_url(only_path: false)
%li
= _("Use the following registration token during setup:")
%code#registration_token= registration_token
%li
= _("Start the Runner!")
- if show_new_nav? && current_user.can_create_group?
- content_for :breadcrumbs_extra do
= link_to "New group", new_group_path, class: "btn btn-new"
.top-area .top-area
%ul.nav-links %ul.nav-links
= nav_link(page: dashboard_groups_path) do = nav_link(page: dashboard_groups_path) do
...@@ -6,9 +10,8 @@ ...@@ -6,9 +10,8 @@
= nav_link(page: explore_groups_path) do = nav_link(page: explore_groups_path) do
= link_to explore_groups_path, title: 'Explore public groups' do = link_to explore_groups_path, title: 'Explore public groups' do
Explore public groups Explore public groups
.nav-controls .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) }
= render 'shared/groups/search_form' = render 'shared/groups/search_form'
= render 'shared/groups/dropdown' = render 'shared/groups/dropdown'
- if current_user.can_create_group? - if current_user.can_create_group?
= link_to new_group_path, class: "btn btn-new" do = link_to "New group", new_group_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}"
New group
= content_for :flash_message do = content_for :flash_message do
= render 'shared/project_limit' = render 'shared/project_limit'
- if show_new_nav? && current_user.can_create_project?
- content_for :breadcrumbs_extra do
= link_to "New project", new_project_path, class: 'btn btn-new'
.top-area.scrolling-tabs-container.inner-page-scroll-tabs .top-area.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
...@@ -14,9 +19,8 @@ ...@@ -14,9 +19,8 @@
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore projects Explore projects
.nav-controls .nav-controls{ class: ("nav-controls-new-nav" if show_new_nav?) }
= render 'shared/projects/search_form' = render 'shared/projects/search_form'
= render 'shared/projects/dropdown' = render 'shared/projects/dropdown'
- if current_user.can_create_project? - if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do = link_to "New project", new_project_path, class: "btn btn-new #{("visible-xs" if show_new_nav?)}"
New project
- if show_new_nav? && current_user
- content_for :breadcrumbs_extra do
= link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet"
.top-area .top-area
%ul.nav-links %ul.nav-links
= nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do = nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do
...@@ -8,6 +12,5 @@ ...@@ -8,6 +12,5 @@
Explore Snippets Explore Snippets
- if current_user - if current_user
.nav-controls.hidden-xs .nav-controls.hidden-xs{ class: ("hidden-sm hidden-md hidden-lg" if show_new_nav?) }
= link_to new_snippet_path, class: "btn btn-new", title: "New snippet" do = link_to "New snippet", new_snippet_path, class: "btn btn-new", title: "New snippet"
New snippet
- @hide_top_links = true
- page_title "Issues" - page_title "Issues"
- header_title "Issues", issues_dashboard_path(assignee_id: current_user.id) - header_title "Issues", issues_dashboard_path(assignee_id: current_user.id)
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues") = auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{current_user.name} issues")
- if show_new_nav?
- content_for :breadcrumbs_extra do
= link_to params.merge(rss_url_options), class: 'btn has-tooltip append-right-10', title: 'Subscribe' do
= icon('rss')
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues'
.top-area .top-area
= render 'shared/issuable/nav', type: :issues = render 'shared/issuable/nav', type: :issues
.nav-controls .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= link_to params.merge(rss_url_options), class: 'btn has-tooltip', title: 'Subscribe' do = link_to params.merge(rss_url_options), class: 'btn has-tooltip', title: 'Subscribe' do
= icon('rss') = icon('rss')
= render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues' = render 'shared/new_project_item_select', path: 'issues/new', label: "New issue", with_feature_enabled: 'issues'
......
- @hide_top_links = true
- page_title "Merge Requests" - page_title "Merge Requests"
- header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id) - header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id)
- if show_new_nav?
- content_for :breadcrumbs_extra do
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests'
.top-area .top-area
= render 'shared/issuable/nav', type: :merge_requests = render 'shared/issuable/nav', type: :merge_requests
.nav-controls .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests' = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", with_feature_enabled: 'merge_requests'
= render 'shared/issuable/filter', type: :merge_requests = render 'shared/issuable/filter', type: :merge_requests
......
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
- page_title 'Milestones' - page_title 'Milestones'
- header_title 'Milestones', dashboard_milestones_path - header_title 'Milestones', dashboard_milestones_path
- if show_new_nav?
- content_for :breadcrumbs_extra do
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
.top-area .top-area
= render 'shared/milestones_filter', counts: @milestone_states = render 'shared/milestones_filter', counts: @milestone_states
.nav-controls .nav-controls{ class: ("visible-xs" if show_new_nav?) }
= render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true = render 'shared/new_project_item_select', path: 'milestones/new', label: 'New milestone', include_groups: true
.milestones .milestones
......
- @no_container = true - @no_container = true
- @hide_top_links = true - @hide_top_links = true
- @breadcrumb_title = "Projects"
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
......
- @hide_top_links = true
- @no_container = true - @no_container = true
- breadcrumb_title "Projects"
- page_title "Starred Projects" - page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
......
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.
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.
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