Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into milestone-tooltip

This commit is contained in:
Fatih Acet 2016-06-08 18:48:20 +03:00
commit 329f6cb9b4
244 changed files with 5849 additions and 2429 deletions

View file

@ -2,7 +2,7 @@ image: "ruby:2.1"
services: services:
- mysql:latest - mysql:latest
- redis:latest - redis:alpine
cache: cache:
key: "ruby21" key: "ruby21"
@ -13,100 +13,187 @@ variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1" MYSQL_ALLOW_EMPTY_PASSWORD: "1"
# retry tests only in CI environment # retry tests only in CI environment
RSPEC_RETRY_RETRY_COUNT: "3" RSPEC_RETRY_RETRY_COUNT: "3"
RAILS_ENV: "test"
SIMPLECOV: "true"
USE_DB: "true"
USE_BUNDLE_INSTALL: "true"
before_script: before_script:
- source ./scripts/prepare_build.sh - source ./scripts/prepare_build.sh
- ruby -v
- which ruby
- retry gem install bundler --no-ri --no-rdoc
- cp config/gitlab.yml.example config/gitlab.yml - cp config/gitlab.yml.example config/gitlab.yml
- touch log/application.log - bundle --version
- touch log/test.log - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"'
- retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" - retry gem install knapsack
- RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate - '[ "$USE_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate'
stages: stages:
- prepare
- test - test
- notifications - post-test
spec:feature: # Prepare and merge knapsack tests
.knapsack-state: &knapsack-state
services: []
variables:
USE_DB: "false"
USE_BUNDLE_INSTALL: "false"
cache:
key: "knapsack"
paths:
- knapsack/
artifacts:
paths:
- knapsack/
knapsack:
<<: *knapsack-state
stage: prepare
script:
- mkdir -p knapsack/
- '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json'
- '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json'
update-knapsack:
<<: *knapsack-state
stage: post-test
script:
- scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json
# Execute all testing suites
.rspec-knapsack: &rspec-knapsack
stage: test stage: test
script: script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null - bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature - JOB_NAME=( $CI_BUILD_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/rspec_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/rspec_report.json ${KNAPSACK_REPORT_PATH}
- knapsack rspec
artifacts:
paths:
- knapsack/
spec:api: .spinach-knapsack: &spinach-knapsack
stage: test stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api - bundle exec rake assets:precompile 2>/dev/null
- JOB_NAME=( $CI_BUILD_NAME )
- export CI_NODE_INDEX=${JOB_NAME[1]}
- export CI_NODE_TOTAL=${JOB_NAME[2]}
- export KNAPSACK_REPORT_PATH=knapsack/spinach_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- export KNAPSACK_GENERATE_REPORT=true
- cp knapsack/spinach_report.json ${KNAPSACK_REPORT_PATH}
- knapsack spinach "-r rerun"
# retry failed tests 3 times
- retry '[ ! -e tmp/spinach-rerun.txt ] || bin/spinach -r rerun $(cat tmp/spinach-rerun.txt)'
artifacts:
paths:
- knapsack/
spec:models: rspec 0 20: *rspec-knapsack
rspec 1 20: *rspec-knapsack
rspec 2 20: *rspec-knapsack
rspec 3 20: *rspec-knapsack
rspec 4 20: *rspec-knapsack
rspec 5 20: *rspec-knapsack
rspec 6 20: *rspec-knapsack
rspec 7 20: *rspec-knapsack
rspec 8 20: *rspec-knapsack
rspec 9 20: *rspec-knapsack
rspec 10 20: *rspec-knapsack
rspec 11 20: *rspec-knapsack
rspec 12 20: *rspec-knapsack
rspec 13 20: *rspec-knapsack
rspec 14 20: *rspec-knapsack
rspec 15 20: *rspec-knapsack
rspec 16 20: *rspec-knapsack
rspec 17 20: *rspec-knapsack
rspec 18 20: *rspec-knapsack
rspec 19 20: *rspec-knapsack
spinach 0 10: *spinach-knapsack
spinach 1 10: *spinach-knapsack
spinach 2 10: *spinach-knapsack
spinach 3 10: *spinach-knapsack
spinach 4 10: *spinach-knapsack
spinach 5 10: *spinach-knapsack
spinach 6 10: *spinach-knapsack
spinach 7 10: *spinach-knapsack
spinach 8 10: *spinach-knapsack
spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.2
.ruby-22: &ruby-22
image: "ruby:2.2"
only:
- master
cache:
key: "ruby22"
paths:
- vendor
.rspec-knapsack-ruby22: &rspec-knapsack-ruby22
<<: *rspec-knapsack
<<: *ruby-22
.spinach-knapsack-ruby22: &spinach-knapsack-ruby22
<<: *spinach-knapsack
<<: *ruby-22
rspec 0 20 ruby22: *rspec-knapsack-ruby22
rspec 1 20 ruby22: *rspec-knapsack-ruby22
rspec 2 20 ruby22: *rspec-knapsack-ruby22
rspec 3 20 ruby22: *rspec-knapsack-ruby22
rspec 4 20 ruby22: *rspec-knapsack-ruby22
rspec 5 20 ruby22: *rspec-knapsack-ruby22
rspec 6 20 ruby22: *rspec-knapsack-ruby22
rspec 7 20 ruby22: *rspec-knapsack-ruby22
rspec 8 20 ruby22: *rspec-knapsack-ruby22
rspec 9 20 ruby22: *rspec-knapsack-ruby22
rspec 10 20 ruby22: *rspec-knapsack-ruby22
rspec 11 20 ruby22: *rspec-knapsack-ruby22
rspec 12 20 ruby22: *rspec-knapsack-ruby22
rspec 13 20 ruby22: *rspec-knapsack-ruby22
rspec 14 20 ruby22: *rspec-knapsack-ruby22
rspec 15 20 ruby22: *rspec-knapsack-ruby22
rspec 16 20 ruby22: *rspec-knapsack-ruby22
rspec 17 20 ruby22: *rspec-knapsack-ruby22
rspec 18 20 ruby22: *rspec-knapsack-ruby22
rspec 19 20 ruby22: *rspec-knapsack-ruby22
spinach 0 10 ruby22: *spinach-knapsack-ruby22
spinach 1 10 ruby22: *spinach-knapsack-ruby22
spinach 2 10 ruby22: *spinach-knapsack-ruby22
spinach 3 10 ruby22: *spinach-knapsack-ruby22
spinach 4 10 ruby22: *spinach-knapsack-ruby22
spinach 5 10 ruby22: *spinach-knapsack-ruby22
spinach 6 10 ruby22: *spinach-knapsack-ruby22
spinach 7 10 ruby22: *spinach-knapsack-ruby22
spinach 8 10 ruby22: *spinach-knapsack-ruby22
spinach 9 10 ruby22: *spinach-knapsack-ruby22
# Other generic tests
.exec: &exec
stage: test stage: test
script: script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models - bundle exec $CI_BUILD_NAME
spec:lib: teaspoon: *exec
stage: test rubocop: *exec
script: rake scss_lint: *exec
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib rake brakeman: *exec
rake flog: *exec
spec:services: rake flay: *exec
stage: test rake db:migrate:reset: *exec
script: license_finder: *exec
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
spec:other:
stage: test
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
spinach:project:half:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
spinach:project:rest:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
spinach:other:
stage: test
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
teaspoon:
stage: test
script:
- RAILS_ENV=test bundle exec teaspoon
rubocop:
stage: test
script:
- bundle exec rubocop
scss-lint:
stage: test
script:
- bundle exec rake scss_lint
brakeman:
stage: test
script:
- bundle exec rake brakeman
flog:
stage: test
script:
- bundle exec rake flog
flay:
stage: test
script:
- bundle exec rake flay
bundler:audit: bundler:audit:
stage: test stage: test
@ -115,127 +202,10 @@ bundler:audit:
script: script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941" - "bundle exec bundle-audit check --update --ignore OSVDB-115941"
db-migrate-reset: # Notify slack in the end
stage: test
script:
- RAILS_ENV=test bundle exec rake db:migrate:reset
# Ruby 2.2 jobs
spec:feature:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
cache:
key: "ruby22"
paths:
- vendor
spec:api:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
cache:
key: "ruby22"
paths:
- vendor
spec:models:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
cache:
key: "ruby22"
paths:
- vendor
spec:lib:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
cache:
key: "ruby22"
paths:
- vendor
spec:services:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
cache:
key: "ruby22"
paths:
- vendor
spec:other:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
cache:
key: "ruby22"
paths:
- vendor
spinach:project:half:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
cache:
key: "ruby22"
paths:
- vendor
spinach:project:rest:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
cache:
key: "ruby22"
paths:
- vendor
spinach:other:ruby22:
stage: test
image: ruby:2.2
only:
- master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
cache:
key: "ruby22"
paths:
- vendor
notify:slack: notify:slack:
stage: notifications stage: post-test
script: script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>" - ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure when: on_failure

View file

@ -2,10 +2,14 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.9.0 (unreleased) v 8.9.0 (unreleased)
- Bulk assign/unassign labels to issues. - Bulk assign/unassign labels to issues.
- Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
- Fix endless redirections when accessing user OAuth applications when they are disabled
- Allow enabling wiki page events from Webhook management UI - Allow enabling wiki page events from Webhook management UI
- Bump rouge to 1.11.0
- Make EmailsOnPushWorker use Sidekiq mailers queue - Make EmailsOnPushWorker use Sidekiq mailers queue
- Fix wiki page events' webhook to point to the wiki repository - Fix wiki page events' webhook to point to the wiki repository
- Fix issue todo not remove when leave project !4150 (Long Nguyen) - Fix issue todo not remove when leave project !4150 (Long Nguyen)
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
- Allow forking projects with restricted visibility level - Allow forking projects with restricted visibility level
- Improve note validation to prevent errors when creating invalid note via API - Improve note validation to prevent errors when creating invalid note via API
- Reduce number of fog gem dependencies - Reduce number of fog gem dependencies
@ -14,7 +18,9 @@ v 8.9.0 (unreleased)
- Redesign navigation for project pages - Redesign navigation for project pages
- Fix groups API to list only user's accessible projects - Fix groups API to list only user's accessible projects
- Redesign account and email confirmation emails - Redesign account and email confirmation emails
- Bump nokogiri to 1.6.8
- Use gitlab-shell v3.0.0 - Use gitlab-shell v3.0.0
- Use Knapsack to evenly distribute tests across multiple nodes
- Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged - Add `sha` parameter to MR merge API, to ensure only reviewed changes are merged
- Don't allow MRs to be merged when commits were added since the last review / page load - Don't allow MRs to be merged when commits were added since the last review / page load
- Add DB index on users.state - Add DB index on users.state
@ -33,12 +39,17 @@ v 8.9.0 (unreleased)
- Make authentication service for Container Registry to be compatible with < Docker 1.11 - Make authentication service for Container Registry to be compatible with < Docker 1.11
- Add Application Setting to configure Container Registry token expire delay (default 5min) - Add Application Setting to configure Container Registry token expire delay (default 5min)
- Cache assigned issue and merge request counts in sidebar nav - Cache assigned issue and merge request counts in sidebar nav
- Use Knapsack only in CI environment
- Cache project build count in sidebar nav - Cache project build count in sidebar nav
- Add milestone expire date to the right sidebar - Add milestone expire date to the right sidebar
- Fix markdown_spec to use before instead of before(:all) to properly cleanup database after testing
- Reduce number of queries needed to render issue labels in the sidebar - Reduce number of queries needed to render issue labels in the sidebar
- Improve error handling importing projects - Improve error handling importing projects
- Remove duplicated notification settings
- Put project Files and Commits tabs under Code tab - Put project Files and Commits tabs under Code tab
- Replace Colorize with Rainbow for coloring console output in Rake tasks. - Replace Colorize with Rainbow for coloring console output in Rake tasks.
- An indicator is now displayed at the top of the comment field for confidential issues.
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
v 8.8.4 (unreleased) v 8.8.4 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds - Ensure branch cleanup regardless of whether the GitHub import process succeeds
@ -46,6 +57,11 @@ v 8.8.4 (unreleased)
- Fix todos page throwing errors when you have a project pending deletion - Fix todos page throwing errors when you have a project pending deletion
- Reduce number of SQL queries when rendering user references - Reduce number of SQL queries when rendering user references
- Upgrade to jQuery 2 - Upgrade to jQuery 2
- Remove prev/next buttons on issues and merge requests
- Import GitHub repositories respecting the API rate limit
- Fix importer for GitHub comments on diff
- Disable Webhooks before proceeding with the GitHub import
- Added descriptions to notification settings dropdown
v 8.8.3 v 8.8.3
- Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312 - Fix 404 page when viewing TODOs that contain milestones or labels in different projects. !4312
@ -166,6 +182,7 @@ v 8.7.6
- Fix import from GitLab.com to a private instance failure. !4181 - Fix import from GitLab.com to a private instance failure. !4181
- Fix external imports not finding the import data. !4106 - Fix external imports not finding the import data. !4106
- Fix notification delay when changing status of an issue - Fix notification delay when changing status of an issue
- Bump Workhorse to 0.7.5 so it can serve raw diffs
v 8.7.5 v 8.7.5
- Fix relative links in wiki pages. !4050 - Fix relative links in wiki pages. !4050

View file

@ -405,6 +405,7 @@ description area. Copy-paste it to retain the markdown format.
entire line to follow it. This prevents linting tools from generating warnings. entire line to follow it. This prevents linting tools from generating warnings.
- Don't touch neighbouring lines. As an exception, automatic mass - Don't touch neighbouring lines. As an exception, automatic mass
refactoring modifications may leave style non-compliant. refactoring modifications may leave style non-compliant.
1. If the merge request adds any new libraries (gems, JavaScript libraries, etc.), they should conform to our [Licensing guidelines][license-finder-doc]. See the instructions in that document for help if your MR fails the "license-finder" test with a "Dependencies that need approval" error.
## Changes for Stable Releases ## Changes for Stable Releases
@ -531,3 +532,4 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design [gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12 [free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/ [`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
[license-finder-doc]: doc/development/licensing.md

View file

@ -1 +1 @@
0.7.4 0.7.5

View file

@ -38,7 +38,7 @@ gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt' gem 'jwt'
# Spam and anti-bot protection # Spam and anti-bot protection
gem 'recaptcha', require: 'recaptcha/rails' gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails'
gem 'akismet', '~> 2.0' gem 'akismet', '~> 2.0'
# Two-factor authentication # Two-factor authentication
@ -86,6 +86,7 @@ gem 'dropzonejs-rails', '~> 0.7.1'
# for backups # for backups
gem 'fog-aws', '~> 0.9' gem 'fog-aws', '~> 0.9'
gem 'fog-azure', '~> 0.0'
gem 'fog-core', '~> 1.40' gem 'fog-core', '~> 1.40'
gem 'fog-local', '~> 0.3' gem 'fog-local', '~> 0.3'
gem 'fog-google', '~> 0.3' gem 'fog-google', '~> 0.3'
@ -111,7 +112,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.10.1' gem 'rouge', '~> 1.11'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
@ -306,6 +307,9 @@ group :development, :test do
gem 'bundler-audit', require: false gem 'bundler-audit', require: false
gem 'benchmark-ips', require: false gem 'benchmark-ips', require: false
gem "license_finder", require: false
gem 'knapsack'
end end
group :test do group :test do

View file

@ -70,6 +70,21 @@ GEM
descendants_tracker (~> 0.0.4) descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
azure (0.7.5)
addressable (~> 2.3)
azure-core (~> 0.1)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
json (~> 1.8)
mime-types (>= 1, < 3.0)
nokogiri (~> 1.6)
systemu (~> 2.6)
thor (~> 0.19)
uuid (~> 2.0)
azure-core (0.1.2)
faraday (~> 0.9)
faraday_middleware (~> 0.10)
nokogiri (~> 1.6)
babosa (1.0.2) babosa (1.0.2)
base32 (0.3.2) base32 (0.3.2)
bcrypt (3.1.11) bcrypt (3.1.11)
@ -213,6 +228,11 @@ GEM
fog-json (~> 1.0) fog-json (~> 1.0)
fog-xml (~> 0.1) fog-xml (~> 0.1)
ipaddress (~> 0.8) ipaddress (~> 0.8)
fog-azure (0.0.2)
azure (~> 0.6)
fog-core (~> 1.27)
fog-json (~> 1.0)
fog-xml (~> 0.1)
fog-core (1.40.0) fog-core (1.40.0)
builder builder
excon (~> 0.49) excon (~> 0.49)
@ -358,6 +378,9 @@ GEM
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
kgio (2.10.0) kgio (2.10.0)
knapsack (1.11.0)
rake
timecop (>= 0.1.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.4.1) letter_opener (1.4.1)
@ -366,6 +389,12 @@ GEM
actionmailer (>= 3.2) actionmailer (>= 3.2)
letter_opener (~> 1.0) letter_opener (~> 1.0)
railties (>= 3.2) railties (>= 3.2)
license_finder (2.1.0)
bundler
httparty
rubyzip
thor
xml-simple
licensee (8.0.0) licensee (8.0.0)
rugged (>= 0.24b) rugged (>= 0.24b)
listen (3.0.5) listen (3.0.5)
@ -381,7 +410,7 @@ GEM
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.1) mime-types (2.99.1)
mimemagic (0.3.0) mimemagic (0.3.0)
mini_portile2 (2.0.0) mini_portile2 (2.1.0)
minitest (5.7.0) minitest (5.7.0)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
multi_json (1.11.2) multi_json (1.11.2)
@ -392,8 +421,9 @@ GEM
net-ldap (0.12.1) net-ldap (0.12.1)
net-ssh (3.0.1) net-ssh (3.0.1)
newrelic_rpm (3.14.1.311) newrelic_rpm (3.14.1.311)
nokogiri (1.6.7.2) nokogiri (1.6.8)
mini_portile2 (~> 2.0.0.rc2) mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
oauth (0.4.7) oauth (0.4.7)
oauth2 (1.0.0) oauth2 (1.0.0)
faraday (>= 0.8, < 0.10) faraday (>= 0.8, < 0.10)
@ -465,6 +495,7 @@ GEM
parser (2.3.1.0) parser (2.3.1.0)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
pkg-config (1.1.7)
poltergeist (1.9.0) poltergeist (1.9.0)
capybara (~> 2.1) capybara (~> 2.1)
cliver (~> 0.3.1) cliver (~> 0.3.1)
@ -540,7 +571,7 @@ GEM
debugger-ruby_core_source (~> 1.3) debugger-ruby_core_source (~> 1.3)
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
recaptcha (1.0.2) recaptcha (3.0.0)
json json
redcarpet (3.3.3) redcarpet (3.3.3)
redis (3.3.0) redis (3.3.0)
@ -569,7 +600,7 @@ GEM
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
rinku (1.7.3) rinku (1.7.3)
rotp (2.1.2) rotp (2.1.2)
rouge (1.10.1) rouge (1.11.0)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
@ -618,6 +649,7 @@ GEM
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.2.0)
rufus-scheduler (3.1.10) rufus-scheduler (3.1.10)
rugged (0.24.0) rugged (0.24.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
@ -728,6 +760,7 @@ GEM
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.5) thread_safe (0.3.5)
tilt (2.0.2) tilt (2.0.2)
timecop (0.8.1)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
tinder (1.10.1) tinder (1.10.1)
eventmachine (~> 1.0) eventmachine (~> 1.0)
@ -789,6 +822,7 @@ GEM
builder builder
expression_parser expression_parser
rinku rinku
xml-simple (1.1.5)
xpath (2.0.0) xpath (2.0.0)
nokogiri (~> 1.3) nokogiri (~> 1.3)
@ -842,6 +876,7 @@ DEPENDENCIES
flay flay
flog flog
fog-aws (~> 0.9) fog-aws (~> 0.9)
fog-azure (~> 0.0)
fog-core (~> 1.40) fog-core (~> 1.40)
fog-google (~> 0.3) fog-google (~> 0.3)
fog-local (~> 0.3) fog-local (~> 0.3)
@ -874,7 +909,9 @@ DEPENDENCIES
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
jwt jwt
kaminari (~> 0.17.0) kaminari (~> 0.17.0)
knapsack
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
license_finder
licensee (~> 8.0.0) licensee (~> 8.0.0)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.7) mail_room (~> 0.7)
@ -918,7 +955,7 @@ DEPENDENCIES
raphael-rails (~> 2.1.2) raphael-rails (~> 2.1.2)
rblineprof rblineprof
rdoc (~> 3.6) rdoc (~> 3.6)
recaptcha recaptcha (~> 3.0)
redcarpet (~> 3.3.3) redcarpet (~> 3.3.3)
redis (~> 3.2) redis (~> 3.2)
redis-namespace redis-namespace
@ -926,7 +963,7 @@ DEPENDENCIES
request_store (~> 1.3.0) request_store (~> 1.3.0)
rerun (~> 0.11.0) rerun (~> 0.11.0)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.10.1) rouge (~> 1.11)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.4.0) rspec-rails (~> 3.4.0)
rspec-retry rspec-retry

View file

@ -8,3 +8,5 @@ relative_url_conf = File.expand_path('../config/initializers/relative_url', __FI
require relative_url_conf if File.exist?("#{relative_url_conf}.rb") require relative_url_conf if File.exist?("#{relative_url_conf}.rb")
Gitlab::Application.load_tasks Gitlab::Application.load_tasks
Knapsack.load_tasks if defined?(Knapsack)

View file

@ -0,0 +1,84 @@
class @LabelManager
errorMessage: 'Unable to update label prioritization at this time'
constructor: (opts = {}) ->
# Defaults
{
@togglePriorityButton = $('.js-toggle-priority')
@prioritizedLabels = $('.js-prioritized-labels')
@otherLabels = $('.js-other-labels')
} = opts
@prioritizedLabels.sortable(
items: 'li'
placeholder: 'list-placeholder'
axis: 'y'
update: @onPrioritySortUpdate.bind(@)
)
@bindEvents()
bindEvents: ->
@togglePriorityButton.on 'click', @, @onTogglePriorityClick
onTogglePriorityClick: (e) ->
e.preventDefault()
_this = e.data
$btn = $(e.currentTarget)
$label = $("##{$btn.data('domId')}")
action = if $btn.parents('.js-prioritized-labels').length then 'remove' else 'add'
_this.toggleLabelPriority($label, action)
toggleLabelPriority: ($label, action, persistState = true) ->
_this = @
url = $label.find('.js-toggle-priority').data 'url'
$target = @prioritizedLabels
$from = @otherLabels
# Optimistic update
if action is 'remove'
$target = @otherLabels
$from = @prioritizedLabels
if $from.find('li').length is 1
$from.find('.empty-message').show()
if not $target.find('li').length
$target.find('.empty-message').hide()
$label.detach().appendTo($target)
# Return if we are not persisting state
return unless persistState
if action is 'remove'
xhr = $.ajax url: url, type: 'DELETE'
else
xhr = @savePrioritySort($label, action)
xhr.fail @rollbackLabelPosition.bind(@, $label, action)
onPrioritySortUpdate: ->
xhr = @savePrioritySort()
xhr.fail ->
new Flash(@errorMessage, 'alert')
savePrioritySort: () ->
$.post
url: @prioritizedLabels.data('url')
data:
label_ids: @getSortedLabelsIds()
rollbackLabelPosition: ($label, originalAction)->
action = if originalAction is 'remove' then 'add' else 'remove'
@toggleLabelPriority($label, action, false)
new Flash(@errorMessage, 'alert')
getSortedLabelsIds: ->
sortedIds = []
@prioritizedLabels.find('li').each ->
sortedIds.push $(@).data 'id'
sortedIds

View file

@ -2,39 +2,47 @@ class @AwardsHandler
constructor: -> constructor: ->
@aliases = emojiAliases() @aliases = gl.emojiAliases()
$(document) $(document)
.off 'click', '.js-add-award' .off 'click', '.js-add-award'
.on 'click', '.js-add-award', (event) => .on 'click', '.js-add-award', (e) =>
event.stopPropagation() e.stopPropagation()
event.preventDefault() e.preventDefault()
@showEmojiMenu $(event.currentTarget) @showEmojiMenu $(e.currentTarget)
$('html').on 'click', (event) -> $('html').on 'click', (e) ->
unless $(event.target).closest('.emoji-menu').length $target = $ e.target
unless $target.closest('.emoji-menu-content').length
$('.js-awards-block.current').removeClass 'current'
unless $target.closest('.emoji-menu').length
if $('.emoji-menu').is(':visible') if $('.emoji-menu').is(':visible')
$('.js-add-award.is-active').removeClass 'is-active' $('.js-add-award.is-active').removeClass 'is-active'
$('.emoji-menu').removeClass 'is-visible' $('.emoji-menu').removeClass 'is-visible'
$(document) $(document)
.off 'click', '.js-emoji-btn' .off 'click', '.js-emoji-btn'
.on 'click', '.js-emoji-btn', @handleClick .on 'click', '.js-emoji-btn', (e) =>
e.preventDefault()
$target = $ e.currentTarget
emoji = $target.find('.icon').data 'emoji'
handleClick: (e) => $target.closest('.js-awards-block').addClass 'current'
@addAward @getVotesBlock(), @getAwardUrl(), emoji
e.preventDefault()
emoji = $(e.currentTarget).find('.icon').data 'emoji'
@getVotesBlock().addClass 'js-awards-block'
@addAward @getAwardUrl(), emoji
showEmojiMenu: ($addBtn) -> showEmojiMenu: ($addBtn) ->
$menu = $('.emoji-menu') $menu = $ '.emoji-menu'
if $addBtn.hasClass 'js-note-emoji'
$addBtn.parents('.note').find('.js-awards-block').addClass 'current'
else
$addBtn.closest('.js-awards-block').addClass 'current'
if $menu.length if $menu.length
$holder = $addBtn.closest('.js-award-holder') $holder = $addBtn.closest('.js-award-holder')
@ -51,7 +59,7 @@ class @AwardsHandler
$('#emoji_search').focus() $('#emoji_search').focus()
else else
$addBtn.addClass 'is-loading is-active' $addBtn.addClass 'is-loading is-active'
url = $addBtn.data 'award-menu-url' url = @getAwardMenuUrl()
@createEmojiMenu url, => @createEmojiMenu url, =>
$addBtn.removeClass 'is-loading' $addBtn.removeClass 'is-loading'
@ -68,12 +76,13 @@ class @AwardsHandler
createEmojiMenu: (awardMenuUrl, callback) -> createEmojiMenu: (awardMenuUrl, callback) ->
$.get awardMenuUrl, (response) => $.get awardMenuUrl, (response) ->
$('body').append response $('body').append response
callback() callback()
positionMenu: ($menu, $addBtn) -> positionMenu: ($menu, $addBtn) ->
position = $addBtn.data('position') position = $addBtn.data('position')
# The menu could potentially be off-screen or in a hidden overflow element # The menu could potentially be off-screen or in a hidden overflow element
@ -91,88 +100,114 @@ class @AwardsHandler
$menu.css(css) $menu.css(css)
addAward: (awardUrl, emoji, checkMutuality = yes) -> addAward: (votesBlock, awardUrl, emoji, checkMutuality = yes, callback) ->
emoji = @normilizeEmojiName emoji
emoji = @normilizeEmojiName(emoji)
@postEmoji awardUrl, emoji, => @postEmoji awardUrl, emoji, =>
@addAwardToEmojiBar(emoji, checkMutuality) @addAwardToEmojiBar votesBlock, emoji, checkMutuality
callback?()
$('.js-awards-block-current').removeClass 'js-awards-block-current'
$('.emoji-menu').removeClass 'is-visible' $('.emoji-menu').removeClass 'is-visible'
addAwardToEmojiBar: (emoji, checkForMutuality = yes) -> addAwardToEmojiBar: (votesBlock, emoji, checkForMutuality = yes) ->
@checkMutuality emoji if checkForMutuality @checkMutuality votesBlock, emoji if checkForMutuality
@addEmojiToFrequentlyUsedList(emoji) @addEmojiToFrequentlyUsedList emoji
emoji = @normilizeEmojiName(emoji) emoji = @normilizeEmojiName emoji
$emojiBtn = @findEmojiIcon(emoji).parent() $emojiButton = @findEmojiIcon(votesBlock, emoji).parent()
if $emojiBtn.length > 0 if $emojiButton.length > 0
if @isActive($emojiBtn) if @isActive $emojiButton
@decrementCounter($emojiBtn, emoji) @decrementCounter $emojiButton, emoji
else else
counter = $emojiBtn.find('.js-counter') counter = $emojiButton.find '.js-counter'
counter.text(parseInt(counter.text()) + 1) counter.text parseInt(counter.text()) + 1
$emojiBtn.addClass('active') $emojiButton.addClass 'active'
@addMeToUserList(emoji) @addMeToUserList votesBlock, emoji
@animateEmoji $emojiButton
else else
@createEmoji(emoji) votesBlock.removeClass 'hidden'
@createEmoji votesBlock, emoji
getVotesBlock: -> return $ '.awards.js-awards-block' getVotesBlock: ->
currentBlock = $ '.js-awards-block.current'
return if currentBlock.length then currentBlock else $('.js-awards-block').eq 0
getAwardUrl: -> @getVotesBlock().data 'award-url' getAwardUrl: -> return @getVotesBlock().data 'award-url'
checkMutuality: (emoji) -> checkMutuality: (votesBlock, emoji) ->
awardUrl = @getAwardUrl() awardUrl = @getAwardUrl()
if emoji in [ 'thumbsup', 'thumbsdown' ] if emoji in [ 'thumbsup', 'thumbsdown' ]
mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup' mutualVote = if emoji is 'thumbsup' then 'thumbsdown' else 'thumbsup'
$emojiButton = votesBlock.find("[data-emoji=#{mutualVote}]").parent()
isAlreadyVoted = $emojiButton.hasClass 'active'
isAlreadyVoted = $("[data-emoji=#{mutualVote}]").parent().hasClass 'active' if isAlreadyVoted
@addAward awardUrl, mutualVote, no if isAlreadyVoted @showEmojiLoader $emojiButton
@addAward votesBlock, awardUrl, mutualVote, no, ->
$emojiButton.removeClass 'is-loading'
isActive: ($emojiBtn) -> $emojiBtn.hasClass 'active' showEmojiLoader: ($emojiButton) ->
$loader = $emojiButton.find '.fa-spinner'
unless $loader.length
$emojiButton.append '<i class="fa fa-spinner fa-spin award-control-icon award-control-icon-loading"></i>'
$emojiButton.addClass 'is-loading'
decrementCounter: ($emojiBtn, emoji) -> isActive: ($emojiButton) -> $emojiButton.hasClass 'active'
isntNoteBody = $emojiBtn.closest('.note-body').length is 0
counter = $('.js-counter', $emojiBtn)
counterNumber = parseInt(counter.text())
if !isntNoteBody
# If this is a note body, we just hide the award emoji row like the initial state decrementCounter: ($emojiButton, emoji) ->
$emojiBtn.closest('.js-awards-block').addClass 'hidden'
counter = $ '.js-counter', $emojiButton
counterNumber = parseInt counter.text(), 10
if counterNumber > 1 if counterNumber > 1
counter.text(counterNumber - 1) counter.text counterNumber - 1
@removeMeFromUserList($emojiBtn, emoji) @removeMeFromUserList $emojiButton, emoji
else if (emoji == 'thumbsup' || emoji == 'thumbsdown') && isntNoteBody else if emoji is 'thumbsup' or emoji is 'thumbsdown'
$emojiBtn.tooltip('destroy') $emojiButton.tooltip 'destroy'
counter.text('0') counter.text '0'
@removeMeFromUserList($emojiBtn, emoji) @removeMeFromUserList $emojiButton, emoji
@removeEmoji $emojiButton if $emojiButton.parents('.note').length
else else
$emojiBtn.tooltip('destroy') @removeEmoji $emojiButton
$emojiBtn.remove()
$emojiBtn.removeClass('active') $emojiButton.removeClass 'active'
removeEmoji: ($emojiButton) ->
$emojiButton.tooltip('destroy')
$emojiButton.remove()
$votesBlock = @getVotesBlock()
if $votesBlock.find('.js-emoji-btn').length is 0
$votesBlock.addClass 'hidden'
getAwardTooltip: ($awardBlock) -> getAwardTooltip: ($awardBlock) ->
return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') return $awardBlock.attr('data-original-title') or $awardBlock.attr('data-title') or ''
removeMeFromUserList: ($emojiBtn, emoji) -> removeMeFromUserList: ($emojiButton, emoji) ->
awardBlock = $emojiBtn awardBlock = $emojiButton
originalTitle = @getAwardTooltip awardBlock originalTitle = @getAwardTooltip awardBlock
authors = originalTitle.split ', ' authors = originalTitle.split ', '
@ -183,117 +218,134 @@ class @AwardsHandler
awardBlock awardBlock
.closest '.js-emoji-btn' .closest '.js-emoji-btn'
.removeData 'original-title' .removeData 'original-title'
.removeData 'title'
.attr 'data-original-title', newAuthors .attr 'data-original-title', newAuthors
.attr 'data-title', newAuthors
@resetTooltip(awardBlock) @resetTooltip awardBlock
addMeToUserList: (emoji) -> addMeToUserList: (votesBlock, emoji) ->
awardBlock = @findEmojiIcon(emoji).parent() awardBlock = @findEmojiIcon(votesBlock, emoji).parent()
origTitle = @getAwardTooltip awardBlock origTitle = @getAwardTooltip awardBlock
users = [] users = []
if origTitle if origTitle
users = origTitle.trim().split(', ') users = origTitle.trim().split ', '
users.push('me') users.push 'me'
awardBlock.attr('title', users.join(', ')) awardBlock.attr 'title', users.join ', '
@resetTooltip(awardBlock) @resetTooltip awardBlock
resetTooltip: (award) -> resetTooltip: (award) ->
award.tooltip('destroy')
award.tooltip 'destroy'
# 'destroy' call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout. # 'destroy' call is asynchronous and there is no appropriate callback on it, this is why we need to set timeout.
setTimeout (-> cb = -> award.tooltip()
award.tooltip() setTimeout cb, 200
), 200
createEmoji_: (emoji) -> createEmoji_: (votesBlock, emoji) ->
emojiCssClass = @resolveNameToCssClass emoji emojiCssClass = @resolveNameToCssClass emoji
buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'>
buttonHtml = "<button class='btn award-control js-emoji-btn has-tooltip active' title='me' data-placement='bottom'>
<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div> <div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>
<span class='award-control-text js-counter'>1</span> <span class='award-control-text js-counter'>1</span>
</button>" </button>"
emoji_node = $(buttonHtml) $emojiButton = $ buttonHtml
.insertBefore '.js-awards-block .js-award-holder:not(.js-award-action-btn)' $emojiButton
.insertBefore votesBlock.find '.js-award-holder'
.find '.emoji-icon' .find '.emoji-icon'
.data 'emoji', emoji .data 'emoji', emoji
@animateEmoji $emojiButton
$('.award-control').tooltip() $('.award-control').tooltip()
votesBlock.removeClass 'current'
$currentBlock = $ '.js-awards-block'
if $currentBlock.is '.hidden'
$currentBlock.removeClass 'hidden'
createEmoji: (emoji) -> animateEmoji: ($emoji) ->
return @createEmoji_ emoji if $('.emoji-menu').length className = 'pulse animated'
awardMenuUrl = gl.awardMenuUrl or '/emojis' $emoji.addClass className
@createEmojiMenu awardMenuUrl, => @createEmoji emoji setTimeout (-> $emoji.removeClass className), 321
createEmoji: (votesBlock, emoji) ->
if $('.emoji-menu').length
return @createEmoji_ votesBlock, emoji
@createEmojiMenu @getAwardMenuUrl(), => @createEmoji_ votesBlock, emoji
getAwardMenuUrl: -> return gl.awardMenuUrl
resolveNameToCssClass: (emoji) -> resolveNameToCssClass: (emoji) ->
emoji_icon = $(".emoji-menu-content [data-emoji='#{emoji}']") emojiIcon = $ ".emoji-menu-content [data-emoji='#{emoji}']"
if emoji_icon.length > 0 if emojiIcon.length > 0
unicodeName = emoji_icon.data('unicode-name') unicodeName = emojiIcon.data 'unicode-name'
else else
# Find by alias # Find by alias
unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data('unicode-name') unicodeName = $(".emoji-menu-content [data-aliases*=':#{emoji}:']").data 'unicode-name'
return "emoji-#{unicodeName}" return "emoji-#{unicodeName}"
postEmoji: (awardUrl, emoji, callback) -> postEmoji: (awardUrl, emoji, callback) ->
$.post awardUrl, { name: emoji }, (data) ->
if data.ok
callback.call()
findEmojiIcon: (emoji) -> $.post awardUrl, { name: emoji }, (data) ->
$(".js-awards-block.awards > .js-emoji-btn [data-emoji='#{emoji}']") callback() if data.ok
findEmojiIcon: (votesBlock, emoji) ->
return votesBlock.find ".js-emoji-btn [data-emoji='#{emoji}']"
scrollToAwards: -> scrollToAwards: ->
$('body, html').animate({
scrollTop: $('.awards').offset().top - 80
}, 200)
normilizeEmojiName: (emoji) -> options = scrollTop: $('.awards').offset().top - 110
@aliases[emoji] || emoji $('body, html').animate options, 200
normilizeEmojiName: (emoji) -> return @aliases[emoji] or emoji
addEmojiToFrequentlyUsedList: (emoji) -> addEmojiToFrequentlyUsedList: (emoji) ->
frequently_used_emojis = @getFrequentlyUsedEmojis()
frequently_used_emojis.push(emoji) frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
$.cookie('frequently_used_emojis', frequently_used_emojis.join(','), { expires: 365 }) frequentlyUsedEmojis.push emoji
$.cookie 'frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }
getFrequentlyUsedEmojis: -> getFrequentlyUsedEmojis: ->
frequently_used_emojis = ($.cookie('frequently_used_emojis') || '').split(',')
_.compact(_.uniq(frequently_used_emojis)) frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') or '').split(',')
return _.compact _.uniq frequentlyUsedEmojis
renderFrequentlyUsedBlock: -> renderFrequentlyUsedBlock: ->
if $.cookie('frequently_used_emojis')
frequently_used_emojis = @getFrequentlyUsedEmojis() if $.cookie 'frequently_used_emojis'
frequentlyUsedEmojis = @getFrequentlyUsedEmojis()
ul = $("<ul class='clearfix emoji-menu-list'>") ul = $("<ul class='clearfix emoji-menu-list'>")
for emoji in frequently_used_emojis for emoji in frequentlyUsedEmojis
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used'))
setupSearch: -> setupSearch: ->
$('input.emoji-search').on 'keyup', (ev) => $('input.emoji-search').on 'keyup', (ev) =>
term = $(ev.target).val() term = $(ev.target).val()
@ -310,5 +362,7 @@ class @AwardsHandler
else else
$('.emoji-menu-content').children().show() $('.emoji-menu-content').children().show()
searchEmojis: (term)->
searchEmojis: (term) ->
$(".emoji-menu-content [data-emoji*='#{term}']").closest('li').clone() $(".emoji-menu-content [data-emoji*='#{term}']").closest('li').clone()

View file

@ -23,7 +23,7 @@ class Dispatcher
new Issue() new Issue()
shortcut_handler = new ShortcutsIssuable() shortcut_handler = new ShortcutsIssuable()
new ZenMode() new ZenMode()
window.awardsHandler = new AwardsHandler() gl.awardsHandler = new AwardsHandler()
when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show'
new Milestone() new Milestone()
when 'dashboard:todos:index' when 'dashboard:todos:index'
@ -54,7 +54,7 @@ class Dispatcher
new Diff() new Diff()
shortcut_handler = new ShortcutsIssuable(true) shortcut_handler = new ShortcutsIssuable(true)
new ZenMode() new ZenMode()
window.awardsHandler = new AwardsHandler() gl.awardsHandler = new AwardsHandler()
when "projects:merge_requests:diffs" when "projects:merge_requests:diffs"
new Diff() new Diff()
new ZenMode() new ZenMode()
@ -100,6 +100,8 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:labels:new', 'projects:labels:edit' when 'projects:labels:new', 'projects:labels:edit'
new Labels() new Labels()
when 'projects:labels:index'
new LabelManager() if $('.prioritized-labels').length
when 'projects:network:show' when 'projects:network:show'
# Ensure we don't create a particular shortcut handler here. This is # Ensure we don't create a particular shortcut handler here. This is
# already created, where the network graph is created. # already created, where the network graph is created.

View file

@ -211,6 +211,7 @@ class GitLabDropdown
@dropdown.on "shown.bs.dropdown", @opened @dropdown.on "shown.bs.dropdown", @opened
@dropdown.on "hidden.bs.dropdown", @hidden @dropdown.on "hidden.bs.dropdown", @hidden
$(@el).on "update.label", @updateLabel
@dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
@dropdown.on 'keyup', (e) => @dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key if e.which is 27 # Escape key
@ -453,7 +454,7 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel @updateLabel()
else else
selectedObject selectedObject
else if el.hasClass(INDETERMINATE_CLASS) else if el.hasClass(INDETERMINATE_CLASS)
@ -480,7 +481,7 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selectedObject, el) @updateLabel(selectedObject, el)
if value? if value?
if !field.length and fieldName if !field.length and fieldName
@addInput(fieldName, value) @addInput(fieldName, value)
@ -579,6 +580,9 @@ class GitLabDropdown
# Scroll the dropdown content up # Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop) $dropdownContent.scrollTop(listItemTop - dropdownContentTop)
updateLabel: (selected = null, el = null) =>
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
$.fn.glDropdown = (opts) -> $.fn.glDropdown = (opts) ->
return @.each -> return @.each ->
if (!$.data @, 'glDropdown') if (!$.data @, 'glDropdown')

View file

@ -6,12 +6,18 @@ issuable_created = false
Issuable.initTemplates() Issuable.initTemplates()
Issuable.initSearch() Issuable.initSearch()
Issuable.initChecks() Issuable.initChecks()
Issuable.initLabelFilterRemove()
initTemplates: -> initTemplates: ->
Issuable.labelRow = _.template( Issuable.labelRow = _.template(
'<% _.each(labels, function(label){ %> '<% _.each(labels, function(label){ %>
<span class="label-row"> <span class="label-row btn-group" role="group" aria-label="<%= _.escape(label.title) %>" style="color: <%= label.text_color %>;">
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a> <a href="#" class="btn btn-transparent has-tooltip" style="background-color: <%= label.color %>;" title="<%= _.escape(label.description) %>" data-container="body">
<%= _.escape(label.title) %>
</a>
<button type="button" class="btn btn-transparent label-remove js-label-filter-remove" style="background-color: <%= label.color %>;" data-label="<%= _.escape(label.title) %>">
<i class="fa fa-times"></i>
</button>
</span> </span>
<% }); %>' <% }); %>'
) )
@ -35,6 +41,21 @@ issuable_created = false
Issuable.filterResults $form Issuable.filterResults $form
, 500) , 500)
initLabelFilterRemove: ->
$(document)
.off 'click', '.js-label-filter-remove'
.on 'click', '.js-label-filter-remove', (e) ->
$button = $(@)
# Remove the label input box
$('input[name="label_name[]"]')
.filter -> @value is $button.data('label')
.remove()
# Submit the form to get new data
Issuable.filterResults $('.filter-form')
$('.js-label-select').trigger('update.label')
toggleLabelFilters: -> toggleLabelFilters: ->
$filteredLabels = $('.filtered-labels') $filteredLabels = $('.filtered-labels')
if $filteredLabels.find('.label-row').length > 0 if $filteredLabels.find('.label-row').length > 0

View file

@ -1,2 +1,2 @@
window.emojiAliases = -> gl.emojiAliases = ->
JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>') JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')

View file

@ -162,13 +162,14 @@ class @Notes
renderNote: (note) -> renderNote: (note) ->
unless note.valid unless note.valid
if note.award if note.award
flash = new Flash('You have already used this award emoji!', 'alert') flash = new Flash('You have already awarded this emoji!', 'alert')
flash.pinTo('.header-content') flash.pinTo('.header-content')
return return
if note.award if note.award
awardsHandler.addAwardToEmojiBar(note.name) votesBlock = $('.js-awards-block').eq 0
awardsHandler.scrollToAwards() gl.awardsHandler.addAwardToEmojiBar votesBlock, note.name
gl.awardsHandler.scrollToAwards()
# render note if it not present in loaded list # render note if it not present in loaded list
# or skip if rendered # or skip if rendered

View file

@ -10,14 +10,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
@replyWithSelectedText() @replyWithSelectedText()
return false return false
) )
Mousetrap.bind('j', =>
@prevIssue()
return false
)
Mousetrap.bind('k', =>
@nextIssue()
return false
)
Mousetrap.bind('e', => Mousetrap.bind('e', =>
@editIssue() @editIssue()
return false return false
@ -29,16 +21,6 @@ class @ShortcutsIssuable extends ShortcutsNavigation
else else
@enabledHelp.push('.hidden-shortcut.issues') @enabledHelp.push('.hidden-shortcut.issues')
prevIssue: ->
$prevBtn = $('.prev-btn')
if not $prevBtn.hasClass('disabled')
Turbolinks.visit($prevBtn.attr('href'))
nextIssue: ->
$nextBtn = $('.next-btn')
if not $nextBtn.hasClass('disabled')
Turbolinks.visit($nextBtn.attr('href'))
replyWithSelectedText: -> replyWithSelectedText: ->
if window.getSelection if window.getSelection
selected = window.getSelection().toString() selected = window.getSelection().toString()

View file

@ -61,6 +61,11 @@
margin-bottom: -$gl-padding; margin-bottom: -$gl-padding;
} }
&.content-component-block {
padding: 11px 0;
background-color: $white-light;
}
.title { .title {
color: $gl-text-color; color: $gl-text-color;
} }

View file

@ -122,10 +122,8 @@
a { a {
display: block; display: block;
position: relative; position: relative;
padding-left: 10px; padding: 5px 10px;
padding-right: 10px;
color: $dropdown-link-color; color: $dropdown-link-color;
line-height: 34px;
text-overflow: ellipsis; text-overflow: ellipsis;
border-radius: 2px; border-radius: 2px;
white-space: nowrap; white-space: nowrap;
@ -162,6 +160,16 @@
} }
} }
.dropdown-menu-large {
width: 340px;
}
.dropdown-menu-no-wrap {
a {
white-space: normal;
}
}
.dropdown-menu-full-width { .dropdown-menu-full-width {
width: 100%; width: 100%;
} }
@ -236,8 +244,7 @@
&::before { &::before {
position: absolute; position: absolute;
left: 5px; left: 5px;
top: 50%; top: 8px;
margin-top: -7px;
font: normal normal normal 14px/1 FontAwesome; font: normal normal normal 14px/1 FontAwesome;
font-size: inherit; font-size: inherit;
text-rendering: auto; text-rendering: auto;
@ -532,3 +539,14 @@
background-color: $calendar-unselectable-bg; background-color: $calendar-unselectable-bg;
} }
} }
.dropdown-menu-inner-title {
display: block;
color: $gl-title-color;
font-weight: 600;
}
.dropdown-menu-inner-content {
display: block;
color: $gl-placeholder-color;
}

View file

@ -89,8 +89,11 @@
} }
} }
$theme-blue: #2980b9;
$theme-charcoal: #3d454d; $theme-charcoal: #3d454d;
$theme-charcoal-dark: #383f45;
$theme-charcoal-text: #b9bbbe;
$theme-blue: #2980b9;
$theme-graphite: #666; $theme-graphite: #666;
$theme-gray: #373737; $theme-gray: #373737;
$theme-green: #019875; $theme-green: #019875;
@ -102,7 +105,7 @@ body {
} }
&.ui_charcoal { &.ui_charcoal {
@include gitlab-theme(#d6d7d9, #485157, $theme-charcoal, #353b41); @include gitlab-theme($theme-charcoal-text, #485157, $theme-charcoal, $theme-charcoal-dark);
} }
&.ui_graphite { &.ui_graphite {

View file

@ -79,6 +79,10 @@ header {
&.header-collapsed { &.header-collapsed {
padding: 0 16px; padding: 0 16px;
.side-nav-toggle {
display: block;
}
} }
.side-nav-toggle { .side-nav-toggle {
@ -86,6 +90,7 @@ header {
position: absolute; position: absolute;
left: -10px; left: -10px;
margin: 6px 0; margin: 6px 0;
font-size: 18px;
padding: 6px 10px; padding: 6px 10px;
border: none; border: none;
background-color: $background-color; background-color: $background-color;
@ -97,10 +102,6 @@ header {
&:focus { &:focus {
outline: none; outline: none;
} }
@media (max-width: $screen-xs-min) {
display: block;
}
} }
} }
@ -171,31 +172,21 @@ header {
} }
} }
@mixin collapsed-header {
margin-left: $sidebar_collapsed_width;
}
.header-collapsed { .header-collapsed {
margin-left: $sidebar_collapsed_width; margin-left: 0;
@media (min-width: $screen-md-min) { .header-content {
@include collapsed-header; padding-left: 30px;
} transition-duration: .3s;
@media (max-width: $screen-xs-min) {
margin-left: 0;
} }
} }
.header-expanded { .header-expanded {
margin-left: $sidebar_collapsed_width; margin-left: 0;
@media (min-width: $screen-md-min) { .header-content {
margin-left: $sidebar_width; padding-left: $sidebar_width;
} transition-duration: .3s;
@media (max-width: $screen-xs-min) {
margin-left: 0;
} }
} }

View file

@ -141,6 +141,18 @@ ul.content-list {
padding: 10px 14px; padding: 10px 14px;
} }
} }
// When dragging a list item
&.ui-sortable-helper {
border-bottom: none;
}
&.list-placeholder {
background-color: $gray-light;
border: dotted 1px $gray-dark;
margin: 1px 0;
min-height: 30px;
}
} }
} }

View file

@ -66,10 +66,6 @@
display: none; display: none;
} }
%ul.notes .note-role, .note-actions {
display: none;
}
.nav-links, .nav-links { .nav-links, .nav-links {
li a { li a {
font-size: 14px; font-size: 14px;

View file

@ -41,8 +41,7 @@
a { a {
display: inline-block; display: inline-block;
padding: 14px; padding: $gl-btn-padding;
padding-top: $gl-padding;
padding-bottom: 11px; padding-bottom: 11px;
margin-bottom: -1px; margin-bottom: -1px;
font-size: 15px; font-size: 15px;
@ -67,6 +66,27 @@
color: #78a; color: #78a;
} }
} }
&.sub-nav {
background-color: $background-color;
.container-fluid {
background-color: $background-color;
}
li {
a {
margin: 0;
padding: 11px 10px 9px;
}
&.active a {
border-bottom: none;
color: $link-underline-blue;
}
}
}
} }
.top-area { .top-area {
@ -81,6 +101,10 @@
width: 50%; width: 50%;
line-height: 28px; line-height: 28px;
&.wiki-page {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */ /* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-min) { @media (max-width: $screen-sm-min) {
width: 100%; width: 100%;
@ -104,6 +128,10 @@
margin-bottom: 0; margin-bottom: 0;
border-bottom: none; border-bottom: none;
li a {
padding: 16px 10px 11px;
}
/* Small devices (phones, tablets, 768px and lower) */ /* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-max) { @media (max-width: $screen-sm-max) {
width: 100%; width: 100%;
@ -309,8 +337,8 @@
} }
.nav-control { .nav-control {
.fade-right {
.fade-right {
@media (min-width: $screen-xs-max) { @media (min-width: $screen-xs-max) {
right: 67px; right: 67px;
} }
@ -321,6 +349,24 @@
} }
} }
.scrolling-tabs-container {
position: relative;
.nav-links {
@include scrolling-links();
.fade-right {
@include fade(left, rgba(255, 255, 255, 0.4), $background-color);
right: 0;
}
.fade-left {
@include fade(right, rgba(255, 255, 255, 0.4), $background-color);
left: 0;
}
}
}
.nav-block { .nav-block {
position: relative; position: relative;

View file

@ -1,11 +1,3 @@
#logo {
z-index: 2;
position: absolute;
width: 58px;
cursor: pointer;
margin-top: 8px;
}
.page-with-sidebar { .page-with-sidebar {
padding-top: $header-height; padding-top: $header-height;
transition-duration: .3s; transition-duration: .3s;
@ -20,12 +12,6 @@
height: 100%; height: 100%;
transition-duration: .3s; transition-duration: .3s;
} }
.gitlab-text-container-link {
z-index: 1;
position: absolute;
left: 0;
}
} }
.sidebar-wrapper { .sidebar-wrapper {
@ -50,47 +36,8 @@
.sidebar-wrapper { .sidebar-wrapper {
.header-logo { .header-logo {
border-bottom: 1px solid transparent;
float: left;
height: $header-height; height: $header-height;
width: $sidebar_width; padding: 8px 26px;
position: fixed;
z-index: 999;
overflow: hidden;
transition-duration: .3s;
a {
float: left;
height: $header-height;
width: 100%;
padding-left: 22px;
overflow: hidden;
outline: none;
transition-duration: .3s;
img {
width: 36px;
height: 36px;
}
#tanuki-logo, img {
float: left;
}
.gitlab-text-container {
width: 230px;
h3 {
width: 158px;
float: left;
margin: 0;
margin-left: 50px;
font-size: 19px;
line-height: 50px;
font-weight: normal;
}
}
}
&:hover { &:hover {
background-color: #eee; background-color: #eee;
@ -98,7 +45,7 @@
} }
.sidebar-user { .sidebar-user {
padding: 7px 22px; padding: 15px 22px;
position: fixed; position: fixed;
bottom: 40px; bottom: 40px;
width: $sidebar_width; width: $sidebar_width;
@ -126,8 +73,7 @@
.nav-sidebar { .nav-sidebar {
margin-top: 14 + $header-height; margin: 22px 0;
margin-bottom: 100px;
transition-duration: .3s; transition-duration: .3s;
list-style: none; list-style: none;
overflow: hidden; overflow: hidden;
@ -145,13 +91,12 @@
} }
a { a {
padding: 7px 15px; text-align: center;
padding: 8px;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 24px;
color: $gray; color: $gray;
display: block; display: block;
text-decoration: none; text-decoration: none;
padding-left: 23px;
font-weight: normal; font-weight: normal;
outline: none; outline: none;
@ -166,14 +111,12 @@
i { i {
width: 16px; width: 16px;
color: $gray-light; color: $gray-light;
margin-right: 13px;
} }
.count { .nav-link-text {
float: right; margin-top: 3px;
background: #eee; font-size: 13px;
padding: 0 8px; line-height: 18px;
@include border-radius(6px);
} }
&.back-link i { &.back-link i {
@ -217,25 +160,13 @@
} }
.page-sidebar-collapsed { .page-sidebar-collapsed {
padding-left: $sidebar_collapsed_width; padding-left: 0;
@media (max-width: $screen-xs-min) {
padding-left: 0;
}
.sidebar-wrapper { .sidebar-wrapper {
width: $sidebar_collapsed_width; width: 0;
@media (max-width: $screen-xs-min) {
width: 0;
}
.header-logo { .header-logo {
width: $sidebar_collapsed_width; width: 0;
@media (max-width: $screen-xs-min) {
width: 0;
}
a { a {
padding-left: ($sidebar_collapsed_width - 36) / 2; padding-left: ($sidebar_collapsed_width - 36) / 2;
@ -246,6 +177,10 @@
} }
} }
#logo {
display: none;
}
.nav-sidebar { .nav-sidebar {
width: $sidebar_collapsed_width; width: $sidebar_collapsed_width;
@ -261,44 +196,23 @@
} }
.collapse-nav a { .collapse-nav a {
width: $sidebar_collapsed_width; width: 0;
@media (max-width: $screen-xs-min) {
width: 0;
}
} }
.sidebar-user { .sidebar-user {
padding-left: ($sidebar_collapsed_width - 36) / 2; width: 0;
width: $sidebar_collapsed_width; padding-left: 0;
padding-right: 0;
@media (max-width: $screen-xs-min) {
width: 0;
padding-left: 0;
padding-right: 0;
}
.username { .username {
display: none; display: none;
} }
} }
} }
.layout-nav {
padding-right: $sidebar_collapsed_width;
@media (max-width: $screen-xs-min) {
padding-right: 0;;
}
}
} }
.page-sidebar-expanded { .page-sidebar-expanded {
padding-left: $sidebar_collapsed_width; padding-left: $sidebar_width;
@media (min-width: $screen-md-min) {
padding-left: $sidebar_width;
}
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-min) {
padding-left: 0; padding-left: 0;
@ -328,7 +242,7 @@
} }
@media (min-width: $screen-xs-min) and (max-width: $screen-md-min) { @media (min-width: $screen-xs-min) and (max-width: $screen-md-min) {
padding-right: 62px; padding-right: 90px;
} }
@media (min-width: $screen-md-min) { @media (min-width: $screen-md-min) {

View file

@ -5,7 +5,7 @@
padding: 0; padding: 0;
.timeline-entry { .timeline-entry {
padding: $gl-padding $gl-btn-padding; padding: $gl-padding $gl-btn-padding 11px;
border-color: $table-border-color; border-color: $table-border-color;
color: $gl-gray; color: $gl-gray;
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;

View file

@ -2,7 +2,7 @@
* Layout * Layout
*/ */
$sidebar_collapsed_width: 62px; $sidebar_collapsed_width: 62px;
$sidebar_width: 220px; $sidebar_width: 90px;
$gutter_collapsed_width: 62px; $gutter_collapsed_width: 62px;
$gutter_width: 290px; $gutter_width: 290px;
$gutter_inner_width: 258px; $gutter_inner_width: 258px;

View file

@ -1,5 +1,15 @@
@import "framework/variables"; @import "framework/variables";
// This file is largely copied from `highlight/white.scss`, but modified to
// avoid all descendant selectors (`table td`). This is because the CSS inlining
// we use performs dramatically worse on descendant selectors than the
// alternatives.
// <https://gitlab.com/gitlab-org/gitlab-ee/issues/490#note_12283632>
//
// DO NOT ADD ANY DESCENDANT SELECTORS TO THIS FILE. Instead, use (in order of
// preference): plain class selectors, type (element name) selectors, or
// explicit child selectors.
table.code { table.code {
width: 100%; width: 100%;
font-family: monospace; font-family: monospace;
@ -11,33 +21,162 @@ table.code {
-premailer-cellspacing: 0; -premailer-cellspacing: 0;
-premailer-width: 100%; -premailer-width: 100%;
td { > tr > td {
line-height: $code_line_height; line-height: $code_line_height;
font-family: monospace; font-family: monospace;
font-size: $code_font_size; font-size: $code_font_size;
}
td.diff-line-num { &.diff-line-num {
margin: 0; margin: 0;
padding: 0; padding: 0;
border: none; border: none;
background: $background-color; padding: 0 5px;
color: rgba(0, 0, 0, 0.3); border-right: 1px solid;
padding: 0 5px; text-align: right;
border-right: 1px solid $border-color; min-width: 35px;
text-align: right; max-width: 50px;
min-width: 35px; width: 35px;
max-width: 50px; }
width: 35px;
}
td.line_content { &.line_content {
display: block; display: block;
margin: 0; margin: 0;
padding: 0 0.5em; padding: 0 0.5em;
border: none; border: none;
white-space: pre; white-space: pre;
}
} }
} }
@import "highlight/white"; .line-numbers, .diff-line-num {
background-color: $background-color;
}
.diff-line-num, .diff-line-num a {
color: $black-transparent;
}
pre.code, .diff-line-num {
border-color: $table-border-gray;
}
.code.white, pre.code, .line_content {
background-color: #fff;
color: #333;
}
.diff-line-num {
&.old {
background-color: $line-number-old;
border-color: $line-removed-dark;
}
&.new {
background-color: $line-number-new;
border-color: $line-added-dark;
}
&.hll:not(.empty-cell) {
background-color: $line-number-select;
border-color: $line-select-yellow-dark;
}
}
.line_content {
&.old {
background-color: $line-removed;
> .line > span.idiff, > .line > span > span.idiff {
background-color: $line-removed-dark;
}
}
&.new {
background-color: $line-added;
> .line > span.idiff, > .line > span > span.idiff {
background-color: $line-added-dark;
}
}
&.match {
color: $black-transparent;
background-color: $match-line;
}
&.hll:not(.empty-cell) {
background-color: $line-select-yellow;
}
}
pre > .hll {
background-color: #f8eec7 !important;
}
span.highlight_word {
background-color: #fafe3d !important;
}
.hll { background-color: #f8f8f8 }
.c { color: #998; font-style: italic; }
.err { color: #a61717; background-color: #e3d2d2; }
.k { font-weight: bold; }
.o { font-weight: bold; }
.cm { color: #998; font-style: italic; }
.cp { color: #999; font-weight: bold; }
.c1 { color: #998; font-style: italic; }
.cs { color: #999; font-weight: bold; font-style: italic; }
.gd { color: #000; background-color: #fdd; }
.gd .x { color: #000; background-color: #faa; }
.ge { font-style: italic; }
.gr { color: #a00; }
.gh { color: #999; }
.gi { color: #000; background-color: #dfd; }
.gi .x { color: #000; background-color: #afa; }
.go { color: #888; }
.gp { color: #555; }
.gs { font-weight: bold; }
.gu { color: #800080; font-weight: bold; }
.gt { color: #a00; }
.kc { font-weight: bold; }
.kd { font-weight: bold; }
.kn { font-weight: bold; }
.kp { font-weight: bold; }
.kr { font-weight: bold; }
.kt { color: #458; font-weight: bold; }
.m { color: #099; }
.s { color: #d14; }
.n { color: #333; }
.na { color: teal; }
.nb { color: #0086b3; }
.nc { color: #458; font-weight: bold; }
.no { color: teal; }
.ni { color: purple; }
.ne { color: #900; font-weight: bold; }
.nf { color: #900; font-weight: bold; }
.nn { color: #555; }
.nt { color: navy; }
.nv { color: teal; }
.ow { font-weight: bold; }
.w { color: #bbb; }
.mf { color: #099; }
.mh { color: #099; }
.mi { color: #099; }
.mo { color: #099; }
.sb { color: #d14; }
.sc { color: #d14; }
.sd { color: #d14; }
.s2 { color: #d14; }
.se { color: #d14; }
.sh { color: #d14; }
.si { color: #d14; }
.sx { color: #d14; }
.sr { color: #009926; }
.s1 { color: #d14; }
.ss { color: #990073; }
.bp { color: #999; }
.vc { color: teal; }
.vg { color: teal; }
.vi { color: teal; }
.il { color: #099; }
.gc { color: #999; background-color: #eaf2f5; }

View file

@ -6,19 +6,19 @@ p.details {
font-style: italic; font-style: italic;
color: #777 color: #777
} }
.footer p { .footer > p {
font-size: small; font-size: small;
color: #777 color: #777
} }
pre.commit-message { pre.commit-message {
white-space: pre-wrap; white-space: pre-wrap;
} }
.file-stats a { .file-stats > a {
text-decoration: none; text-decoration: none;
} > .new-file {
.file-stats .new-file { color: #090;
color: #090; }
} > .deleted-file {
.file-stats .deleted-file { color: #b00;
color: #b00; }
} }

View file

@ -95,6 +95,7 @@
.award-control { .award-control {
margin-right: 5px; margin-right: 5px;
margin-bottom: 5px;
padding-left: 5px; padding-left: 5px;
padding-right: 5px; padding-right: 5px;
line-height: 20px; line-height: 20px;
@ -108,7 +109,8 @@
} }
&.is-loading { &.is-loading {
.award-control-icon-normal { .award-control-icon-normal,
.emoji-icon {
display: none; display: none;
} }

View file

@ -51,7 +51,7 @@
.label-row { .label-row {
.label-name { .label-name {
display: inline-block; display: inline-block;
width: 200px; width: 170px;
@media (max-width: $screen-xs-min) { @media (max-width: $screen-xs-min) {
display: block; display: block;
@ -138,3 +138,51 @@
} }
} }
} }
.prioritized-labels {
margin-bottom: 30px;
.add-priority {
display: none;
color: $gray-light;
}
}
.other-labels {
.remove-priority {
display: none;
}
}
.toggle-priority {
display: inline-block;
vertical-align: middle;
button {
border-color: transparent;
padding: 5px 8px;
vertical-align: top;
font-size: 14px;
&:hover {
border-color: transparent;
}
}
}
.filtered-labels {
.label-row {
&:not(:last-child) {
margin-right: 5px;
}
}
.label-remove {
border-left: 1px solid rgba(0, 0, 0, .1);
z-index: 3;
}
.btn {
color: inherit;
}
}

View file

@ -79,11 +79,14 @@
} }
&.ci-failed, &.ci-failed,
&.ci-canceled,
&.ci-error { &.ci-error {
color: $gl-danger; color: $gl-danger;
} }
&.ci-canceled {
color: $gl-gray;
}
a.monospace { a.monospace {
color: inherit; color: inherit;
} }

View file

@ -87,6 +87,39 @@
} }
} }
.md-header .nav-links {
display: flex;
display: -webkit-flex;
flex-flow: row wrap;
-webkit-flex-flow: row wrap;
width: 100%;
.pull-right {
// Flexbox quirk to make sure right-aligned items stay right-aligned.
margin-left: auto;
}
}
.confidential-issue-warning {
background-color: $gray-normal;
border-radius: 3px;
padding: 3px 12px;
margin: auto;
margin-top: 0;
text-align: center;
font-size: 13px;
@media (max-width: $screen-md-min) {
// On smaller devices the warning becomes the fourth item in the list,
// rather than centering, and grows to span the full width of the
// comment area.
order: 4;
-webkit-order: 4;
margin: 6px auto;
width: 100%;
}
}
.discussion-form { .discussion-form {
padding: $gl-padding-top $gl-padding; padding: $gl-padding-top $gl-padding;
background-color: $white-light; background-color: $white-light;

View file

@ -69,6 +69,10 @@ ul.notes {
.note-edit-form { .note-edit-form {
display: block; display: block;
&.current-note-edit-form + .note-awards {
display: none;
}
} }
} }
@ -116,8 +120,41 @@ ul.notes {
} }
} }
.note-awards {
.js-awards-block {
padding: 2px;
margin-top: 10px;
}
.award-control {
font-size: 13px;
padding: 2px 5px;
}
}
.note-header { .note-header {
padding-bottom: 3px; padding-bottom: 3px;
padding-right: 20px;
@media (min-width: $screen-sm-min) {
padding-right: 0;
}
}
.note-emoji-button {
.fa-spinner {
display: none;
}
&.is-loading {
.fa-smile-o {
display: none;
}
.fa-spinner {
display: inline-block;
}
}
} }
} }
@ -179,6 +216,8 @@ ul.notes {
.discussion-header, .discussion-header,
.note-header { .note-header {
position: relative;
a { a {
color: inherit; color: inherit;
@ -215,6 +254,16 @@ ul.notes {
color: $notes-action-color; color: $notes-action-color;
} }
.note-actions {
position: absolute;
right: 0;
top: 0;
@media (min-width: $screen-sm-min) {
position: relative;
}
}
.discussion-actions { .discussion-actions {
@media (max-width: $screen-md-max) { @media (max-width: $screen-md-max) {
float: none; float: none;
@ -228,8 +277,13 @@ ul.notes {
.note-action-button { .note-action-button {
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 0;
line-height: 24px; line-height: 20px;
@media (min-width: $screen-sm-min) {
margin-left: 10px;
line-height: 24px;
}
.fa { .fa {
color: $notes-action-color; color: $notes-action-color;

View file

@ -32,6 +32,15 @@
.container-fluid { .container-fluid {
position: relative; position: relative;
@media (min-width: $screen-md-max) {
.row {
display: flex;
-ms-flex-align: center;
-webkit-align-items: center;
-webkit-box-align: center;
}
}
} }
.cover-controls { .cover-controls {
@ -57,7 +66,6 @@
max-width: 86px; max-width: 86px;
min-width: 86px; min-width: 86px;
padding-right: 0; padding-right: 0;
margin: 11px 0;
@media (max-width: $screen-md-max) { @media (max-width: $screen-md-max) {
padding-left: 0; padding-left: 0;
@ -489,9 +497,11 @@ pre.light-well {
margin: 0; margin: 0;
} }
.project-show-activity {
.activity-filter-block { .activity-filter-block {
margin-top: -1px; .controls {
padding-bottom: 10px;
border-bottom: 1px solid $border-color;
} }
} }

View file

@ -9,13 +9,22 @@ module ToggleAwardEmoji
name = params.require(:name) name = params.require(:name)
awardable.toggle_award_emoji(name, current_user) awardable.toggle_award_emoji(name, current_user)
TodoService.new.new_award_emoji(awardable, current_user) TodoService.new.new_award_emoji(to_todoable(awardable), current_user)
render json: { ok: true } render json: { ok: true }
end end
private private
def to_todoable(awardable)
case awardable
when Note
awardable.noteable
else
awardable
end
end
def awardable def awardable
raise NotImplementedError raise NotImplementedError
end end

View file

@ -42,46 +42,8 @@ class JwtController < ApplicationController
end end
def authenticate_user(login, password) def authenticate_user(login, password)
# TODO: this is a copy and paste from grack_auth, user = Gitlab::Auth.find_in_gitlab_or_ldap(login, password)
# it should be refactored in the future Gitlab::Auth.rate_limit!(request.ip, success: user.present?, login: login)
user = Gitlab::Auth.new.find(login, password)
# If the user authenticated successfully, we reset the auth failure count
# from Rack::Attack for that IP. A client may attempt to authenticate
# with a username and blank password first, and only after it receives
# a 401 error does it present a password. Resetting the count prevents
# false positives from occurring.
#
# Otherwise, we let Rack::Attack know there was a failed authentication
# attempt from this IP. This information is stored in the Rails cache
# (Redis) and will be used by the Rack::Attack middleware to decide
# whether to block requests from this IP.
config = Gitlab.config.rack_attack.git_basic_auth
if config.enabled
if user
# A successful login will reset the auth failure count from this IP
Rack::Attack::Allow2Ban.reset(request.ip, config)
else
banned = Rack::Attack::Allow2Ban.filter(request.ip, config) do
# Unless the IP is whitelisted, return true so that Allow2Ban
# increments the counter (stored in Rails.cache) for the IP
if config.ip_whitelist.include?(request.ip)
false
else
true
end
end
if banned
Rails.logger.info "IP #{request.ip} failed to login " \
"as #{login} but has been temporarily banned from Git auth"
return
end
end
end
user user
end end
end end

View file

@ -32,7 +32,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
def verify_user_oauth_applications_enabled def verify_user_oauth_applications_enabled
return if current_application_settings.user_oauth_applications? return if current_application_settings.user_oauth_applications?
redirect_to applications_profile_url redirect_to profile_path
end end
def set_index_vars def set_index_vars

View file

@ -26,9 +26,9 @@ class Projects::BuildsController < Projects::ApplicationController
end end
def show def show
@builds = @project.ci_commits.find_by_sha(@build.sha).builds.order('id DESC') @builds = @project.pipelines.find_by_sha(@build.sha).builds.order('id DESC')
@builds = @builds.where("id not in (?)", @build.id) @builds = @builds.where("id not in (?)", @build.id)
@commit = @build.commit @pipeline = @build.pipeline
respond_to do |format| respond_to do |format|
format.html format.html

View file

@ -99,12 +99,12 @@ class Projects::CommitController < Projects::ApplicationController
@commit ||= @project.commit(params[:id]) @commit ||= @project.commit(params[:id])
end end
def ci_commits def pipelines
@ci_commits ||= project.ci_commits.where(sha: commit.sha) @pipelines ||= project.pipelines.where(sha: commit.sha)
end end
def ci_builds def ci_builds
@ci_builds ||= Ci::Build.where(commit: ci_commits) @ci_builds ||= Ci::Build.where(pipeline: pipelines)
end end
def define_show_vars def define_show_vars
@ -117,8 +117,8 @@ class Projects::CommitController < Projects::ApplicationController
@diff_refs = [commit.parent || commit, commit] @diff_refs = [commit.parent || commit, commit]
@notes_count = commit.notes.count @notes_count = commit.notes.count
@statuses = CommitStatus.where(commit: ci_commits) @statuses = CommitStatus.where(pipeline: pipelines)
@builds = Ci::Build.where(commit: ci_commits) @builds = Ci::Build.where(pipeline: pipelines)
end end
def assign_change_commit_vars(mr_source_branch) def assign_change_commit_vars(mr_source_branch)

View file

@ -0,0 +1,145 @@
class Projects::GitHttpController < Projects::ApplicationController
attr_reader :user
skip_before_action :repository
before_action :authenticate_user
before_action :ensure_project_found!
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
def info_refs
if upload_pack? && upload_pack_allowed?
render_ok
elsif receive_pack? && receive_pack_allowed?
render_ok
else
render_not_found
end
end
# POST /foo/bar.git/git-upload-pack (git pull)
def git_upload_pack
if upload_pack? && upload_pack_allowed?
render_ok
else
render_not_found
end
end
# POST /foo/bar.git/git-receive-pack" (git push)
def git_receive_pack
if receive_pack? && receive_pack_allowed?
render_ok
else
render_not_found
end
end
private
def authenticate_user
return if project && project.public? && upload_pack?
authenticate_or_request_with_http_basic do |login, password|
auth_result = Gitlab::Auth.find(login, password, project: project, ip: request.ip)
if auth_result.type == :ci && upload_pack?
@ci = true
elsif auth_result.type == :oauth && !upload_pack?
# Not allowed
else
@user = auth_result.user
end
ci? || user
end
end
def ensure_project_found!
render_not_found if project.blank?
end
def project
return @project if defined?(@project)
project_id, _ = project_id_with_suffix
if project_id.blank?
@project = nil
else
@project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
end
end
# This method returns two values so that we can parse
# params[:project_id] (untrusted input!) in exactly one place.
def project_id_with_suffix
id = params[:project_id] || ''
%w[.wiki.git .git].each do |suffix|
if id.end_with?(suffix)
# Be careful to only remove the suffix from the end of 'id'.
# Accidentally removing it from the middle is how security
# vulnerabilities happen!
return [id.slice(0, id.length - suffix.length), suffix]
end
end
# Something is wrong with params[:project_id]; do not pass it on.
[nil, nil]
end
def upload_pack?
git_command == 'git-upload-pack'
end
def receive_pack?
git_command == 'git-receive-pack'
end
def git_command
if action_name == 'info_refs'
params[:service]
else
action_name.dasherize
end
end
def render_ok
render json: Gitlab::Workhorse.git_http_ok(repository, user)
end
def repository
_, suffix = project_id_with_suffix
if suffix == '.wiki.git'
project.wiki.repository
else
project.repository
end
end
def render_not_found
render text: 'Not Found', status: :not_found
end
def ci?
@ci.present?
end
def upload_pack_allowed?
return false unless Gitlab.config.gitlab_shell.upload_pack
if user
Gitlab::GitAccess.new(user, project).download_access_check.allowed?
else
ci? || project.public?
end
end
def receive_pack_allowed?
return false unless Gitlab.config.gitlab_shell.receive_pack
# Skip user authorization on upload request.
# It will be done by the pre-receive hook in the repository.
user.present?
end
end

View file

@ -5,13 +5,14 @@ class Projects::LabelsController < Projects::ApplicationController
before_action :label, only: [:edit, :update, :destroy] before_action :label, only: [:edit, :update, :destroy]
before_action :authorize_read_label! before_action :authorize_read_label!
before_action :authorize_admin_labels!, only: [ before_action :authorize_admin_labels!, only: [
:new, :create, :edit, :update, :generate, :destroy :new, :create, :edit, :update, :generate, :destroy, :remove_priority, :set_priorities
] ]
respond_to :js, :html respond_to :js, :html
def index def index
@labels = @project.labels.page(params[:page]) @labels = @project.labels.unprioritized.page(params[:page])
@prioritized_labels = @project.labels.prioritized
respond_to do |format| respond_to do |format|
format.html format.html
@ -71,6 +72,30 @@ class Projects::LabelsController < Projects::ApplicationController
end end
end end
def remove_priority
respond_to do |format|
if label.update_attribute(:priority, nil)
format.json { render json: label }
else
message = label.errors.full_messages.uniq.join('. ')
format.json { render json: { message: message }, status: :unprocessable_entity }
end
end
end
def set_priorities
Label.transaction do
params[:label_ids].each_with_index do |label_id, index|
label = @project.labels.find_by_id(label_id)
label.update_attribute(:priority, index) if label
end
end
respond_to do |format|
format.json { render json: { message: 'success' } }
end
end
protected protected
def module_enabled def module_enabled

View file

@ -58,9 +58,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html format.html
format.json { render json: @merge_request } format.json { render json: @merge_request }
format.diff { render text: @merge_request.to_diff } format.patch { render text: @merge_request.to_patch }
format.patch { render text: @merge_request.to_patch } format.diff do
headers.store(*Gitlab::Workhorse.send_git_diff(@project.repository,
@merge_request.diff_base_commit.id,
@merge_request.last_commit.id))
headers['Content-Disposition'] = 'inline'
head :ok
end
end end
end end
@ -120,8 +127,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare @diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
@diff_notes_disabled = true @diff_notes_disabled = true
@ci_commit = @merge_request.ci_commit @pipeline = @merge_request.pipeline
@statuses = @ci_commit.statuses if @ci_commit @statuses = @pipeline.statuses if @pipeline
@note_counts = Note.where(commit_id: @commits.map(&:id)). @note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count group(:commit_id).count
@ -200,7 +207,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present? && @merge_request.ci_commit && @merge_request.ci_commit.active? if params[:merge_when_build_succeeds].present? && @merge_request.pipeline && @merge_request.pipeline.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request) .execute(@merge_request)
@status = :merge_when_build_succeeds @status = :merge_when_build_succeeds
@ -231,10 +238,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def ci_status def ci_status
ci_commit = @merge_request.ci_commit pipeline = @merge_request.pipeline
if ci_commit if pipeline
status = ci_commit.status status = pipeline.status
coverage = ci_commit.try(:coverage) coverage = pipeline.try(:coverage)
status ||= "preparing" status ||= "preparing"
else else
@ -317,8 +324,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request_diff = @merge_request.merge_request_diff @merge_request_diff = @merge_request.merge_request_diff
@ci_commit = @merge_request.ci_commit @pipeline = @merge_request.pipeline
@statuses = @ci_commit.statuses if @ci_commit @statuses = @pipeline.statuses if @pipeline
if @merge_request.locked_long_ago? if @merge_request.locked_long_ago?
@merge_request.unlock_mr @merge_request.unlock_mr
@ -327,8 +334,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def define_widget_vars def define_widget_vars
@ci_commit = @merge_request.ci_commit @pipeline = @merge_request.pipeline
@ci_commits = [@ci_commit].compact @pipelines = [@pipeline].compact
closes_issues closes_issues
end end

View file

@ -1,4 +1,6 @@
class Projects::NotesController < Projects::ApplicationController class Projects::NotesController < Projects::ApplicationController
include ToggleAwardEmoji
# Authorize # Authorize
before_action :authorize_read_note! before_action :authorize_read_note!
before_action :authorize_create_note!, only: [:create] before_action :authorize_create_note!, only: [:create]
@ -61,6 +63,7 @@ class Projects::NotesController < Projects::ApplicationController
def note def note
@note ||= @project.notes.find(params[:id]) @note ||= @project.notes.find(params[:id])
end end
alias_method :awardable, :note
def note_to_html(note) def note_to_html(note)
render_to_string( render_to_string(

View file

@ -7,7 +7,7 @@ class Projects::PipelinesController < Projects::ApplicationController
def index def index
@scope = params[:scope] @scope = params[:scope]
all_pipelines = project.ci_commits all_pipelines = project.pipelines
@pipelines_count = all_pipelines.count @pipelines_count = all_pipelines.count
@running_or_pending_count = all_pipelines.running_or_pending.count @running_or_pending_count = all_pipelines.running_or_pending.count
@pipelines = PipelinesFinder.new(project).execute(all_pipelines, @scope) @pipelines = PipelinesFinder.new(project).execute(all_pipelines, @scope)
@ -15,7 +15,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def new def new
@pipeline = project.ci_commits.new(ref: @project.default_branch) @pipeline = project.pipelines.new(ref: @project.default_branch)
end end
def create def create
@ -50,7 +50,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def pipeline def pipeline
@pipeline ||= project.ci_commits.find_by!(id: params[:id]) @pipeline ||= project.pipelines.find_by!(id: params[:id])
end end
def commit def commit

View file

@ -224,7 +224,7 @@ class IssuableFinder
def sort(items) def sort(items)
# Ensure we always have an explicit sort order (instead of inheriting # Ensure we always have an explicit sort order (instead of inheriting
# multiple orders when combining ActiveRecord::Relation objects). # multiple orders when combining ActiveRecord::Relation objects).
params[:sort] ? items.sort(params[:sort]) : items.reorder(id: :desc) params[:sort] ? items.sort(params[:sort], excluded_labels: label_names) : items.reorder(id: :desc)
end end
def by_assignee(items) def by_assignee(items)
@ -318,7 +318,11 @@ class IssuableFinder
end end
def label_names def label_names
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name] if labels?
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
else
[]
end
end end
def current_user_related? def current_user_related?

View file

@ -1,7 +1,7 @@
module CiStatusHelper module CiStatusHelper
def ci_status_path(ci_commit) def ci_status_path(pipeline)
project = ci_commit.project project = pipeline.project
builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha) builds_namespace_project_commit_path(project.namespace, project, pipeline.sha)
end end
def ci_status_with_icon(status, target = nil) def ci_status_with_icon(status, target = nil)

View file

@ -8,14 +8,6 @@ module IssuablesHelper
"right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}" "right-sidebar-#{sidebar_gutter_collapsed? ? 'collapsed' : 'expanded'}"
end end
def issuables_count(issuable)
base_issuable_scope(issuable).maximum(:iid)
end
def next_issuable_for(issuable)
base_issuable_scope(issuable).where('iid > ?', issuable.iid).last
end
def multi_label_name(current_labels, default_label) def multi_label_name(current_labels, default_label)
# current_labels may be a string from before # current_labels may be a string from before
if current_labels.is_a?(Array) if current_labels.is_a?(Array)
@ -45,10 +37,6 @@ module IssuablesHelper
end end
end end
def prev_issuable_for(issuable)
base_issuable_scope(issuable).where('iid < ?', issuable.iid).first
end
def user_dropdown_label(user_id, default_label) def user_dropdown_label(user_id, default_label)
return default_label if user_id.nil? return default_label if user_id.nil?
return "Unassigned" if user_id == "0" return "Unassigned" if user_id == "0"

View file

@ -31,6 +31,21 @@ module NotificationsHelper
end end
end end
def notification_description(level)
case level.to_sym
when :participating
'You will only receive notifications from related resources'
when :mention
'You will receive notifications only for comments in which you were @mentioned'
when :watch
'You will receive notifications for any activity'
when :disabled
'You will not get any notifications via email'
when :global
'Use your global notification setting'
end
end
def notification_list_item(level, setting) def notification_list_item(level, setting)
title = notification_title(level) title = notification_title(level)
@ -39,9 +54,10 @@ module NotificationsHelper
notification_title: title notification_title: title
} }
content_tag(:li, class: ('active' if setting.level == level)) do content_tag(:li, role: "menuitem") do
link_to '#', class: 'update-notification', data: data do link_to '#', class: "update-notification #{('is-active' if setting.level == level)}", data: data do
notification_icon(level, title) link_output = content_tag(:strong, title, class: 'dropdown-menu-inner-title')
link_output << content_tag(:span, notification_description(level), class: 'dropdown-menu-inner-content')
end end
end end
end end

View file

@ -14,7 +14,8 @@ module SortingHelper
sort_value_recently_signin => sort_title_recently_signin, sort_value_recently_signin => sort_title_recently_signin,
sort_value_oldest_signin => sort_title_oldest_signin, sort_value_oldest_signin => sort_title_oldest_signin,
sort_value_downvotes => sort_title_downvotes, sort_value_downvotes => sort_title_downvotes,
sort_value_upvotes => sort_title_upvotes sort_value_upvotes => sort_title_upvotes,
sort_value_priority => sort_title_priority
} }
end end
@ -28,6 +29,10 @@ module SortingHelper
} }
end end
def sort_title_priority
'Priority'
end
def sort_title_oldest_updated def sort_title_oldest_updated
'Oldest updated' 'Oldest updated'
end end
@ -84,6 +89,10 @@ module SortingHelper
'Most popular' 'Most popular'
end end
def sort_value_priority
'priority'
end
def sort_value_oldest_updated def sort_value_oldest_updated
'updated_asc' 'updated_asc'
end end

View file

@ -45,8 +45,8 @@ module Ci
new_build.options = build.options new_build.options = build.options
new_build.commands = build.commands new_build.commands = build.commands
new_build.tag_list = build.tag_list new_build.tag_list = build.tag_list
new_build.gl_project_id = build.gl_project_id new_build.project = build.project
new_build.commit_id = build.commit_id new_build.pipeline = build.pipeline
new_build.name = build.name new_build.name = build.name
new_build.allow_failure = build.allow_failure new_build.allow_failure = build.allow_failure
new_build.stage = build.stage new_build.stage = build.stage
@ -66,7 +66,7 @@ module Ci
# We use around_transition to create builds for next stage as soon as possible, before the `after_*` is executed # We use around_transition to create builds for next stage as soon as possible, before the `after_*` is executed
around_transition any => [:success, :failed, :canceled] do |build, block| around_transition any => [:success, :failed, :canceled] do |build, block|
block.call block.call
build.commit.create_next_builds(build) if build.commit build.pipeline.create_next_builds(build) if build.pipeline
end end
after_transition any => [:success, :failed, :canceled] do |build| after_transition any => [:success, :failed, :canceled] do |build|
@ -80,7 +80,7 @@ module Ci
end end
def retried? def retried?
!self.commit.statuses.latest.include?(self) !self.pipeline.statuses.latest.include?(self)
end end
def retry def retry
@ -89,7 +89,7 @@ module Ci
def depends_on_builds def depends_on_builds
# Get builds of the same type # Get builds of the same type
latest_builds = self.commit.builds.latest latest_builds = self.pipeline.builds.latest
# Return builds from previous stages # Return builds from previous stages
latest_builds.where('stage_idx < ?', stage_idx) latest_builds.where('stage_idx < ?', stage_idx)
@ -114,16 +114,16 @@ module Ci
def merge_request def merge_request
merge_requests = MergeRequest.includes(:merge_request_diff) merge_requests = MergeRequest.includes(:merge_request_diff)
.where(source_branch: ref, source_project_id: commit.gl_project_id) .where(source_branch: ref, source_project_id: pipeline.gl_project_id)
.reorder(iid: :asc) .reorder(iid: :asc)
merge_requests.find do |merge_request| merge_requests.find do |merge_request|
merge_request.commits.any? { |ci| ci.id == commit.sha } merge_request.commits.any? { |ci| ci.id == pipeline.sha }
end end
end end
def project_id def project_id
commit.project.id pipeline.project_id
end end
def project_name def project_name
@ -360,8 +360,8 @@ module Ci
end end
def global_yaml_variables def global_yaml_variables
if commit.config_processor if pipeline.config_processor
commit.config_processor.global_variables.map do |key, value| pipeline.config_processor.global_variables.map do |key, value|
{ key: key, value: value, public: true } { key: key, value: value, public: true }
end end
else else
@ -370,8 +370,8 @@ module Ci
end end
def job_yaml_variables def job_yaml_variables
if commit.config_processor if pipeline.config_processor
commit.config_processor.job_variables(name).map do |key, value| pipeline.config_processor.job_variables(name).map do |key, value|
{ key: key, value: value, public: true } { key: key, value: value, public: true }
end end
else else

View file

@ -1,12 +1,14 @@
module Ci module Ci
class Commit < ActiveRecord::Base class Pipeline < ActiveRecord::Base
extend Ci::Model extend Ci::Model
include Statuseable include Statuseable
self.table_name = 'ci_commits'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
has_many :statuses, class_name: 'CommitStatus' has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
validates_presence_of :sha validates_presence_of :sha
validates_presence_of :status validates_presence_of :status
@ -21,7 +23,7 @@ module Ci
def self.stages def self.stages
# We use pluck here due to problems with MySQL which doesn't allow LIMIT/OFFSET in queries # We use pluck here due to problems with MySQL which doesn't allow LIMIT/OFFSET in queries
CommitStatus.where(commit: pluck(:id)).stages CommitStatus.where(pipeline: pluck(:id)).stages
end end
def project_id def project_id
@ -47,7 +49,7 @@ module Ci
end end
def short_sha def short_sha
Ci::Commit.truncate_sha(sha) Ci::Pipeline.truncate_sha(sha)
end end
def commit_data def commit_data

View file

@ -3,7 +3,7 @@ module Ci
extend Ci::Model extend Ci::Model
belongs_to :trigger, class_name: 'Ci::Trigger' belongs_to :trigger, class_name: 'Ci::Trigger'
belongs_to :commit, class_name: 'Ci::Commit' belongs_to :commit, class_name: 'Ci::Pipeline', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
serialize :variables serialize :variables

View file

@ -214,13 +214,13 @@ class Commit
@raw.short_id(7) @raw.short_id(7)
end end
def ci_commits def pipelines
@ci_commits ||= project.ci_commits.where(sha: sha) @pipeline ||= project.pipelines.where(sha: sha)
end end
def status def status
return @status if defined?(@status) return @status if defined?(@status)
@status ||= ci_commits.status @status ||= pipelines.status
end end
def revert_branch_name def revert_branch_name

View file

@ -4,10 +4,10 @@ class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds' self.table_name = 'ci_builds'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
belongs_to :commit, class_name: 'Ci::Commit', touch: true belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, touch: true
belongs_to :user belongs_to :user
validates :commit, presence: true validates :pipeline, presence: true
validates_presence_of :name validates_presence_of :name
@ -44,18 +44,18 @@ class CommitStatus < ActiveRecord::Base
end end
after_transition [:pending, :running] => :success do |commit_status| after_transition [:pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status) MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
end end
after_transition any => :failed do |commit_status| after_transition any => :failed do |commit_status|
MergeRequests::AddTodoWhenBuildFailsService.new(commit_status.commit.project, nil).execute(commit_status) MergeRequests::AddTodoWhenBuildFailsService.new(commit_status.pipeline.project, nil).execute(commit_status)
end end
end end
delegate :sha, :short_sha, to: :commit delegate :sha, :short_sha, to: :pipeline
def before_sha def before_sha
commit.before_sha || Gitlab::Git::BLANK_SHA pipeline.before_sha || Gitlab::Git::BLANK_SHA
end end
def self.stages def self.stages

View file

@ -105,17 +105,24 @@ module Issuable
where(t[:title].matches(pattern).or(t[:description].matches(pattern))) where(t[:title].matches(pattern).or(t[:description].matches(pattern)))
end end
def sort(method) def sort(method, excluded_labels: [])
case method.to_s case method.to_s
when 'milestone_due_asc' then order_milestone_due_asc when 'milestone_due_asc' then order_milestone_due_asc
when 'milestone_due_desc' then order_milestone_due_desc when 'milestone_due_desc' then order_milestone_due_desc
when 'downvotes_desc' then order_downvotes_desc when 'downvotes_desc' then order_downvotes_desc
when 'upvotes_desc' then order_upvotes_desc when 'upvotes_desc' then order_upvotes_desc
when 'priority' then order_labels_priority(excluded_labels: excluded_labels)
else else
order_by(method) order_by(method)
end end
end end
def order_labels_priority(excluded_labels: [])
select("#{table_name}.*, (#{highest_label_priority(excluded_labels).to_sql}) AS highest_priority").
group(arel_table[:id]).
reorder(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
end
def with_label(title, sort = nil) def with_label(title, sort = nil)
if title.is_a?(Array) && title.size > 1 if title.is_a?(Array) && title.size > 1
joins(:labels).where(labels: { title: title }).group(*grouping_columns(sort)).having("COUNT(DISTINCT labels.title) = #{title.size}") joins(:labels).where(labels: { title: title }).group(*grouping_columns(sort)).having("COUNT(DISTINCT labels.title) = #{title.size}")
@ -139,6 +146,20 @@ module Issuable
grouping_columns grouping_columns
end end
private
def highest_label_priority(excluded_labels)
query = Label.select(Label.arel_table[:priority].minimum).
joins(:label_links).
where(label_links: { target_type: name }).
where("label_links.target_id = #{table_name}.id").
reorder(nil)
query.where.not(title: excluded_labels) if excluded_labels.present?
query
end
end end
def today? def today?

View file

@ -75,10 +75,10 @@ class Issue < ActiveRecord::Base
@link_reference_pattern ||= super("issues", /(?<issue>\d+)/) @link_reference_pattern ||= super("issues", /(?<issue>\d+)/)
end end
def self.sort(method) def self.sort(method, excluded_labels: [])
case method.to_s case method.to_s
when 'due_date_asc' then order_due_date_asc when 'due_date_asc' then order_due_date_asc
when 'due_date_desc' then order_due_date_desc when 'due_date_desc' then order_due_date_desc
else else
super super
end end

View file

@ -26,10 +26,20 @@ class Label < ActiveRecord::Base
format: { with: /\A[^&\?,]+\z/ }, format: { with: /\A[^&\?,]+\z/ },
uniqueness: { scope: :project_id } uniqueness: { scope: :project_id }
before_save :nullify_priority
default_scope { order(title: :asc) } default_scope { order(title: :asc) }
scope :templates, -> { where(template: true) } scope :templates, -> { where(template: true) }
def self.prioritized
where.not(priority: nil).reorder(:priority, :title)
end
def self.unprioritized
where(priority: nil)
end
alias_attribute :name, :title alias_attribute :name, :title
def self.reference_prefix def self.reference_prefix
@ -118,4 +128,8 @@ class Label < ActiveRecord::Base
id id
end end
end end
def nullify_priority
self.priority = nil if priority.blank?
end
end end

View file

@ -313,13 +313,6 @@ class MergeRequest < ActiveRecord::Base
) )
end end
# Returns the raw diff for this merge request
#
# see "git diff"
def to_diff
target_project.repository.diff_text(diff_base_commit.sha, source_sha)
end
# Returns the commit as a series of email patches. # Returns the commit as a series of email patches.
# #
# see "git format-patch" # see "git format-patch"
@ -579,8 +572,8 @@ class MergeRequest < ActiveRecord::Base
diverged_commits_count > 0 diverged_commits_count > 0
end end
def ci_commit def pipeline
@ci_commit ||= source_project.ci_commit(last_commit.id, source_branch) if last_commit && source_project @pipeline ||= source_project.pipeline(last_commit.id, source_branch) if last_commit && source_project
end end
def diff_refs def diff_refs

View file

@ -3,6 +3,7 @@ class Note < ActiveRecord::Base
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include Participable include Participable
include Mentionable include Mentionable
include Awardable
default_value_for :system, false default_value_for :system, false

View file

@ -1,5 +1,5 @@
class NotificationSetting < ActiveRecord::Base class NotificationSetting < ActiveRecord::Base
enum level: { disabled: 0, participating: 1, watch: 2, global: 3, mention: 4 } enum level: { global: 3, watch: 2, mention: 4, participating: 1, disabled: 0 }
default_value_for :level, NotificationSetting.levels[:global] default_value_for :level, NotificationSetting.levels[:global]

View file

@ -119,7 +119,7 @@ class Project < ActiveRecord::Base
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id
has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id
has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id
has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
@ -930,12 +930,12 @@ class Project < ActiveRecord::Base
!namespace.share_with_group_lock !namespace.share_with_group_lock
end end
def ci_commit(sha, ref) def pipeline(sha, ref)
ci_commits.order(id: :desc).find_by(sha: sha, ref: ref) pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
end end
def ensure_ci_commit(sha, ref) def ensure_pipeline(sha, ref)
ci_commit(sha, ref) || ci_commits.create(sha: sha, ref: ref) pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref)
end end
def enable_ci def enable_ci

View file

@ -1,11 +1,11 @@
module Ci module Ci
class CreateBuildsService class CreateBuildsService
def initialize(commit) def initialize(pipeline)
@commit = commit @pipeline = pipeline
end end
def execute(stage, user, status, trigger_request = nil) def execute(stage, user, status, trigger_request = nil)
builds_attrs = config_processor.builds_for_stage_and_ref(stage, @commit.ref, @commit.tag, trigger_request) builds_attrs = config_processor.builds_for_stage_and_ref(stage, @pipeline.ref, @pipeline.tag, trigger_request)
# check when to create next build # check when to create next build
builds_attrs = builds_attrs.select do |build_attrs| builds_attrs = builds_attrs.select do |build_attrs|
@ -21,8 +21,8 @@ module Ci
builds_attrs.map do |build_attrs| builds_attrs.map do |build_attrs|
# don't create the same build twice # don't create the same build twice
unless @commit.builds.find_by(ref: @commit.ref, tag: @commit.tag, unless @pipeline.builds.find_by(ref: @pipeline.ref, tag: @pipeline.tag,
trigger_request: trigger_request, name: build_attrs[:name]) trigger_request: trigger_request, name: build_attrs[:name])
build_attrs.slice!(:name, build_attrs.slice!(:name,
:commands, :commands,
:tag_list, :tag_list,
@ -31,13 +31,13 @@ module Ci
:stage, :stage,
:stage_idx) :stage_idx)
build_attrs.merge!(ref: @commit.ref, build_attrs.merge!(ref: @pipeline.ref,
tag: @commit.tag, tag: @pipeline.tag,
trigger_request: trigger_request, trigger_request: trigger_request,
user: user, user: user,
project: @commit.project) project: @pipeline.project)
@commit.builds.create!(build_attrs) @pipeline.builds.create!(build_attrs)
end end
end end
end end
@ -45,7 +45,7 @@ module Ci
private private
def config_processor def config_processor
@config_processor ||= @commit.config_processor @config_processor ||= @pipeline.config_processor
end end
end end
end end

View file

@ -1,7 +1,7 @@
module Ci module Ci
class CreatePipelineService < BaseService class CreatePipelineService < BaseService
def execute def execute
pipeline = project.ci_commits.new(params) pipeline = project.pipelines.new(params)
unless ref_names.include?(params[:ref]) unless ref_names.include?(params[:ref])
pipeline.errors.add(:base, 'Reference not found') pipeline.errors.add(:base, 'Reference not found')
@ -19,7 +19,7 @@ module Ci
end end
begin begin
Ci::Commit.transaction do Ci::Pipeline.transaction do
pipeline.sha = commit.id pipeline.sha = commit.id
unless pipeline.config_processor unless pipeline.config_processor

View file

@ -7,14 +7,14 @@ module Ci
# check if ref is tag # check if ref is tag
tag = project.repository.find_tag(ref).present? tag = project.repository.find_tag(ref).present?
ci_commit = project.ci_commits.create(sha: commit.sha, ref: ref, tag: tag) pipeline = project.pipelines.create(sha: commit.sha, ref: ref, tag: tag)
trigger_request = trigger.trigger_requests.create!( trigger_request = trigger.trigger_requests.create!(
variables: variables, variables: variables,
commit: ci_commit, commit: pipeline,
) )
if ci_commit.create_builds(nil, trigger_request) if pipeline.create_builds(nil, trigger_request)
trigger_request trigger_request
end end
end end

View file

@ -3,9 +3,9 @@ module Ci
def execute(project, opts) def execute(project, opts)
sha = opts[:sha] || ref_sha(project, opts[:ref]) sha = opts[:sha] || ref_sha(project, opts[:ref])
ci_commits = project.ci_commits.where(sha: sha) pipelines = project.pipelines.where(sha: sha)
ci_commits = ci_commits.where(ref: opts[:ref]) if opts[:ref] pipelines = pipelines.where(ref: opts[:ref]) if opts[:ref]
image_name = image_for_status(ci_commits.status) image_name = image_for_status(pipelines.status)
image_path = Rails.root.join('public/ci', image_name) image_path = Rails.root.join('public/ci', image_name)
OpenStruct.new(path: image_path, name: image_name) OpenStruct.new(path: image_path, name: image_name)

View file

@ -18,23 +18,23 @@ class CreateCommitBuildsService
return false return false
end end
commit = Ci::Commit.new(project: project, sha: sha, ref: ref, before_sha: before_sha, tag: tag) pipeline = Ci::Pipeline.new(project: project, sha: sha, ref: ref, before_sha: before_sha, tag: tag)
# Skip creating ci_commit when no gitlab-ci.yml is found # Skip creating pipeline when no gitlab-ci.yml is found
unless commit.ci_yaml_file unless pipeline.ci_yaml_file
return false return false
end end
# Create a new ci_commit # Create a new pipeline
commit.save! pipeline.save!
# Skip creating builds for commits that have [ci skip] # Skip creating builds for commits that have [ci skip]
unless commit.skip_ci? unless pipeline.skip_ci?
# Create builds for commit # Create builds for commit
commit.create_builds(user) pipeline.create_builds(user)
end end
commit.touch pipeline.touch
commit pipeline
end end
end end

View file

@ -55,12 +55,12 @@ module MergeRequests
def each_merge_request(commit_status) def each_merge_request(commit_status)
merge_request_from(commit_status).each do |merge_request| merge_request_from(commit_status).each do |merge_request|
ci_commit = merge_request.ci_commit pipeline = merge_request.pipeline
next unless ci_commit next unless pipeline
next unless ci_commit.sha == commit_status.sha next unless pipeline.sha == commit_status.sha
yield merge_request, ci_commit yield merge_request, pipeline
end end
end end
end end

View file

@ -20,10 +20,10 @@ module MergeRequests
# Triggers the automatic merge of merge_request once the build succeeds # Triggers the automatic merge of merge_request once the build succeeds
def trigger(commit_status) def trigger(commit_status)
each_merge_request(commit_status) do |merge_request, ci_commit| each_merge_request(commit_status) do |merge_request, pipeline|
next unless merge_request.merge_when_build_succeeds? next unless merge_request.merge_when_build_succeeds?
next unless merge_request.mergeable? next unless merge_request.mergeable?
next unless ci_commit.success? next unless pipeline.success?
MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params) MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
end end

View file

@ -99,8 +99,8 @@
%td.build-link %td.build-link
- if project - if project
= link_to ci_status_path(build.commit) do = link_to ci_status_path(build.pipeline) do
%strong #{build.commit.short_sha} %strong #{build.pipeline.short_sha}
%td.timestamp %td.timestamp
- if build.finished_at - if build.finished_at

View file

@ -11,7 +11,7 @@
gl.awardMenuUrl = "#{emojis_path}" gl.awardMenuUrl = "#{emojis_path}"
.award-menu-holder.js-award-holder .award-menu-holder.js-award-holder
%button.btn.award-control.js-add-award{ type: "button", data: { award_menu_url: emojis_path } } %button.btn.award-control.js-add-award{ type: "button" }
= icon('smile-o', class: "award-control-icon award-control-icon-normal") = icon('smile-o', class: "award-control-icon award-control-icon-normal")
= icon('spinner spin', class: "award-control-icon award-control-icon-loading") = icon('spinner spin', class: "award-control-icon award-control-icon-loading")
%span.award-control-text %span.award-control-text

View file

@ -1,10 +1,14 @@
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
%span.event_label{class: event.action_name} %span.event_label{class: event.action_name}
= event_action_name(event)
- if event.target - if event.target
%strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title = event.action_name
%strong
= link_to [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title do
= event.target_type.titleize.downcase
= event.target.reference_link_text
- else
= event_action_name(event)
= event_preposition(event) = event_preposition(event)

View file

@ -4,6 +4,10 @@
%i.fa.fa-github %i.fa.fa-github
Import projects from GitHub Import projects from GitHub
%p
%i.fa.fa-warning
To import GitHub pull requests, any pull request source branches that had been deleted are temporarily restored on GitHub. To prevent any connected CI services from being overloaded with dozens of irrelevant branches being created and deleted again, GitHub webhooks are temporarily disabled during the import process.
%p.light %p.light
Select projects you want to import. Select projects you want to import.
%hr %hr

View file

@ -1,11 +1,9 @@
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" } .page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class } .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo = link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do
%a#logo .header-logo
= brand_header_logo #logo
= link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do = brand_header_logo
.gitlab-text-container
%h3 GitLab
- if defined?(sidebar) && sidebar - if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}" = render "layouts/nav/#{sidebar}"
@ -17,10 +15,8 @@
.collapse-nav .collapse-nav
= render partial: 'layouts/collapse_button' = render partial: 'layouts/collapse_button'
- if current_user - if current_user
= link_to current_user, class: 'sidebar-user', title: "Profile" do = link_to current_user, class: 'sidebar-user', title: "Profile", data: {user: current_user.username} do
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s46'
.username
= current_user.username
- if defined?(nav) && nav - if defined?(nav) && nav
.layout-nav .layout-nav
.container-fluid .container-fluid

View file

@ -2,54 +2,50 @@
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
= icon('bookmark fw') = icon('bookmark fw')
%span .nav-link-text
Projects Projects
= nav_link(controller: :todos) do = nav_link(controller: :todos) do
= link_to dashboard_todos_path, title: 'Todos' do = link_to dashboard_todos_path, title: 'Todos' do
= icon('bell fw') = icon('bell fw')
%span .nav-link-text
Todos Todos
%span.count.todos-pending-count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do = nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
= icon('dashboard fw') = icon('dashboard fw')
%span .nav-link-text
Activity Activity
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, title: 'Groups' do = link_to dashboard_groups_path, title: 'Groups' do
= icon('group fw') = icon('group fw')
%span .nav-link-text
Groups Groups
= nav_link(controller: 'dashboard/milestones') do = nav_link(controller: 'dashboard/milestones') do
= link_to dashboard_milestones_path, title: 'Milestones' do = link_to dashboard_milestones_path, title: 'Milestones' do
= icon('clock-o fw') = icon('clock-o fw')
%span .nav-link-text
Milestones Milestones
= nav_link(path: 'dashboard#issues') do = nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
= icon('exclamation-circle fw') = icon('exclamation-circle fw')
%span .nav-link-text
Issues Issues
%span.count= number_with_delimiter(current_user.assigned_open_issues_count)
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
= icon('tasks fw') = icon('tasks fw')
%span .nav-link-text
Merge Requests Merge Requests
%span.count= number_with_delimiter(current_user.assigned_open_merge_request_count)
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do = link_to dashboard_snippets_path, title: 'Snippets' do
= icon('clipboard fw') = icon('clipboard fw')
%span .nav-link-text
Snippets Snippets
= nav_link(controller: :help) do = nav_link(controller: :help) do
= link_to help_path, title: 'Help' do = link_to help_path, title: 'Help' do
= icon('question-circle fw') = icon('question-circle fw')
%span .nav-link-text
Help Help
= nav_link(html_options: {class: profile_tab_class}) do = nav_link(html_options: {class: profile_tab_class}) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
= icon('user fw') = icon('user fw')
%span .nav-link-text
Profile Settings Profile Settings

View file

@ -10,11 +10,12 @@
= icon('gear fw') = icon('gear fw')
%span %span
Account Account
= nav_link(controller: 'oauth/applications') do - if current_application_settings.user_oauth_applications?
= link_to applications_profile_path, title: 'Applications' do = nav_link(controller: 'oauth/applications') do
= icon('cloud fw') = link_to applications_profile_path, title: 'Applications' do
%span = icon('cloud fw')
Applications %span
Applications
= nav_link(controller: :emails) do = nav_link(controller: :emails) do
= link_to profile_emails_path, title: 'Emails' do = link_to profile_emails_path, title: 'Emails' do
= icon('envelope-o fw') = icon('envelope-o fw')

View file

@ -10,7 +10,7 @@
%p %p
Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)} Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
%p %p
Author: #{@build.commit.git_author_name} Author: #{@build.pipeline.git_author_name}
%p %p
Branch: #{@build.ref} Branch: #{@build.ref}
%p %p
@ -18,7 +18,7 @@
%p %p
Job: #{@build.name} Job: #{@build.name}
%p %p
Message: #{@build.commit.git_commit_message} Message: #{@build.pipeline.git_commit_message}
%p %p
Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}

View file

@ -1,11 +1,11 @@
Build failed for <%= @project.name %> Build failed for <%= @project.name %>
Status: <%= @build.status %> Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %> Commit: <%= @build.pipeline.short_sha %>
Author: <%= @build.commit.git_author_name %> Author: <%= @build.pipeline.git_author_name %>
Branch: <%= @build.ref %> Branch: <%= @build.ref %>
Stage: <%= @build.stage %> Stage: <%= @build.stage %>
Job: <%= @build.name %> Job: <%= @build.name %>
Message: <%= @build.commit.git_commit_message %> Message: <%= @build.pipeline.git_commit_message %>
Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %> Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>

View file

@ -10,7 +10,7 @@
%p %p
Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)} Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
%p %p
Author: #{@build.commit.git_author_name} Author: #{@build.pipeline.git_author_name}
%p %p
Branch: #{@build.ref} Branch: #{@build.ref}
%p %p
@ -18,7 +18,7 @@
%p %p
Job: #{@build.name} Job: #{@build.name}
%p %p
Message: #{@build.commit.git_commit_message} Message: #{@build.pipeline.git_commit_message}
%p %p
Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}

View file

@ -1,11 +1,11 @@
Build successful for <%= @project.name %> Build successful for <%= @project.name %>
Status: <%= @build.status %> Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %> Commit: <%= @build.pipeline.short_sha %>
Author: <%= @build.commit.git_author_name %> Author: <%= @build.pipeline.git_author_name %>
Branch: <%= @build.ref %> Branch: <%= @build.ref %>
Stage: <%= @build.stage %> Stage: <%= @build.stage %>
Job: <%= @build.name %> Job: <%= @build.name %>
Message: <%= @build.commit.git_commit_message %> Message: <%= @build.pipeline.git_commit_message %>
Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %> Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>

View file

@ -29,10 +29,10 @@
.project-clone-holder .project-clone-holder
= render "shared/clone_panel" = render "shared/clone_panel"
.project-repo-buttons.btn-group.project-right-buttons .project-repo-buttons.btn-group.project-right-buttons
= render "projects/buttons/download" = render "projects/buttons/download"
= render 'projects/buttons/dropdown' = render 'projects/buttons/dropdown'
= render 'projects/buttons/notifications' = render 'projects/buttons/notifications'
:javascript :javascript
new Star(); new Star();

View file

@ -7,6 +7,12 @@
%li %li
%a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 } %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
Preview Preview
- if defined?(@issue) && @issue.confidential?
%li.confidential-issue-warning
= icon('warning')
%span This is a confidential issue. Your comment will not be visible to the public.
%li.pull-right %li.pull-right
%button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 } %button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
Go full screen Go full screen

View file

@ -1,32 +1,35 @@
- @no_container = true
- page_title "Branches" - page_title "Branches"
= render "projects/commits/head" = render "projects/commits/head"
.row-content-block
.pull-right %div{ class: (container_class) }
- if can? current_user, :push_code, @project .row-content-block.second-block.content-component-block
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do .pull-right
= icon('plus') - if can? current_user, :push_code, @project
New branch = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
&nbsp; = icon('plus')
.dropdown.inline New branch
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} &nbsp;
%span.light .dropdown.inline
- if @sort.present? %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
= @sort.humanize %span.light
- else - if @sort.present?
Name = @sort.humanize
%b.caret - else
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to namespace_project_branches_path(sort: nil) do
Name Name
= link_to namespace_project_branches_path(sort: 'recently_updated') do %b.caret
= sort_title_recently_updated %ul.dropdown-menu.dropdown-menu-align-right
= link_to namespace_project_branches_path(sort: 'last_updated') do %li
= sort_title_oldest_updated = link_to namespace_project_branches_path(sort: nil) do
.oneline Name
Protected branches can be managed in project settings = link_to namespace_project_branches_path(sort: 'recently_updated') do
- unless @branches.empty? = sort_title_recently_updated
%ul.content-list.all-branches = link_to namespace_project_branches_path(sort: 'last_updated') do
- @branches.each do |branch| = sort_title_oldest_updated
= render "projects/branches/branch", branch: branch .oneline
= paginate @branches, theme: 'gitlab' Protected branches can be managed in project settings
- unless @branches.empty?
%ul.content-list.all-branches
- @branches.each do |branch|
= render "projects/branches/branch", branch: branch
= paginate @branches, theme: 'gitlab'

View file

@ -1,62 +1,64 @@
- @no_container = true
- page_title "Builds" - page_title "Builds"
= render "projects/pipelines/head" = render "projects/pipelines/head"
.top-area %div{ class: (container_class) }
%ul.nav-links .top-area
%li{class: ('active' if @scope.nil?)} %ul.nav-links
= link_to project_builds_path(@project) do %li{class: ('active' if @scope.nil?)}
All = link_to project_builds_path(@project) do
%span.badge.js-totalbuilds-count All
= number_with_delimiter(@all_builds.count(:id)) %span.badge.js-totalbuilds-count
= number_with_delimiter(@all_builds.count(:id))
%li{class: ('active' if @scope == 'running')} %li{class: ('active' if @scope == 'running')}
= link_to project_builds_path(@project, scope: :running) do = link_to project_builds_path(@project, scope: :running) do
Running Running
%span.badge.js-running-count %span.badge.js-running-count
= number_with_delimiter(@all_builds.running_or_pending.count(:id)) = number_with_delimiter(@all_builds.running_or_pending.count(:id))
%li{class: ('active' if @scope == 'finished')} %li{class: ('active' if @scope == 'finished')}
= link_to project_builds_path(@project, scope: :finished) do = link_to project_builds_path(@project, scope: :finished) do
Finished Finished
%span.badge.js-running-count %span.badge.js-running-count
= number_with_delimiter(@all_builds.finished.count(:id)) = number_with_delimiter(@all_builds.finished.count(:id))
.nav-controls .nav-controls
- if can?(current_user, :update_build, @project) - if can?(current_user, :update_build, @project)
- if @all_builds.running_or_pending.any? - if @all_builds.running_or_pending.any?
= link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
- unless @repository.gitlab_ci_yml - unless @repository.gitlab_ci_yml
= link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info' = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
= link_to ci_lint_path, class: 'btn btn-default' do = link_to ci_lint_path, class: 'btn btn-default' do
= icon('wrench') = icon('wrench')
%span CI Lint %span CI Lint
%ul.content-list %ul.content-list
- if @builds.blank? - if @builds.blank?
%li %li
.nothing-here-block No builds to show .nothing-here-block No builds to show
- else - else
.table-holder .table-holder
%table.table.builds %table.table.builds
%thead %thead
%tr %tr
%th Status %th Status
%th Build ID %th Build ID
%th Commit %th Commit
%th Ref %th Ref
%th Stage %th Stage
%th Name %th Name
%th Tags %th Tags
%th Duration %th Duration
%th Finished at %th Finished at
- if @project.build_coverage_enabled? - if @project.build_coverage_enabled?
%th Coverage %th Coverage
%th %th
= render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled? = render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
= paginate @builds, theme: 'gitlab' = paginate @builds, theme: 'gitlab'

View file

@ -4,7 +4,7 @@
.build-page .build-page
.row-content-block.top-block .row-content-block.top-block
Build ##{@build.id} for commit Build ##{@build.id} for commit
%strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit) %strong.monospace= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline)
from from
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref) = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
- merge_request = @build.merge_request - merge_request = @build.merge_request
@ -13,7 +13,7 @@
= link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request) = link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
#up-build-trace #up-build-trace
- builds = @build.commit.builds.latest.to_a - builds = @build.pipeline.builds.latest.to_a
- if builds.size > 1 - if builds.size > 1
%ul.nav-links.no-top.no-bottom %ul.nav-links.no-top.no-bottom
- builds.each do |build| - builds.each do |build|
@ -178,16 +178,16 @@
Commit Commit
.pull-right .pull-right
%small %small
= link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace" = link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
%p %p
%span.attr-name Branch: %span.attr-name Branch:
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref) = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
%p %p
%span.attr-name Author: %span.attr-name Author:
#{@build.commit.git_author_name} #{@build.pipeline.git_author_name}
%p %p
%span.attr-name Message: %span.attr-name Message:
#{@build.commit.git_commit_message} #{@build.pipeline.git_commit_message}
- if @build.tags.any? - if @build.tags.any?
.build-widget .build-widget
@ -201,7 +201,7 @@
.build-widget .build-widget
%h4.title #{pluralize(@builds.count(:id), "other build")} for %h4.title #{pluralize(@builds.count(:id), "other build")} for
= succeed ":" do = succeed ":" do
= link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace" = link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
%table.table.builds %table.table.builds
- @builds.each_with_index do |build, i| - @builds.each_with_index do |build, i|
%tr.build %tr.build

View file

@ -2,10 +2,10 @@
= form_for @notification_setting, url: namespace_project_notification_setting_path(@project.namespace.becomes(Namespace), @project), method: :patch, remote: true, html: { class: 'inline', id: 'notification-form' } do |f| = form_for @notification_setting, url: namespace_project_notification_setting_path(@project.namespace.becomes(Namespace), @project), method: :patch, remote: true, html: { class: 'inline', id: 'notification-form' } do |f|
= f.hidden_field :level = f.hidden_field :level
.dropdown .dropdown
%a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"} %button.btn.btn-default.notifications-btn#notifications-button{ data: { toggle: "dropdown" }, aria: { haspopup: "true", expanded: "false" } }
= icon('bell') = icon('bell')
= notification_title(@notification_setting.level) = notification_title(@notification_setting.level)
= icon('caret-down') = icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown %ul.dropdown-menu.dropdown-menu-no-wrap.dropdown-menu-align-right.dropdown-menu-selectable.dropdown-menu-large{ role: "menu" }
- NotificationSetting.levels.each do |level| - NotificationSetting.levels.each do |level|
= notification_list_item(level.first, @notification_setting) = notification_list_item(level.first, @notification_setting)

View file

@ -1,55 +1,55 @@
- status = commit.status - status = pipeline.status
%tr.commit %tr.commit
%td.commit-link %td.commit-link
= link_to namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: "ci-status ci-#{status}" do = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: "ci-status ci-#{status}" do
= ci_icon_for_status(status) = ci_icon_for_status(status)
%strong ##{commit.id} %strong ##{pipeline.id}
%td %td
%div.branch-commit %div.branch-commit
- if commit.ref - if pipeline.ref
= link_to commit.ref, namespace_project_commits_path(@project.namespace, @project, commit.ref), class: "monospace" = link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace"
&middot; &middot;
= link_to commit.short_sha, namespace_project_commit_path(@project.namespace, @project, commit.sha), class: "commit-id monospace" = link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: "commit-id monospace"
&nbsp; &nbsp;
- if commit.tag? - if pipeline.tag?
%span.label.label-primary tag %span.label.label-primary tag
- elsif commit.latest? - elsif pipeline.latest?
%span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest %span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest
- if commit.triggered? - if pipeline.triggered?
%span.label.label-primary triggered %span.label.label-primary triggered
- if commit.yaml_errors.present? - if pipeline.yaml_errors.present?
%span.label.label-danger.has-tooltip{ title: "#{commit.yaml_errors}" } yaml invalid %span.label.label-danger.has-tooltip{ title: "#{pipeline.yaml_errors}" } yaml invalid
- if commit.builds.any?(&:stuck?) - if pipeline.builds.any?(&:stuck?)
%span.label.label-warning stuck %span.label.label-warning stuck
%p.commit-title %p.commit-title
- if commit_data = commit.commit_data - if commit_data = pipeline.commit_data
= link_to_gfm truncate(commit_data.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message" = link_to_gfm truncate(commit_data.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message"
- else - else
Cant find HEAD commit for this branch Cant find HEAD commit for this branch
- stages_status = commit.statuses.stages_status - stages_status = pipeline.statuses.stages_status
- stages.each do |stage| - stages.each do |stage|
%td %td
- status = stages_status[stage] - status = stages_status[stage]
- tooltip = "#{stage.titleize}: #{status || 'not found'}" - tooltip = "#{stage.titleize}: #{status || 'not found'}"
- if status - if status
= link_to namespace_project_pipeline_path(@project.namespace, @project, commit.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do = link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do
= ci_icon_for_status(status) = ci_icon_for_status(status)
- else - else
.light.has-tooltip{ title: tooltip } .light.has-tooltip{ title: tooltip }
\- \-
%td %td
- if commit.started_at && commit.finished_at - if pipeline.started_at && pipeline.finished_at
%p.duration %p.duration
#{duration_in_words(commit.finished_at, commit.started_at)} #{duration_in_words(pipeline.finished_at, pipeline.started_at)}
%td %td
.controls.hidden-xs.pull-right .controls.hidden-xs.pull-right
- artifacts = commit.builds.latest.select { |b| b.artifacts? } - artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
- if artifacts.present? - if artifacts.present?
.dropdown.inline.build-artifacts .dropdown.inline.build-artifacts
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
@ -63,9 +63,9 @@
%span #{build.name} %span #{build.name}
- if can?(current_user, :update_pipeline, @project) - if can?(current_user, :update_pipeline, @project)
- if commit.retryable? - if pipeline.retryable?
= link_to retry_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn has-tooltip', title: "Retry", method: :post do = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
= icon("repeat") = icon("repeat")
- if commit.cancelable? - if pipeline.cancelable?
= link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, commit.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do = link_to cancel_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn btn-remove has-tooltip', title: "Cancel", method: :post do
= icon("remove") = icon("remove")

View file

@ -1,2 +1,2 @@
- @ci_commits.each do |ci_commit| - @pipelines.each do |pipeline|
= render "ci_commit", ci_commit: ci_commit, pipeline_details: true = render "pipeline", pipeline: pipeline, pipeline_details: true

View file

@ -1,52 +0,0 @@
.row-content-block.build-content.middle-block
.pull-right
- if can?(current_user, :update_pipeline, ci_commit.project)
- if ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_namespace_project_pipeline_path(ci_commit.project.namespace, ci_commit.project, ci_commit.id), class: 'btn btn-grouped btn-primary', method: :post
- if ci_commit.builds.running_or_pending.any?
= link_to "Cancel running", cancel_namespace_project_pipeline_path(ci_commit.project.namespace, ci_commit.project, ci_commit.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
.oneline.clearfix
- if defined?(pipeline_details) && pipeline_details
Pipeline
= link_to "##{ci_commit.id}", namespace_project_pipeline_path(ci_commit.project.namespace, ci_commit.project, ci_commit.id), class: "monospace"
with
= pluralize ci_commit.statuses.count(:id), "build"
- if ci_commit.ref
for
= link_to ci_commit.ref, namespace_project_commits_path(ci_commit.project.namespace, ci_commit.project, ci_commit.ref), class: "monospace"
- if defined?(link_to_commit) && link_to_commit
for commit
= link_to ci_commit.short_sha, namespace_project_commit_path(ci_commit.project.namespace, ci_commit.project, ci_commit.sha), class: "monospace"
- if ci_commit.duration
in
= time_interval_in_words ci_commit.duration
- if ci_commit.yaml_errors.present?
.bs-callout.bs-callout-danger
%h4 Found errors in your .gitlab-ci.yml:
%ul
- ci_commit.yaml_errors.split(",").each do |error|
%li= error
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
- if ci_commit.project.builds_enabled? && !ci_commit.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Name
%th Tags
%th Duration
%th Finished at
- if ci_commit.project.build_coverage_enabled?
%th Coverage
%th
- ci_commit.statuses.stages.each do |stage|
= render 'projects/commit/ci_stage', stage: stage, statuses: ci_commit.statuses.where(stage: stage)

View file

@ -53,13 +53,13 @@
- if @commit.status - if @commit.status
.commit-info-row .commit-info-row
Builds for Builds for
= pluralize(@commit.ci_commits.count, 'pipeline') = pluralize(@commit.pipelines.count, 'pipeline')
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status-link ci-status-icon-#{@commit.status}" do
= ci_icon_for_status(@commit.status) = ci_icon_for_status(@commit.status)
= ci_label_for_status(@commit.status) = ci_label_for_status(@commit.status)
- if @commit.ci_commits.duration - if @commit.pipelines.duration
in in
= time_interval_in_words @commit.ci_commits.duration = time_interval_in_words @commit.pipelines.duration
.commit-box.content-block .commit-box.content-block
%h3.commit-title %h3.commit-title

View file

@ -0,0 +1,52 @@
.row-content-block.build-content.middle-block
.pull-right
- if can?(current_user, :update_pipeline, pipeline.project)
- if pipeline.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-grouped btn-primary', method: :post
- if pipeline.builds.running_or_pending.any?
= link_to "Cancel running", cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
.oneline.clearfix
- if defined?(pipeline_details) && pipeline_details
Pipeline
= link_to "##{pipeline.id}", namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "monospace"
with
= pluralize pipeline.statuses.count(:id), "build"
- if pipeline.ref
for
= link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace"
- if defined?(link_to_commit) && link_to_commit
for commit
= link_to pipeline.short_sha, namespace_project_commit_path(pipeline.project.namespace, pipeline.project, pipeline.sha), class: "monospace"
- if pipeline.duration
in
= time_interval_in_words pipeline.duration
- if pipeline.yaml_errors.present?
.bs-callout.bs-callout-danger
%h4 Found errors in your .gitlab-ci.yml:
%ul
- pipeline.yaml_errors.split(",").each do |error|
%li= error
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
- if pipeline.project.builds_enabled? && !pipeline.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Name
%th Tags
%th Duration
%th Finished at
- if pipeline.project.build_coverage_enabled?
%th Coverage
%th
- pipeline.statuses.stages.each do |stage|
= render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.where(stage: stage)

View file

@ -1,24 +1,28 @@
%ul.nav-links .scrolling-tabs-container
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do %ul.nav-links.sub-nav.scrolling-tabs
= link_to project_files_path(@project) do %div{ class: (container_class) }
Files .fade-left
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_files_path(@project) do
Files
= nav_link(controller: [:commit, :commits]) do = nav_link(controller: [:commit, :commits]) do
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
Commits Commits
= nav_link(controller: %w(network)) do = nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, current_ref) do = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
Network Network
= nav_link(controller: :compare) do = nav_link(controller: :compare) do
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
Compare Compare
= nav_link(html_options: {class: branches_tab_class}) do = nav_link(html_options: {class: branches_tab_class}) do
= link_to namespace_project_branches_path(@project.namespace, @project) do = link_to namespace_project_branches_path(@project.namespace, @project) do
Branches Branches
= nav_link(controller: [:tags, :releases]) do = nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do = link_to namespace_project_tags_path(@project.namespace, @project) do
Tags Tags
.fade-right

View file

@ -1,3 +1,5 @@
- @no_container = true
- page_title "Commits", @ref - page_title "Commits", @ref
= content_for :meta_tags do = content_for :meta_tags do
- if current_user - if current_user
@ -5,37 +7,38 @@
= render "head" = render "head"
.row-content-block.second-block %div{ class: (container_class) }
.tree-ref-holder .row-content-block.second-block.content-component-block
= render 'shared/ref_switcher', destination: 'commits' .tree-ref-holder
= render 'shared/ref_switcher', destination: 'commits'
.block-controls.hidden-xs.hidden-sm
- if @merge_request.present?
.control
= link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
- elsif create_mr_button?(@repository.root_ref, @ref)
.control
= link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
= icon('plus')
Create Merge Request
.block-controls.hidden-xs.hidden-sm
- if @merge_request.present?
.control .control
= link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn' = form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'pull-left commits-search-form') do
- elsif create_mr_button?(@repository.root_ref, @ref) = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input', spellcheck: false }
.control
= link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
= icon('plus')
Create Merge Request
.control - if current_user && current_user.private_token
= form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'pull-left commits-search-form') do .control
= search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input', spellcheck: false } = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do
= icon("rss")
- if current_user && current_user.private_token
.control
= link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do
= icon("rss")
%ul.breadcrumb.repo-breadcrumb %ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs = commits_breadcrumbs
%div{id: dom_id(@project)} %div{id: dom_id(@project)}
#commits-list.content_list= render "commits", project: @project #commits-list.content_list= render "commits", project: @project
.clear .clear
= spinner = spinner
:javascript :javascript
CommitsList.init(#{@limit}); CommitsList.init(#{@limit});

View file

@ -1,16 +1,18 @@
- @no_container = true
- page_title "Compare" - page_title "Compare"
= render "projects/commits/head" = render "projects/commits/head"
.row-content-block %div{ class: (container_class) }
Compare branches, tags or commit ranges. .row-content-block.second-block.content-component-block
%br Compare branches, tags or commit ranges.
Fill input field with commit id like %br
%code.label-branch 4eedf23 Fill input field with commit id like
or branch/tag name like %code.label-branch 4eedf23
%code.label-branch master or branch/tag name like
and press compare button for the commits list and a code diff. %code.label-branch master
%br and press compare button for the commits list and a code diff.
Changes are shown <b>from</b> the version in the first field <b>to</b> the version in the second field. %br
Changes are shown <b>from</b> the version in the first field <b>to</b> the version in the second field.
.prepend-top-20 .prepend-top-20
= render "form" = render "form"

View file

@ -16,4 +16,4 @@
%li %li
Commits covered: Commits covered:
%strong %strong
= @project.ci_commits.count(:all) = @project.pipelines.count(:all)

View file

@ -1,91 +1 @@
- page_title "Webhooks" = render 'shared/web_hooks/form', hook: @hook, hooks: @hooks, url_components: [@project.namespace.becomes(Namespace), @project]
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
= page_title
%p
#{link_to "Webhooks", help_page_path("web_hooks", "web_hooks")} can be
used for binding events when something is happening within the project.
.col-lg-9.append-bottom-default
%h5.prepend-top-0
Add new webhook
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project) do |f|
= form_errors(@hook)
.form-group
= f.label :url, "URL", class: "label-light"
= f.text_field :url, class: "form-control", placeholder: "http://example.com/trigger-ci.json"
.form-group
= f.label :token, "Secret Token", class: 'label-light'
= f.text_field :token, class: "form-control", placeholder: ''
%p.help-block
Use this token to validate received payloads
.form-group
= f.label :url, "Trigger", class: "label-light"
%div
= f.check_box :push_events, class: "pull-left"
.prepend-left-20
= f.label :push_events, class: "label-light append-bottom-0" do
Push events
%p.light
This url will be triggered by a push to the repository
%div
= f.check_box :tag_push_events, class: "pull-left"
.prepend-left-20
= f.label :tag_push_events, class: "label-light append-bottom-0" do
Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
%div
= f.check_box :note_events, class: "pull-left"
.prepend-left-20
= f.label :note_events, class: "label-light append-bottom-0" do
Comments
%p.light
This url will be triggered when someone adds a comment
%div
= f.check_box :issues_events, class: "pull-left"
.prepend-left-20
= f.label :issues_events, class: "label-light append-bottom-0" do
Issues events
%p.light
This url will be triggered when an issue is created/updated/merged
%div
= f.check_box :merge_requests_events, class: "pull-left"
.prepend-left-20
= f.label :merge_requests_events, class: "label-light append-bottom-0" do
Merge Request events
%p.light
This url will be triggered when a merge request is created/updated/merged
%div
= f.check_box :build_events, class: "pull-left"
.prepend-left-20
= f.label :build_events, class: "label-light append-bottom-0" do
Build events
%p.light
This url will be triggered when the build status changes
%div
= f.check_box :wiki_page_events, class: 'pull-left'
.prepend-left-20
= f.label :wiki_page_events, class: 'label-light append-bottom-0' do
Wiki Page events
%p.light
This url will be triggered when a wiki page is created/updated
.form-group
= f.label :enable_ssl_verification, "SSL verification", class: "label-light"
%div
= f.check_box :enable_ssl_verification, class: "pull-left"
.prepend-left-20
= f.label :enable_ssl_verification, class: "label-light append-bottom-0" do
Enable SSL verification
= f.submit "Add Webhook", class: "btn btn-create"
%hr
%h5.prepend-top-default
Webhooks (#{@hooks.count})
- if @hooks.any?
%ul.well-list
- @hooks.each do |hook|
= render "project_hook", hook: hook
- else
%p.settings-message.text-center.append-bottom-0
No webhooks found, add one in the form above.

View file

@ -2,12 +2,12 @@
%h2.merge-requests-title %h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request') = pluralize(@merge_requests.count, 'Related Merge Request')
%ul.unstyled-list %ul.unstyled-list
- has_any_ci = @merge_requests.any?(&:ci_commit) - has_any_ci = @merge_requests.any?(&:pipeline)
- @merge_requests.each do |merge_request| - @merge_requests.each do |merge_request|
%li %li
%span.merge-request-ci-status %span.merge-request-ci-status
- if merge_request.ci_commit - if merge_request.pipeline
= render_pipeline_status(merge_request.ci_commit) = render_pipeline_status(merge_request.pipeline)
- elsif has_any_ci - elsif has_any_ci
= icon('blank fw') = icon('blank fw')
%span.merge-request-id %span.merge-request-id

View file

@ -5,10 +5,10 @@
- @related_branches.each do |branch| - @related_branches.each do |branch|
%li %li
- sha = @project.repository.find_branch(branch).target - sha = @project.repository.find_branch(branch).target
- ci_commit = @project.ci_commit(sha, branch) if sha - pipeline = @project.pipeline(sha, branch) if sha
- if ci_commit - if ci_copipelinemmit
%span.related-branch-ci-status %span.related-branch-ci-status
= render_pipeline_status(ci_commit) = render_pipeline_status(pipeline)
%span.related-branch-info %span.related-branch-info
%strong %strong
= link_to namespace_project_compare_path(@project.namespace, @project, from: @project.default_branch, to: branch), class: "label-branch" do = link_to namespace_project_compare_path(@project.namespace, @project, from: @project.default_branch, to: branch), class: "label-branch" do

View file

@ -68,9 +68,9 @@
#related-branches{ data: { url: related_branches_namespace_project_issue_url(@project.namespace, @project, @issue) } } #related-branches{ data: { url: related_branches_namespace_project_issue_url(@project.namespace, @project, @issue) } }
// This element is filled in using JavaScript. // This element is filled in using JavaScript.
.content-block.content-block-small .content-block.content-block-small
= render 'new_branch' = render 'new_branch'
= render 'award_emoji/awards_block', awardable: @issue, inline: true = render 'award_emoji/awards_block', awardable: @issue, inline: true
%section.issuable-discussion %section.issuable-discussion
= render 'projects/issues/discussion' = render 'projects/issues/discussion'

View file

@ -1,4 +1,5 @@
%li{ id: dom_id(label), data: { id: label.id } } - label_css_id = dom_id(label)
%li{id: label_css_id, data: { id: label.id } }
= render "shared/label_row", label: label = render "shared/label_row", label: label
.pull-info-right .pull-info-right
%span.append-right-20 %span.append-right-20
@ -10,18 +11,18 @@
= pluralize label.open_issues_count(current_user), 'open issue' = pluralize label.open_issues_count(current_user), 'open issue'
- if current_user - if current_user
.label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}} .label-subscription{ data: { url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label) } }
.subscription-status{data: {status: label_subscription_status(label)}} .subscription-status{ data: { status: label_subscription_status(label) } }
%button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: "button", data: { toggle: "tooltip" } } %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: "button", data: { toggle: "tooltip" } }
%span= label_subscription_toggle_button_text(label) %span= label_subscription_toggle_button_text(label)
- if can? current_user, :admin_label, @project - if can?(current_user, :admin_label, @project)
= link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn action-buttons', data: {toggle: "tooltip"} do = link_to edit_namespace_project_label_path(@project.namespace, @project, label), title: "Edit", class: 'btn action-buttons', data: { toggle: 'tooltip' } do
%i.fa.fa-pencil-square-o %i.fa.fa-pencil-square-o
= link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn action-buttons remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?", toggle: "tooltip"} do = link_to namespace_project_label_path(@project.namespace, @project, label), title: "Delete", class: 'btn action-buttons remove-row', method: :delete, remote: true, data: { confirm: 'Remove this label? Are you sure?', toggle: 'tooltip' } do
%i.fa.fa-trash-o %i.fa.fa-trash-o
- if current_user - if current_user
:javascript :javascript
new Subscription('##{dom_id(label)} .label-subscription'); new Subscription('##{label_css_id} .label-subscription');

View file

@ -1,22 +1,36 @@
- page_title "Labels" - page_title "Labels"
- hide_class = ''
.top-area .top-area
.nav-text .nav-text
Labels can be applied to issues and merge requests. Labels can be applied to issues and merge requests.
.nav-controls .nav-controls
- if can? current_user, :admin_label, @project - if can?(current_user, :admin_label, @project)
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do = link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
= icon('plus') = icon('plus')
New label New label
.labels .labels
- if @labels.present? - if can?(current_user, :admin_label, @project)
%ul.content-list.manage-labels-list -# Only show it in the first page
= render @labels - hide = @project.labels.empty? || (params[:page].present? && params[:page] != '1')
= paginate @labels, theme: 'gitlab' .prioritized-labels{ class: ('hide' if hide) }
- else %h5 Prioritized Labels
.nothing-here-block %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_namespace_project_labels_path(@project.namespace, @project) }
- if can? current_user, :admin_label, @project - if @prioritized_labels.present?
Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}. = render @prioritized_labels
- else - else
No labels created %p.empty-message No prioritized labels yet
.other-labels
- if can?(current_user, :admin_label, @project)
%h5{ class: ('hide' if hide) } Other Labels
- if @labels.present?
%ul.content-list.manage-labels-list.js-other-labels
= render @labels
= paginate @labels, theme: 'gitlab'
- else
.nothing-here-block
- if can?(current_user, :admin_label, @project)
Create a label or #{link_to 'generate a default set of labels', generate_namespace_project_labels_path(@project.namespace, @project), method: :post}.
- else
No labels created

View file

@ -11,9 +11,9 @@
= icon('ban') = icon('ban')
CLOSED CLOSED
- if merge_request.ci_commit - if merge_request.pipeline
%li %li
= render_pipeline_status(merge_request.ci_commit) = render_pipeline_status(merge_request.pipeline)
- if merge_request.open? && merge_request.broken? - if merge_request.open? && merge_request.broken?
%li %li

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