Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into milestone-tooltip
This commit is contained in:
commit
329f6cb9b4
244 changed files with 5849 additions and 2429 deletions
364
.gitlab-ci.yml
364
.gitlab-ci.yml
|
@ -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
|
||||||
|
|
17
CHANGELOG
17
CHANGELOG
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.7.4
|
0.7.5
|
||||||
|
|
8
Gemfile
8
Gemfile
|
@ -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
|
||||||
|
|
51
Gemfile.lock
51
Gemfile.lock
|
@ -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
|
||||||
|
|
2
Rakefile
2
Rakefile
|
@ -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)
|
||||||
|
|
84
app/assets/javascripts/LabelManager.js.coffee
Normal file
84
app/assets/javascripts/LabelManager.js.coffee
Normal 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
|
|
@ -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()
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
window.emojiAliases = ->
|
gl.emojiAliases = ->
|
||||||
JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')
|
JSON.parse('<%= Gitlab::AwardEmoji.aliases.to_json %>')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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;
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
145
app/controllers/projects/git_http_controller.rb
Normal file
145
app/controllers/projects/git_http_controller.rb
Normal 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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)}
|
||||||
|
|
|
@ -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) %>
|
||||||
|
|
|
@ -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)}
|
||||||
|
|
|
@ -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) %>
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
= icon('plus')
|
||||||
.dropdown.inline
|
New branch
|
||||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
|
||||||
%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'
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
·
|
·
|
||||||
= 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"
|
||||||
|
|
||||||
- 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")
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
|
|
@ -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
|
||||||
|
|
52
app/views/projects/commit/_pipeline.html.haml
Normal file
52
app/views/projects/commit/_pipeline.html.haml
Normal 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)
|
|
@ -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
|
||||||
|
|
|
@ -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});
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -16,4 +16,4 @@
|
||||||
%li
|
%li
|
||||||
Commits covered:
|
Commits covered:
|
||||||
%strong
|
%strong
|
||||||
= @project.ci_commits.count(:all)
|
= @project.pipelines.count(:all)
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
Loading…
Reference in a new issue