New upstream version 9.5.4+dfsg
This commit is contained in:
parent
b03c211899
commit
574775de31
5149 changed files with 159847 additions and 65503 deletions
47
.codeclimate.yml
Normal file
47
.codeclimate.yml
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
engines:
|
||||
brakeman:
|
||||
enabled: true
|
||||
bundler-audit:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- ruby
|
||||
- javascript
|
||||
exclude_paths:
|
||||
- "lib/api/v3/*"
|
||||
eslint:
|
||||
enabled: true
|
||||
rubocop:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- Gemfile.lock
|
||||
- "**.erb"
|
||||
- "**.haml"
|
||||
- "**.rb"
|
||||
- "**.rhtml"
|
||||
- "**.slim"
|
||||
- "**.inc"
|
||||
- "**.js"
|
||||
- "**.jsx"
|
||||
- "**.module"
|
||||
exclude_paths:
|
||||
- config/
|
||||
- db/
|
||||
- features/
|
||||
- node_modules/
|
||||
- spec/
|
||||
- vendor/
|
||||
- .yarn-cache/
|
||||
- tmp/
|
||||
- builds/
|
||||
- coverage/
|
||||
- public/
|
||||
- shared/
|
||||
- webpack-report/
|
||||
- log/
|
||||
- backups/
|
||||
- coverage-javascript/
|
|
@ -11,6 +11,7 @@
|
|||
"gon": false,
|
||||
"localStorage": false
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"plugins": [
|
||||
"filenames",
|
||||
"import",
|
||||
|
@ -27,7 +28,9 @@
|
|||
},
|
||||
"rules": {
|
||||
"filenames/match-regex": [2, "^[a-z0-9_]+$"],
|
||||
"import/no-commonjs": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 1 }],
|
||||
"promise/catch-or-return": "error"
|
||||
"promise/catch-or-return": "error",
|
||||
"no-underscore-dangle": ["error", { "allow": ["__"]}]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,3 +3,5 @@ lib/gitlab/sanitizers/svg/whitelist.rb
|
|||
lib/gitlab/diff/position_tracer.rb
|
||||
app/policies/project_policy.rb
|
||||
app/models/concerns/relative_positioning.rb
|
||||
app/workers/stuck_merge_jobs_worker.rb
|
||||
lib/gitlab/redis/*.rb
|
||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -18,8 +18,10 @@ eslint-report.html
|
|||
.sass-cache/
|
||||
/.secret
|
||||
/.vagrant
|
||||
/.yarn-cache
|
||||
/.byebug_history
|
||||
/Vagrantfile
|
||||
/app/assets/javascripts/locale/**/app.js
|
||||
/backups/*
|
||||
/config/aws.yml
|
||||
/config/database.yml
|
||||
|
@ -29,6 +31,9 @@ eslint-report.html
|
|||
/config/initializers/smtp_settings.rb
|
||||
/config/initializers/relative_url.rb
|
||||
/config/resque.yml
|
||||
/config/redis.cache.yml
|
||||
/config/redis.queues.yml
|
||||
/config/redis.shared_state.yml
|
||||
/config/unicorn.rb
|
||||
/config/secrets.yml
|
||||
/config/sidekiq.yml
|
||||
|
@ -48,6 +53,7 @@ eslint-report.html
|
|||
/public/uploads/
|
||||
/shared/artifacts/
|
||||
/spec/javascripts/fixtures/blob/pdf/
|
||||
/spec/javascripts/fixtures/blob/balsamiq/
|
||||
/rails_best_practices_output.html
|
||||
/tags
|
||||
/tmp/*
|
||||
|
@ -57,3 +63,4 @@ eslint-report.html
|
|||
/.gitlab_workhorse_secret
|
||||
/webpack-report/
|
||||
/locale/**/LC_MESSAGES
|
||||
/.rspec
|
||||
|
|
401
.gitlab-ci.yml
401
.gitlab-ci.yml
|
@ -1,9 +1,20 @@
|
|||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1-postgresql-9.6"
|
||||
|
||||
cache:
|
||||
key: "ruby-233"
|
||||
.default-cache: &default-cache
|
||||
key: "ruby-233-with-yarn"
|
||||
paths:
|
||||
- vendor/ruby
|
||||
- vendor/ruby
|
||||
- .yarn-cache/
|
||||
|
||||
.push-cache: &push-cache
|
||||
cache:
|
||||
<<: *default-cache
|
||||
policy: push
|
||||
|
||||
.pull-cache: &pull-cache
|
||||
cache:
|
||||
<<: *default-cache
|
||||
policy: pull
|
||||
|
||||
variables:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
|
||||
|
@ -23,11 +34,11 @@ before_script:
|
|||
- source scripts/prepare_build.sh
|
||||
|
||||
stages:
|
||||
- build
|
||||
- prepare
|
||||
- test
|
||||
- post-test
|
||||
- pages
|
||||
- build
|
||||
- prepare
|
||||
- test
|
||||
- post-test
|
||||
- pages
|
||||
|
||||
# Predefined scopes
|
||||
.dedicated-runner: &dedicated-runner
|
||||
|
@ -40,10 +51,6 @@ stages:
|
|||
SETUP_DB: "false"
|
||||
USE_BUNDLE_INSTALL: "false"
|
||||
KNAPSACK_S3_BUCKET: "gitlab-ce-cache"
|
||||
cache:
|
||||
key: "knapsack"
|
||||
paths:
|
||||
- knapsack/
|
||||
artifacts:
|
||||
expire_in: 31d
|
||||
paths:
|
||||
|
@ -59,34 +66,39 @@ stages:
|
|||
- mysql:latest
|
||||
- redis:alpine
|
||||
|
||||
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
|
||||
.only-if-want-mysql: &only-if-want-mysql
|
||||
only:
|
||||
- /mysql/
|
||||
- /-stable/
|
||||
- master@gitlab-org/gitlab-ce
|
||||
- master@gitlab-org/gitlab-ee
|
||||
- master@gitlab/gitlabhq
|
||||
- master@gitlab/gitlab-ee
|
||||
- tags@gitlab-org/gitlab-ce
|
||||
- tags@gitlab-org/gitlab-ee
|
||||
- tags@gitlab/gitlabhq
|
||||
- //@gitlab-org/gitlab-ee
|
||||
- //@gitlab/gitlab-ee
|
||||
- tags@gitlab/gitlab-ee
|
||||
|
||||
# Skip all jobs except the ones that begin with 'docs/'.
|
||||
# Used for commits including ONLY documentation changes.
|
||||
# https://docs.gitlab.com/ce/development/writing_documentation.html#testing
|
||||
.except-docs: &except-docs
|
||||
except:
|
||||
- /^docs\/.*/
|
||||
- /(^docs[\/-].*|.*-docs$)/
|
||||
|
||||
.rspec-knapsack: &rspec-knapsack
|
||||
stage: test
|
||||
<<: *dedicated-runner
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
script:
|
||||
- JOB_NAME=( $CI_JOB_NAME )
|
||||
- export CI_NODE_INDEX=${JOB_NAME[-2]}
|
||||
- export CI_NODE_TOTAL=${JOB_NAME[-1]}
|
||||
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_${JOB_NAME[1]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
|
||||
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
|
||||
- export KNAPSACK_GENERATE_REPORT=true
|
||||
- export CACHE_CLASSES=true
|
||||
- cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
|
||||
- scripts/gitaly-test-spawn
|
||||
- knapsack rspec "--color --format documentation"
|
||||
artifacts:
|
||||
expire_in: 31d
|
||||
|
@ -104,17 +116,18 @@ stages:
|
|||
.rspec-knapsack-mysql: &rspec-knapsack-mysql
|
||||
<<: *rspec-knapsack
|
||||
<<: *use-mysql
|
||||
<<: *only-master-and-ee-or-mysql
|
||||
<<: *only-if-want-mysql
|
||||
<<: *except-docs
|
||||
|
||||
.spinach-knapsack: &spinach-knapsack
|
||||
stage: test
|
||||
<<: *dedicated-runner
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
script:
|
||||
- JOB_NAME=( $CI_JOB_NAME )
|
||||
- export CI_NODE_INDEX=${JOB_NAME[-2]}
|
||||
- export CI_NODE_TOTAL=${JOB_NAME[-1]}
|
||||
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_${JOB_NAME[1]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
|
||||
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
|
||||
- export KNAPSACK_GENERATE_REPORT=true
|
||||
- export CACHE_CLASSES=true
|
||||
- cp ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
|
||||
|
@ -135,30 +148,33 @@ stages:
|
|||
.spinach-knapsack-mysql: &spinach-knapsack-mysql
|
||||
<<: *spinach-knapsack
|
||||
<<: *use-mysql
|
||||
<<: *only-master-and-ee-or-mysql
|
||||
<<: *only-if-want-mysql
|
||||
<<: *except-docs
|
||||
|
||||
.only-canonical-masters: &only-canonical-masters
|
||||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
- master@gitlab-org/gitlab-ee
|
||||
- master@gitlab/gitlabhq
|
||||
- master@gitlab/gitlab-ee
|
||||
|
||||
# Trigger a package build on omnibus-gitlab repository
|
||||
|
||||
build-package:
|
||||
image: ruby:2.3-alpine
|
||||
before_script: []
|
||||
services: []
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
USE_BUNDLE_INSTALL: "false"
|
||||
stage: build
|
||||
cache: {}
|
||||
when: manual
|
||||
script:
|
||||
# If no branch in omnibus is specified, trigger pipeline against master
|
||||
- if [ -z "$OMNIBUS_BRANCH" ] ; then export OMNIBUS_BRANCH=master ;fi
|
||||
- echo "token=${BUILD_TRIGGER_TOKEN}" > version_details
|
||||
- echo "ref=${OMNIBUS_BRANCH}" >> version_details
|
||||
- echo "variables[ALTERNATIVE_SOURCES]=true" >> version_details
|
||||
- echo "variables[GITLAB_VERSION]=${CI_COMMIT_SHA}" >> version_details
|
||||
# Collect version details of all components
|
||||
- for f in *_VERSION; do echo "variables[$f]=$(cat $f)" >> version_details; done
|
||||
# Trigger the API and pass values collected above as parameters to it
|
||||
- cat version_details | tr '\n' '&' | curl -X POST https://gitlab.com/api/v4/projects/20699/trigger/pipeline --data-binary @-
|
||||
- rm version_details
|
||||
- scripts/trigger-build
|
||||
only:
|
||||
- //@gitlab-org/gitlab-ce
|
||||
- //@gitlab-org/gitlab-ee
|
||||
|
||||
# Prepare and merge knapsack tests
|
||||
knapsack:
|
||||
|
@ -166,6 +182,11 @@ knapsack:
|
|||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
stage: prepare
|
||||
cache:
|
||||
key: knapsack
|
||||
paths:
|
||||
- knapsack/
|
||||
policy: pull
|
||||
script:
|
||||
- mkdir -p knapsack/${CI_PROJECT_NAME}/
|
||||
- wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${KNAPSACK_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH
|
||||
|
@ -176,28 +197,34 @@ knapsack:
|
|||
update-knapsack:
|
||||
<<: *knapsack-state
|
||||
<<: *dedicated-runner
|
||||
<<: *only-canonical-masters
|
||||
stage: post-test
|
||||
cache:
|
||||
key: knapsack
|
||||
paths:
|
||||
- knapsack/
|
||||
policy: push
|
||||
script:
|
||||
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_pg_node_*.json
|
||||
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach_pg_node_*.json
|
||||
- retry gem install fog-aws mime-types
|
||||
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
|
||||
- scripts/merge-reports ${KNAPSACK_SPINACH_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/spinach-pg_node_*.json
|
||||
- '[[ -z ${KNAPSACK_S3_BUCKET} ]] || scripts/sync-reports put $KNAPSACK_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH $KNAPSACK_SPINACH_SUITE_REPORT_PATH'
|
||||
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
|
||||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
- master@gitlab-org/gitlab-ee
|
||||
- master@gitlab/gitlabhq
|
||||
- master@gitlab/gitlab-ee
|
||||
|
||||
setup-test-env:
|
||||
<<: *use-pg
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
stage: prepare
|
||||
cache:
|
||||
<<: *default-cache
|
||||
script:
|
||||
- node --version
|
||||
- yarn install --pure-lockfile
|
||||
- yarn install --pure-lockfile --cache-folder .yarn-cache
|
||||
- bundle exec rake gettext:po_to_json
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
||||
- scripts/gitaly-test-build # Do not use 'bundle exec' here
|
||||
artifacts:
|
||||
expire_in: 7d
|
||||
paths:
|
||||
|
@ -205,81 +232,82 @@ setup-test-env:
|
|||
- public/assets
|
||||
- tmp/tests
|
||||
|
||||
rspec pg 0 20: *rspec-knapsack-pg
|
||||
rspec pg 1 20: *rspec-knapsack-pg
|
||||
rspec pg 2 20: *rspec-knapsack-pg
|
||||
rspec pg 3 20: *rspec-knapsack-pg
|
||||
rspec pg 4 20: *rspec-knapsack-pg
|
||||
rspec pg 5 20: *rspec-knapsack-pg
|
||||
rspec pg 6 20: *rspec-knapsack-pg
|
||||
rspec pg 7 20: *rspec-knapsack-pg
|
||||
rspec pg 8 20: *rspec-knapsack-pg
|
||||
rspec pg 9 20: *rspec-knapsack-pg
|
||||
rspec pg 10 20: *rspec-knapsack-pg
|
||||
rspec pg 11 20: *rspec-knapsack-pg
|
||||
rspec pg 12 20: *rspec-knapsack-pg
|
||||
rspec pg 13 20: *rspec-knapsack-pg
|
||||
rspec pg 14 20: *rspec-knapsack-pg
|
||||
rspec pg 15 20: *rspec-knapsack-pg
|
||||
rspec pg 16 20: *rspec-knapsack-pg
|
||||
rspec pg 17 20: *rspec-knapsack-pg
|
||||
rspec pg 18 20: *rspec-knapsack-pg
|
||||
rspec pg 19 20: *rspec-knapsack-pg
|
||||
rspec-pg 0 25: *rspec-knapsack-pg
|
||||
rspec-pg 1 25: *rspec-knapsack-pg
|
||||
rspec-pg 2 25: *rspec-knapsack-pg
|
||||
rspec-pg 3 25: *rspec-knapsack-pg
|
||||
rspec-pg 4 25: *rspec-knapsack-pg
|
||||
rspec-pg 5 25: *rspec-knapsack-pg
|
||||
rspec-pg 6 25: *rspec-knapsack-pg
|
||||
rspec-pg 7 25: *rspec-knapsack-pg
|
||||
rspec-pg 8 25: *rspec-knapsack-pg
|
||||
rspec-pg 9 25: *rspec-knapsack-pg
|
||||
rspec-pg 10 25: *rspec-knapsack-pg
|
||||
rspec-pg 11 25: *rspec-knapsack-pg
|
||||
rspec-pg 12 25: *rspec-knapsack-pg
|
||||
rspec-pg 13 25: *rspec-knapsack-pg
|
||||
rspec-pg 14 25: *rspec-knapsack-pg
|
||||
rspec-pg 15 25: *rspec-knapsack-pg
|
||||
rspec-pg 16 25: *rspec-knapsack-pg
|
||||
rspec-pg 17 25: *rspec-knapsack-pg
|
||||
rspec-pg 18 25: *rspec-knapsack-pg
|
||||
rspec-pg 19 25: *rspec-knapsack-pg
|
||||
rspec-pg 20 25: *rspec-knapsack-pg
|
||||
rspec-pg 21 25: *rspec-knapsack-pg
|
||||
rspec-pg 22 25: *rspec-knapsack-pg
|
||||
rspec-pg 23 25: *rspec-knapsack-pg
|
||||
rspec-pg 24 25: *rspec-knapsack-pg
|
||||
|
||||
rspec mysql 0 20: *rspec-knapsack-mysql
|
||||
rspec mysql 1 20: *rspec-knapsack-mysql
|
||||
rspec mysql 2 20: *rspec-knapsack-mysql
|
||||
rspec mysql 3 20: *rspec-knapsack-mysql
|
||||
rspec mysql 4 20: *rspec-knapsack-mysql
|
||||
rspec mysql 5 20: *rspec-knapsack-mysql
|
||||
rspec mysql 6 20: *rspec-knapsack-mysql
|
||||
rspec mysql 7 20: *rspec-knapsack-mysql
|
||||
rspec mysql 8 20: *rspec-knapsack-mysql
|
||||
rspec mysql 9 20: *rspec-knapsack-mysql
|
||||
rspec mysql 10 20: *rspec-knapsack-mysql
|
||||
rspec mysql 11 20: *rspec-knapsack-mysql
|
||||
rspec mysql 12 20: *rspec-knapsack-mysql
|
||||
rspec mysql 13 20: *rspec-knapsack-mysql
|
||||
rspec mysql 14 20: *rspec-knapsack-mysql
|
||||
rspec mysql 15 20: *rspec-knapsack-mysql
|
||||
rspec mysql 16 20: *rspec-knapsack-mysql
|
||||
rspec mysql 17 20: *rspec-knapsack-mysql
|
||||
rspec mysql 18 20: *rspec-knapsack-mysql
|
||||
rspec mysql 19 20: *rspec-knapsack-mysql
|
||||
rspec-mysql 0 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 1 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 2 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 3 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 4 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 5 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 6 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 7 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 8 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 9 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 10 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 11 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 12 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 13 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 14 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 15 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 16 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 17 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 18 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 19 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 20 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 21 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 22 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 23 25: *rspec-knapsack-mysql
|
||||
rspec-mysql 24 25: *rspec-knapsack-mysql
|
||||
|
||||
spinach pg 0 10: *spinach-knapsack-pg
|
||||
spinach pg 1 10: *spinach-knapsack-pg
|
||||
spinach pg 2 10: *spinach-knapsack-pg
|
||||
spinach pg 3 10: *spinach-knapsack-pg
|
||||
spinach pg 4 10: *spinach-knapsack-pg
|
||||
spinach pg 5 10: *spinach-knapsack-pg
|
||||
spinach pg 6 10: *spinach-knapsack-pg
|
||||
spinach pg 7 10: *spinach-knapsack-pg
|
||||
spinach pg 8 10: *spinach-knapsack-pg
|
||||
spinach pg 9 10: *spinach-knapsack-pg
|
||||
spinach-pg 0 5: *spinach-knapsack-pg
|
||||
spinach-pg 1 5: *spinach-knapsack-pg
|
||||
spinach-pg 2 5: *spinach-knapsack-pg
|
||||
spinach-pg 3 5: *spinach-knapsack-pg
|
||||
spinach-pg 4 5: *spinach-knapsack-pg
|
||||
|
||||
spinach mysql 0 10: *spinach-knapsack-mysql
|
||||
spinach mysql 1 10: *spinach-knapsack-mysql
|
||||
spinach mysql 2 10: *spinach-knapsack-mysql
|
||||
spinach mysql 3 10: *spinach-knapsack-mysql
|
||||
spinach mysql 4 10: *spinach-knapsack-mysql
|
||||
spinach mysql 5 10: *spinach-knapsack-mysql
|
||||
spinach mysql 6 10: *spinach-knapsack-mysql
|
||||
spinach mysql 7 10: *spinach-knapsack-mysql
|
||||
spinach mysql 8 10: *spinach-knapsack-mysql
|
||||
spinach mysql 9 10: *spinach-knapsack-mysql
|
||||
spinach-mysql 0 5: *spinach-knapsack-mysql
|
||||
spinach-mysql 1 5: *spinach-knapsack-mysql
|
||||
spinach-mysql 2 5: *spinach-knapsack-mysql
|
||||
spinach-mysql 3 5: *spinach-knapsack-mysql
|
||||
spinach-mysql 4 5: *spinach-knapsack-mysql
|
||||
|
||||
# Other generic tests
|
||||
# Static analysis jobs
|
||||
.ruby-static-analysis: &ruby-static-analysis
|
||||
<<: *pull-cache
|
||||
variables:
|
||||
SIMPLECOV: "false"
|
||||
SETUP_DB: "false"
|
||||
USE_BUNDLE_INSTALL: "true"
|
||||
|
||||
.rake-exec: &rake-exec
|
||||
<<: *ruby-static-analysis
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
script:
|
||||
- bundle exec rake $CI_JOB_NAME
|
||||
|
@ -290,17 +318,15 @@ static-analysis:
|
|||
<<: *except-docs
|
||||
stage: test
|
||||
script:
|
||||
- rm -r node_modules
|
||||
- yarn install --pure-lockfile
|
||||
- scripts/static-analysis
|
||||
|
||||
# Documentation checks:
|
||||
# - Check validity of relative links
|
||||
# - Make sure cURL examples in API docs use the full switches
|
||||
docs lint:
|
||||
<<: *dedicated-runner
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
|
||||
stage: test
|
||||
<<: *dedicated-runner
|
||||
cache: {}
|
||||
dependencies: []
|
||||
before_script: []
|
||||
|
@ -319,7 +345,7 @@ downtime_check:
|
|||
- master
|
||||
- tags
|
||||
- /^[\d-]+-stable(-ee)?$/
|
||||
- /^docs\/*/
|
||||
- /(^docs[\/-].*|.*-docs$)/
|
||||
|
||||
ee_compat_check:
|
||||
<<: *rake-exec
|
||||
|
@ -328,7 +354,7 @@ ee_compat_check:
|
|||
except:
|
||||
- master
|
||||
- tags
|
||||
- /^[\d-]+-stable(-ee)?$/
|
||||
- /^[\d-]+-stable(-ee)?/
|
||||
allow_failure: yes
|
||||
cache:
|
||||
key: "ee_compat_check_repo"
|
||||
|
@ -341,67 +367,96 @@ ee_compat_check:
|
|||
paths:
|
||||
- ee_compat_check/patches/*.patch
|
||||
|
||||
# DB migration, rollback, and seed jobs
|
||||
.db-migrate-reset: &db-migrate-reset
|
||||
stage: test
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
script:
|
||||
- bundle exec rake db:migrate:reset
|
||||
|
||||
rake pg db:migrate:reset:
|
||||
db:migrate:reset-pg:
|
||||
<<: *db-migrate-reset
|
||||
<<: *use-pg
|
||||
|
||||
rake mysql db:migrate:reset:
|
||||
db:migrate:reset-mysql:
|
||||
<<: *db-migrate-reset
|
||||
<<: *use-mysql
|
||||
|
||||
.db-rollback: &db-rollback
|
||||
.migration-paths: &migration-paths
|
||||
<<: *dedicated-runner
|
||||
<<: *only-canonical-masters
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
script:
|
||||
- git fetch origin v8.14.10
|
||||
- git checkout -f FETCH_HEAD
|
||||
- bundle install $BUNDLE_INSTALL_FLAGS
|
||||
- cp config/gitlab.yml.example config/gitlab.yml
|
||||
- bundle exec rake db:drop db:create db:schema:load db:seed_fu
|
||||
- git checkout $CI_COMMIT_SHA
|
||||
- bundle install $BUNDLE_INSTALL_FLAGS
|
||||
- . scripts/prepare_build.sh
|
||||
- bundle exec rake db:migrate
|
||||
|
||||
migration:path-pg:
|
||||
<<: *migration-paths
|
||||
<<: *use-pg
|
||||
|
||||
migration:path-mysql:
|
||||
<<: *migration-paths
|
||||
<<: *use-mysql
|
||||
|
||||
.db-rollback: &db-rollback
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
script:
|
||||
- bundle exec rake db:rollback STEP=120
|
||||
- bundle exec rake db:migrate
|
||||
|
||||
rake pg db:rollback:
|
||||
db:rollback-pg:
|
||||
<<: *db-rollback
|
||||
<<: *use-pg
|
||||
|
||||
rake mysql db:rollback:
|
||||
db:rollback-mysql:
|
||||
<<: *db-rollback
|
||||
<<: *use-mysql
|
||||
|
||||
.db-seed_fu: &db-seed_fu
|
||||
stage: test
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
variables:
|
||||
SIZE: "1"
|
||||
SETUP_DB: "false"
|
||||
RAILS_ENV: "development"
|
||||
script:
|
||||
- git clone https://gitlab.com/gitlab-org/gitlab-test.git
|
||||
/home/git/repositories/gitlab-org/gitlab-test.git
|
||||
- bundle exec rake db:setup db:seed_fu
|
||||
- cp -R db/fixtures/development db/fixtures/test
|
||||
- bundle exec rake db:seed_fu
|
||||
artifacts:
|
||||
when: on_failure
|
||||
expire_in: 1d
|
||||
paths:
|
||||
- log/development.log
|
||||
|
||||
rake pg db:seed_fu:
|
||||
db:seed_fu-pg:
|
||||
<<: *db-seed_fu
|
||||
<<: *use-pg
|
||||
|
||||
rake mysql db:seed_fu:
|
||||
db:seed_fu-mysql:
|
||||
<<: *db-seed_fu
|
||||
<<: *use-mysql
|
||||
|
||||
rake gitlab:assets:compile:
|
||||
stage: test
|
||||
# Frontend-related jobs
|
||||
gitlab:assets:compile:
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
stage: test
|
||||
dependencies: []
|
||||
variables:
|
||||
NODE_ENV: "production"
|
||||
|
@ -410,65 +465,63 @@ rake gitlab:assets:compile:
|
|||
USE_DB: "false"
|
||||
SKIP_STORAGE_VALIDATION: "true"
|
||||
WEBPACK_REPORT: "true"
|
||||
NO_COMPRESSION: "true"
|
||||
script:
|
||||
- bundle exec rake yarn:install gitlab:assets:compile
|
||||
- yarn install --pure-lockfile --production --cache-folder .yarn-cache
|
||||
- bundle exec rake gettext:po_to_json
|
||||
- bundle exec rake gitlab:assets:compile
|
||||
artifacts:
|
||||
name: webpack-report
|
||||
expire_in: 31d
|
||||
paths:
|
||||
- webpack-report/
|
||||
- webpack-report/
|
||||
|
||||
rake karma:
|
||||
stage: test
|
||||
karma:
|
||||
<<: *use-pg
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-chrome-59.0-node-7.1-postgresql-9.6"
|
||||
stage: test
|
||||
variables:
|
||||
BABEL_ENV: "coverage"
|
||||
CHROME_LOG_FILE: "chrome_debug.log"
|
||||
script:
|
||||
- rm -r node_modules
|
||||
- yarn install --pure-lockfile
|
||||
- scripts/gitaly-test-spawn
|
||||
- bundle exec rake gettext:po_to_json
|
||||
- bundle exec rake karma
|
||||
coverage: '/^Statements *: (\d+\.\d+%)/'
|
||||
artifacts:
|
||||
name: coverage-javascript
|
||||
expire_in: 31d
|
||||
when: always
|
||||
paths:
|
||||
- chrome_debug.log
|
||||
- coverage-javascript/
|
||||
|
||||
.migration-paths: &migration-paths
|
||||
codeclimate:
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
before_script: []
|
||||
image: docker:latest
|
||||
stage: test
|
||||
<<: *dedicated-runner
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
- master@gitlab-org/gitlab-ee
|
||||
- master@gitlab/gitlabhq
|
||||
- master@gitlab/gitlab-ee
|
||||
DOCKER_DRIVER: overlay
|
||||
services:
|
||||
- docker:dind
|
||||
script:
|
||||
- git fetch origin v8.14.10
|
||||
- git checkout -f FETCH_HEAD
|
||||
- bundle install $BUNDLE_INSTALL_FLAGS
|
||||
- bundle exec rake db:drop db:create db:schema:load db:seed_fu
|
||||
- git checkout $CI_COMMIT_SHA
|
||||
- bundle install $BUNDLE_INSTALL_FLAGS
|
||||
- . scripts/prepare_build.sh
|
||||
- bundle exec rake db:migrate
|
||||
|
||||
migration pg paths:
|
||||
<<: *migration-paths
|
||||
<<: *use-pg
|
||||
|
||||
migration mysql paths:
|
||||
<<: *migration-paths
|
||||
<<: *use-mysql
|
||||
- docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f json > raw_codeclimate.json
|
||||
- cat raw_codeclimate.json | docker run -i stedolan/jq -c 'map({check_name,fingerprint,location})' > codeclimate.json
|
||||
artifacts:
|
||||
paths: [codeclimate.json]
|
||||
|
||||
coverage:
|
||||
stage: post-test
|
||||
services: []
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
stage: post-test
|
||||
services: []
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
USE_BUNDLE_INSTALL: "true"
|
||||
|
@ -485,7 +538,10 @@ coverage:
|
|||
lint:javascript:report:
|
||||
<<: *dedicated-runner
|
||||
<<: *except-docs
|
||||
<<: *pull-cache
|
||||
stage: post-test
|
||||
dependencies:
|
||||
- setup-test-env
|
||||
before_script: []
|
||||
script:
|
||||
- find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files
|
||||
|
@ -496,33 +552,15 @@ lint:javascript:report:
|
|||
paths:
|
||||
- eslint-report.html
|
||||
|
||||
# Trigger docs build
|
||||
# https://gitlab.com/gitlab-com/doc-gitlab-com/blob/master/README.md#deployment-process
|
||||
trigger_docs:
|
||||
stage: post-test
|
||||
image: "alpine"
|
||||
<<: *dedicated-runner
|
||||
before_script:
|
||||
- apk update && apk add curl
|
||||
variables:
|
||||
GIT_STRATEGY: "none"
|
||||
cache: {}
|
||||
artifacts: {}
|
||||
script:
|
||||
- "HTTP_STATUS=$(curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=${CI_PROJECT_NAME} --silent --output curl.log --write-out '%{http_code}' https://gitlab.com/api/v3/projects/1794617/trigger/builds)"
|
||||
- if [ "${HTTP_STATUS}" -ne "201" ]; then echo "Error ${HTTP_STATUS}"; cat curl.log; echo; exit 1; fi
|
||||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
- master@gitlab-org/gitlab-ee
|
||||
|
||||
pages:
|
||||
<<: *dedicated-runner
|
||||
<<: *pull-cache
|
||||
before_script: []
|
||||
stage: pages
|
||||
<<: *dedicated-runner
|
||||
dependencies:
|
||||
- coverage
|
||||
- rake karma
|
||||
- rake gitlab:assets:compile
|
||||
- karma
|
||||
- gitlab:assets:compile
|
||||
- lint:javascript:report
|
||||
script:
|
||||
- mv public/ .public/
|
||||
|
@ -542,6 +580,7 @@ pages:
|
|||
# rubygems.org in the future.
|
||||
cache gems:
|
||||
<<: *dedicated-runner
|
||||
<<: *pull-cache
|
||||
only:
|
||||
- tags
|
||||
variables:
|
||||
|
@ -554,3 +593,11 @@ cache gems:
|
|||
only:
|
||||
- master@gitlab-org/gitlab-ce
|
||||
- master@gitlab-org/gitlab-ee
|
||||
|
||||
gitlab_git_test:
|
||||
<<: *pull-cache
|
||||
<<: *except-docs
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
script:
|
||||
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
Please read this!
|
||||
|
||||
Before opening a new issue, make sure to search for keywords in the issues
|
||||
filtered by the "regression" or "bug" label:
|
||||
filtered by the "regression" or "bug" label.
|
||||
|
||||
For the Community Edition issue tracker:
|
||||
|
||||
- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=regression
|
||||
- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=bug
|
||||
|
||||
For the Enterprise Edition issue tracker:
|
||||
|
||||
- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=regression
|
||||
- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=bug
|
||||
|
||||
and verify the issue you're about to submit isn't a duplicate.
|
||||
|
||||
Please remove this notice if you're confident your issue isn't a duplicate.
|
||||
|
@ -20,6 +27,12 @@ Please remove this notice if you're confident your issue isn't a duplicate.
|
|||
|
||||
(How one can reproduce the issue - this is very important)
|
||||
|
||||
### Example Project
|
||||
|
||||
(If possible, please create an example project here on GitLab.com that exhibits the problematic behaviour, and link to it here in the bug report)
|
||||
|
||||
(If you are using an older version of GitLab, this will also determine whether the bug has been fixed in a more recent version)
|
||||
|
||||
### What is the current *bug* behavior?
|
||||
|
||||
(What actually happens)
|
||||
|
@ -40,6 +53,7 @@ logs, and code as it's very hard to read otherwise.)
|
|||
#### Results of GitLab environment info
|
||||
|
||||
<details>
|
||||
<summary>Expand for output related to GitLab environment info</summary>
|
||||
<pre>
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
|
@ -54,6 +68,7 @@ logs, and code as it's very hard to read otherwise.)
|
|||
#### Results of GitLab application Check
|
||||
|
||||
<details>
|
||||
<summary>Expand for output related to the GitLab application check</summary>
|
||||
<pre>
|
||||
|
||||
(For installations with omnibus-gitlab package run and paste the output of:
|
||||
|
|
|
@ -3,8 +3,14 @@ Please read this!
|
|||
Before opening a new issue, make sure to search for keywords in the issues
|
||||
filtered by the "feature proposal" label:
|
||||
|
||||
For the Community Edition issue tracker:
|
||||
|
||||
- https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=feature+proposal
|
||||
|
||||
For the Enterprise Edition issue tracker:
|
||||
|
||||
- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=feature+proposal
|
||||
|
||||
and verify the issue you're about to submit isn't a duplicate.
|
||||
|
||||
Please remove this notice if you're confident your issue isn't a duplicate.
|
||||
|
@ -21,12 +27,24 @@ Please remove this notice if you're confident your issue isn't a duplicate.
|
|||
|
||||
### Documentation blurb
|
||||
|
||||
(Write the start of the documentation of this feature here, include:
|
||||
#### Overview
|
||||
|
||||
1. Why should someone use it; what's the underlying problem.
|
||||
2. What is the solution.
|
||||
3. How does someone use this
|
||||
What is it?
|
||||
Why should someone use this feature?
|
||||
What is the underlying (business) problem?
|
||||
How do you use this feature?
|
||||
|
||||
During implementation, this can then be copied and used as a starter for the documentation.)
|
||||
#### Use cases
|
||||
|
||||
/label ~"feature proposal"
|
||||
Who is this for? Provide one or more use cases.
|
||||
|
||||
### Feature checklist
|
||||
|
||||
Make sure these are completed before closing the issue,
|
||||
with a link to the relevant commit.
|
||||
|
||||
- [ ] [Feature assurance](https://about.gitlab.com/handbook/product/#feature-assurance)
|
||||
- [ ] Documentation
|
||||
- [ ] Added to [features.yml](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
|
||||
|
||||
/label ~"feature proposal"
|
|
@ -35,9 +35,21 @@ linters:
|
|||
HtmlAttributes:
|
||||
enabled: true
|
||||
|
||||
IdNames:
|
||||
enabled: false
|
||||
|
||||
ImplicitDiv:
|
||||
enabled: true
|
||||
|
||||
InlineJavaScript:
|
||||
enabled: true
|
||||
|
||||
InlineStyles:
|
||||
enabled: false
|
||||
|
||||
InstanceVariables:
|
||||
enabled: false
|
||||
|
||||
LeadingCommentSpace:
|
||||
enabled: false
|
||||
|
||||
|
@ -54,6 +66,9 @@ linters:
|
|||
ObjectReferenceAttributes:
|
||||
enabled: true
|
||||
|
||||
RepeatedId:
|
||||
enabled: false
|
||||
|
||||
RuboCop:
|
||||
enabled: false
|
||||
# These cops are incredibly noisy when it comes to HAML templates, so we
|
||||
|
@ -101,3 +116,6 @@ linters:
|
|||
|
||||
UnnecessaryStringOutput:
|
||||
enabled: true
|
||||
|
||||
ViewLength:
|
||||
enabled: false
|
||||
|
|
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
@ -0,0 +1 @@
|
|||
7.5
|
2
.rspec
2
.rspec
|
@ -1,2 +0,0 @@
|
|||
--color
|
||||
--format Fuubar
|
498
.rubocop.yml
498
.rubocop.yml
|
@ -6,6 +6,7 @@ inherit_from: .rubocop_todo.yml
|
|||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.3
|
||||
TargetRailsVersion: 4.2
|
||||
# Cop names are not d§splayed in offense messages by default. Change behavior
|
||||
# by overriding DisplayCopNames, or by giving the -D/--display-cop-names
|
||||
# option.
|
||||
|
@ -29,12 +30,221 @@ AllCops:
|
|||
Bundler/OrderedGems:
|
||||
Enabled: false
|
||||
|
||||
# Style #######################################################################
|
||||
# Layout ######################################################################
|
||||
|
||||
# Check indentation of private/protected visibility modifiers.
|
||||
Style/AccessModifierIndentation:
|
||||
Layout/AccessModifierIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of an array literal if they span more than one line.
|
||||
Layout/AlignArray:
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of a hash literal if they span more than one line.
|
||||
Layout/AlignHash:
|
||||
Enabled: true
|
||||
|
||||
# Here we check if the parameters on a multi-line method call or
|
||||
# definition are aligned.
|
||||
Layout/AlignParameters:
|
||||
Enabled: false
|
||||
|
||||
# Put end statement of multiline block on its own line.
|
||||
Layout/BlockEndNewline:
|
||||
Enabled: true
|
||||
|
||||
# Indentation of when in a case/when/[else/]end.
|
||||
Layout/CaseIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Indentation of comments.
|
||||
Layout/CommentIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Multi-line method chaining should be done with leading dots.
|
||||
Layout/DotPosition:
|
||||
Enabled: true
|
||||
EnforcedStyle: leading
|
||||
|
||||
# Align elses and elsifs correctly.
|
||||
Layout/ElseAlignment:
|
||||
Enabled: true
|
||||
|
||||
# Add an empty line after magic comments to separate them from code.
|
||||
Layout/EmptyLineAfterMagicComment:
|
||||
Enabled: false
|
||||
|
||||
# Use empty lines between defs.
|
||||
Layout/EmptyLineBetweenDefs:
|
||||
Enabled: true
|
||||
|
||||
# Don't use several empty lines in a row.
|
||||
Layout/EmptyLines:
|
||||
Enabled: true
|
||||
|
||||
# Keep blank lines around access modifiers.
|
||||
Layout/EmptyLinesAroundAccessModifier:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around block bodies.
|
||||
Layout/EmptyLinesAroundBlockBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around class bodies.
|
||||
Layout/EmptyLinesAroundClassBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around exception handling keywords.
|
||||
Layout/EmptyLinesAroundExceptionHandlingKeywords:
|
||||
Enabled: false
|
||||
|
||||
# Keeps track of empty lines around method bodies.
|
||||
Layout/EmptyLinesAroundMethodBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around module bodies.
|
||||
Layout/EmptyLinesAroundModuleBody:
|
||||
Enabled: true
|
||||
|
||||
# Use Unix-style line endings.
|
||||
Layout/EndOfLine:
|
||||
Enabled: true
|
||||
|
||||
# Checks for a line break before the first parameter in a multi-line method
|
||||
# parameter definition.
|
||||
Layout/FirstMethodParameterLineBreak:
|
||||
Enabled: true
|
||||
|
||||
# Keep indentation straight.
|
||||
Layout/IndentationConsistency:
|
||||
Enabled: true
|
||||
|
||||
# Use 2 spaces for indentation.
|
||||
Layout/IndentationWidth:
|
||||
Enabled: true
|
||||
|
||||
# Checks the indentation of the first line of the right-hand-side of a
|
||||
# multi-line assignment.
|
||||
Layout/IndentAssignment:
|
||||
Enabled: true
|
||||
|
||||
# This cops checks the indentation of the here document bodies.
|
||||
Layout/IndentHeredoc:
|
||||
Enabled: false
|
||||
|
||||
# Comments should start with a space.
|
||||
Layout/LeadingCommentSpace:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in an array literal is either on the same line
|
||||
# as the last array element, or a new line.
|
||||
Layout/MultilineArrayBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Ensures newlines after multiline block do statements.
|
||||
Layout/MultilineBlockLayout:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in a hash literal is either on the same line as
|
||||
# the last hash element, or a new line.
|
||||
Layout/MultilineHashBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Checks that the closing brace in a method call is either on the same line as
|
||||
# the last method argument, or a new line.
|
||||
Layout/MultilineMethodCallBraceLayout:
|
||||
Enabled: false
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Checks indentation of method calls with the dot operator that span more than
|
||||
# one line.
|
||||
Layout/MultilineMethodCallIndentation:
|
||||
Enabled: false
|
||||
|
||||
# Checks that the closing brace in a method definition is symmetrical with
|
||||
# respect to the opening brace and the method parameters.
|
||||
Layout/MultilineMethodDefinitionBraceLayout:
|
||||
Enabled: false
|
||||
|
||||
# Checks indentation of binary operations that span more than one line.
|
||||
Layout/MultilineOperationIndentation:
|
||||
Enabled: true
|
||||
EnforcedStyle: indented
|
||||
|
||||
# Use spaces after colons.
|
||||
Layout/SpaceAfterColon:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after commas.
|
||||
Layout/SpaceAfterComma:
|
||||
Enabled: true
|
||||
|
||||
# Do not put a space between a method name and the opening parenthesis in a
|
||||
# method definition.
|
||||
Layout/SpaceAfterMethodName:
|
||||
Enabled: true
|
||||
|
||||
# Tracks redundant space after the ! operator.
|
||||
Layout/SpaceAfterNot:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after semicolons.
|
||||
Layout/SpaceAfterSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Use space around equals in parameter default
|
||||
Layout/SpaceAroundEqualsInParameterDefault:
|
||||
Enabled: true
|
||||
|
||||
# Use a space around keywords if appropriate.
|
||||
Layout/SpaceAroundKeyword:
|
||||
Enabled: true
|
||||
|
||||
# Use a single space around operators.
|
||||
Layout/SpaceAroundOperators:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before commas.
|
||||
Layout/SpaceBeforeComma:
|
||||
Enabled: true
|
||||
|
||||
# Checks for missing space between code and a comment on the same line.
|
||||
Layout/SpaceBeforeComment:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before semicolons.
|
||||
Layout/SpaceBeforeSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Checks for spaces inside square brackets.
|
||||
Layout/SpaceInsideBrackets:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces inside hash literal braces - or don't.
|
||||
Layout/SpaceInsideHashLiteralBraces:
|
||||
Enabled: true
|
||||
|
||||
# No spaces inside range literals.
|
||||
Layout/SpaceInsideRangeLiteral:
|
||||
Enabled: true
|
||||
|
||||
# Checks for padding/surrounding spaces inside string interpolation.
|
||||
Layout/SpaceInsideStringInterpolation:
|
||||
EnforcedStyle: no_space
|
||||
Enabled: true
|
||||
|
||||
# No hard tabs.
|
||||
Layout/Tab:
|
||||
Enabled: true
|
||||
|
||||
# Checks trailing blank lines and final newline.
|
||||
Layout/TrailingBlankLines:
|
||||
Enabled: true
|
||||
|
||||
# Style #######################################################################
|
||||
|
||||
# Check the naming of accessor methods for get_/set_.
|
||||
Style/AccessorMethodName:
|
||||
Enabled: false
|
||||
|
@ -44,19 +254,6 @@ Style/Alias:
|
|||
EnforcedStyle: prefer_alias_method
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of an array literal if they span more than one line.
|
||||
Style/AlignArray:
|
||||
Enabled: true
|
||||
|
||||
# Align the elements of a hash literal if they span more than one line.
|
||||
Style/AlignHash:
|
||||
Enabled: true
|
||||
|
||||
# Here we check if the parameters on a multi-line method call or
|
||||
# definition are aligned.
|
||||
Style/AlignParameters:
|
||||
Enabled: false
|
||||
|
||||
# Whether `and` and `or` are banned only in conditionals (conditionals)
|
||||
# or completely (always).
|
||||
Style/AndOr:
|
||||
|
@ -91,10 +288,6 @@ Style/BlockComments:
|
|||
Style/BlockDelimiters:
|
||||
Enabled: true
|
||||
|
||||
# Put end statement of multiline block on its own line.
|
||||
Style/BlockEndNewline:
|
||||
Enabled: true
|
||||
|
||||
# This cop checks for braces around the last parameter in a method call
|
||||
# if the last parameter is a hash.
|
||||
Style/BracesAroundHashParameters:
|
||||
|
@ -104,10 +297,6 @@ Style/BracesAroundHashParameters:
|
|||
Style/CaseEquality:
|
||||
Enabled: false
|
||||
|
||||
# Indentation of when in a case/when/[else/]end.
|
||||
Style/CaseIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Checks for uses of character literals.
|
||||
Style/CharacterLiteral:
|
||||
Enabled: true
|
||||
|
@ -142,10 +331,6 @@ Style/ColonMethodCall:
|
|||
Style/CommentAnnotation:
|
||||
Enabled: false
|
||||
|
||||
# Indentation of comments.
|
||||
Style/CommentIndentation:
|
||||
Enabled: true
|
||||
|
||||
# Check for `if` and `case` statements where each branch is used for
|
||||
# assignment to the same variable when using the return of the
|
||||
# condition can be used instead.
|
||||
|
@ -170,46 +355,10 @@ Style/Documentation:
|
|||
Style/DoubleNegation:
|
||||
Enabled: false
|
||||
|
||||
# Align elses and elsifs correctly.
|
||||
Style/ElseAlignment:
|
||||
Enabled: true
|
||||
|
||||
# Use empty lines between defs.
|
||||
Style/EmptyLineBetweenDefs:
|
||||
Enabled: true
|
||||
|
||||
# Don't use several empty lines in a row.
|
||||
Style/EmptyLines:
|
||||
Enabled: true
|
||||
|
||||
# Keep blank lines around access modifiers.
|
||||
Style/EmptyLinesAroundAccessModifier:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around block bodies.
|
||||
Style/EmptyLinesAroundBlockBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around class bodies.
|
||||
Style/EmptyLinesAroundClassBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around method bodies.
|
||||
Style/EmptyLinesAroundMethodBody:
|
||||
Enabled: true
|
||||
|
||||
# Keeps track of empty lines around module bodies.
|
||||
Style/EmptyLinesAroundModuleBody:
|
||||
Enabled: true
|
||||
|
||||
# Avoid the use of END blocks.
|
||||
Style/EndBlock:
|
||||
Enabled: true
|
||||
|
||||
# Use Unix-style line endings.
|
||||
Style/EndOfLine:
|
||||
Enabled: true
|
||||
|
||||
# Favor the use of Fixnum#even? && Fixnum#odd?
|
||||
Style/EvenOdd:
|
||||
Enabled: true
|
||||
|
@ -218,11 +367,6 @@ Style/EvenOdd:
|
|||
Style/FileName:
|
||||
Enabled: true
|
||||
|
||||
# Checks for a line break before the first parameter in a multi-line method
|
||||
# parameter definition.
|
||||
Style/FirstMethodParameterLineBreak:
|
||||
Enabled: true
|
||||
|
||||
# Checks for flip flops.
|
||||
Style/FlipFlop:
|
||||
Enabled: true
|
||||
|
@ -231,6 +375,10 @@ Style/FlipFlop:
|
|||
Style/For:
|
||||
Enabled: true
|
||||
|
||||
# Use a consistent style for format string tokens.
|
||||
Style/FormatStringToken:
|
||||
Enabled: false
|
||||
|
||||
# Checks if there is a magic comment to enforce string literals
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
|
@ -256,31 +404,19 @@ Style/IdenticalConditionalBranches:
|
|||
Style/IfWithSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Checks the indentation of the first line of the right-hand-side of a
|
||||
# multi-line assignment.
|
||||
Style/IndentAssignment:
|
||||
Enabled: true
|
||||
|
||||
# Keep indentation straight.
|
||||
Style/IndentationConsistency:
|
||||
Enabled: true
|
||||
|
||||
# Use 2 spaces for indentation.
|
||||
Style/IndentationWidth:
|
||||
Enabled: true
|
||||
|
||||
# Use Kernel#loop for infinite loops.
|
||||
Style/InfiniteLoop:
|
||||
Enabled: true
|
||||
|
||||
# Use the inverse method instead of `!.method`
|
||||
# if an inverse method is defined.
|
||||
Style/InverseMethods:
|
||||
Enabled: false
|
||||
|
||||
# Use lambda.call(...) instead of lambda.(...).
|
||||
Style/LambdaCall:
|
||||
Enabled: true
|
||||
|
||||
# Comments should start with a space.
|
||||
Style/LeadingCommentSpace:
|
||||
Enabled: true
|
||||
|
||||
# Checks if the method definitions have or don't have parentheses.
|
||||
Style/MethodDefParentheses:
|
||||
Enabled: true
|
||||
|
@ -293,55 +429,23 @@ Style/MethodName:
|
|||
Style/ModuleFunction:
|
||||
Enabled: false
|
||||
|
||||
# Checks that the closing brace in an array literal is either on the same line
|
||||
# as the last array element, or a new line.
|
||||
Style/MultilineArrayBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Avoid multi-line chains of blocks.
|
||||
Style/MultilineBlockChain:
|
||||
Enabled: true
|
||||
|
||||
# Ensures newlines after multiline block do statements.
|
||||
Style/MultilineBlockLayout:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in a hash literal is either on the same line as
|
||||
# the last hash element, or a new line.
|
||||
Style/MultilineHashBraceLayout:
|
||||
Enabled: true
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Do not use then for multi-line if/unless.
|
||||
Style/MultilineIfThen:
|
||||
Enabled: true
|
||||
|
||||
# Checks that the closing brace in a method call is either on the same line as
|
||||
# the last method argument, or a new line.
|
||||
Style/MultilineMethodCallBraceLayout:
|
||||
Enabled: false
|
||||
EnforcedStyle: symmetrical
|
||||
|
||||
# Checks indentation of method calls with the dot operator that span more than
|
||||
# one line.
|
||||
Style/MultilineMethodCallIndentation:
|
||||
Enabled: false
|
||||
|
||||
# Checks that the closing brace in a method definition is symmetrical with
|
||||
# respect to the opening brace and the method parameters.
|
||||
Style/MultilineMethodDefinitionBraceLayout:
|
||||
Enabled: false
|
||||
|
||||
# Checks indentation of binary operations that span more than one line.
|
||||
Style/MultilineOperationIndentation:
|
||||
Enabled: true
|
||||
EnforcedStyle: indented
|
||||
|
||||
# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
|
||||
Style/MultilineTernaryOperator:
|
||||
Enabled: true
|
||||
|
||||
# Avoid comparing a variable with multiple items in a conditional,
|
||||
# use Array#include? instead.
|
||||
Style/MultipleComparison:
|
||||
Enabled: false
|
||||
|
||||
# This cop checks whether some constant value isn't a
|
||||
# mutable literal (e.g. array or hash).
|
||||
Style/MutableConstant:
|
||||
|
@ -390,6 +494,15 @@ Style/OpMethod:
|
|||
Style/ParenthesesAroundCondition:
|
||||
Enabled: true
|
||||
|
||||
# This cop (by default) checks for uses of methods Hash#has_key? and
|
||||
# Hash#has_value? where it enforces Hash#key? and Hash#value?
|
||||
# It is configurable to enforce the inverse, using `verbose` method
|
||||
# names also.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: short, verbose
|
||||
Style/PreferredHashMethods:
|
||||
Enabled: false
|
||||
|
||||
# Checks for an obsolete RuntimeException argument in raise/fail.
|
||||
Style/RedundantException:
|
||||
Enabled: true
|
||||
|
@ -407,68 +520,6 @@ Style/SignalException:
|
|||
EnforcedStyle: only_raise
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after colons.
|
||||
Style/SpaceAfterColon:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after commas.
|
||||
Style/SpaceAfterComma:
|
||||
Enabled: true
|
||||
|
||||
# Do not put a space between a method name and the opening parenthesis in a
|
||||
# method definition.
|
||||
Style/SpaceAfterMethodName:
|
||||
Enabled: true
|
||||
|
||||
# Tracks redundant space after the ! operator.
|
||||
Style/SpaceAfterNot:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces after semicolons.
|
||||
Style/SpaceAfterSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Use space around equals in parameter default
|
||||
Style/SpaceAroundEqualsInParameterDefault:
|
||||
Enabled: true
|
||||
|
||||
# Use a space around keywords if appropriate.
|
||||
Style/SpaceAroundKeyword:
|
||||
Enabled: true
|
||||
|
||||
# Use a single space around operators.
|
||||
Style/SpaceAroundOperators:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before commas.
|
||||
Style/SpaceBeforeComma:
|
||||
Enabled: true
|
||||
|
||||
# Checks for missing space between code and a comment on the same line.
|
||||
Style/SpaceBeforeComment:
|
||||
Enabled: true
|
||||
|
||||
# No spaces before semicolons.
|
||||
Style/SpaceBeforeSemicolon:
|
||||
Enabled: true
|
||||
|
||||
# Checks for spaces inside square brackets.
|
||||
Style/SpaceInsideBrackets:
|
||||
Enabled: true
|
||||
|
||||
# Use spaces inside hash literal braces - or don't.
|
||||
Style/SpaceInsideHashLiteralBraces:
|
||||
Enabled: true
|
||||
|
||||
# No spaces inside range literals.
|
||||
Style/SpaceInsideRangeLiteral:
|
||||
Enabled: true
|
||||
|
||||
# Checks for padding/surrounding spaces inside string interpolation.
|
||||
Style/SpaceInsideStringInterpolation:
|
||||
EnforcedStyle: no_space
|
||||
Enabled: true
|
||||
|
||||
# Check for the usage of parentheses around stabby lambda arguments.
|
||||
Style/StabbyLambdaParentheses:
|
||||
EnforcedStyle: require_parentheses
|
||||
|
@ -484,17 +535,19 @@ Style/StringMethods:
|
|||
intern: to_sym
|
||||
Enabled: true
|
||||
|
||||
# No hard tabs.
|
||||
Style/Tab:
|
||||
Enabled: true
|
||||
|
||||
# Checks trailing blank lines and final newline.
|
||||
Style/TrailingBlankLines:
|
||||
Enabled: true
|
||||
# Use %i or %I for arrays of symbols.
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
# This cop checks for trailing comma in array and hash literals.
|
||||
Style/TrailingCommaInLiteral:
|
||||
Enabled: false
|
||||
Enabled: true
|
||||
EnforcedStyleForMultiline: no_comma
|
||||
|
||||
# This cop checks for trailing comma in argument lists.
|
||||
Style/TrailingCommaInArguments:
|
||||
Enabled: true
|
||||
EnforcedStyleForMultiline: no_comma
|
||||
|
||||
# Checks for %W when interpolation is not needed.
|
||||
Style/UnneededCapitalW:
|
||||
|
@ -533,6 +586,10 @@ Style/WhileUntilModifier:
|
|||
Style/WordArray:
|
||||
Enabled: true
|
||||
|
||||
# Do not use literals as the first operand of a comparison.
|
||||
Style/YodaCondition:
|
||||
Enabled: false
|
||||
|
||||
# Use `proc` instead of `Proc.new`.
|
||||
Style/Proc:
|
||||
Enabled: true
|
||||
|
@ -543,7 +600,7 @@ Style/Proc:
|
|||
# branches, and conditions.
|
||||
Metrics/AbcSize:
|
||||
Enabled: true
|
||||
Max: 57.08
|
||||
Max: 56.96
|
||||
|
||||
# This cop checks if the length of a block exceeds some maximum value.
|
||||
Metrics/BlockLength:
|
||||
|
@ -588,6 +645,11 @@ Metrics/PerceivedComplexity:
|
|||
|
||||
# Lint ########################################################################
|
||||
|
||||
# Checks for ambiguous block association with method when param passed without
|
||||
# parentheses.
|
||||
Lint/AmbiguousBlockAssociation:
|
||||
Enabled: false
|
||||
|
||||
# Checks for ambiguous operators in the first argument of a method invocation
|
||||
# without parentheses.
|
||||
Lint/AmbiguousOperator:
|
||||
|
@ -789,6 +851,10 @@ Lint/Void:
|
|||
|
||||
# Performance #################################################################
|
||||
|
||||
# Use `caller(n..n)` instead of `caller`.
|
||||
Performance/Caller:
|
||||
Enabled: false
|
||||
|
||||
# Use `casecmp` rather than `downcase ==`.
|
||||
Performance/Casecmp:
|
||||
Enabled: true
|
||||
|
@ -863,14 +929,23 @@ Rails/ActionFilter:
|
|||
Enabled: true
|
||||
EnforcedStyle: action
|
||||
|
||||
# Check that models subclass ApplicationRecord.
|
||||
Rails/ApplicationRecord:
|
||||
Enabled: false
|
||||
|
||||
# Enforce using `blank?` and `present?`.
|
||||
Rails/Blank:
|
||||
Enabled: false
|
||||
|
||||
# Checks the correct usage of date aware methods, such as `Date.today`,
|
||||
# `Date.current`, etc.
|
||||
Rails/Date:
|
||||
Enabled: false
|
||||
|
||||
# Prefer delegate method for delegations.
|
||||
# Disabled per https://gitlab.com/gitlab-org/gitlab-ce/issues/35869
|
||||
Rails/Delegate:
|
||||
Enabled: true
|
||||
Enabled: false
|
||||
|
||||
# This cop checks dynamic `find_by_*` methods.
|
||||
Rails/DynamicFindBy:
|
||||
|
@ -919,10 +994,18 @@ Rails/OutputSafety:
|
|||
Rails/PluralizationGrammar:
|
||||
Enabled: true
|
||||
|
||||
# Enforce using `blank?` and `present?`.
|
||||
Rails/Present:
|
||||
Enabled: false
|
||||
|
||||
# Checks for `read_attribute(:attr)` and `write_attribute(:attr, val)`.
|
||||
Rails/ReadWriteAttribute:
|
||||
Enabled: false
|
||||
|
||||
# Do not assign relative date to constants.
|
||||
Rails/RelativeDateConstant:
|
||||
Enabled: false
|
||||
|
||||
# Checks the arguments of ActiveRecord scopes.
|
||||
Rails/ScopeArgs:
|
||||
Enabled: true
|
||||
|
@ -945,6 +1028,10 @@ RSpec/AnyInstance:
|
|||
RSpec/BeEql:
|
||||
Enabled: true
|
||||
|
||||
# We don't enforce this as we use this technique in a few places.
|
||||
RSpec/BeforeAfterAll:
|
||||
Enabled: false
|
||||
|
||||
# Check that the first argument to the top level describe is the tested class or
|
||||
# module.
|
||||
RSpec/DescribeClass:
|
||||
|
@ -963,6 +1050,12 @@ RSpec/DescribeSymbol:
|
|||
RSpec/DescribedClass:
|
||||
Enabled: true
|
||||
|
||||
# Checks if an example group does not include any tests.
|
||||
RSpec/EmptyExampleGroup:
|
||||
Enabled: true
|
||||
CustomIncludeMethods:
|
||||
- run_permission_checks
|
||||
|
||||
# Checks for long example.
|
||||
RSpec/ExampleLength:
|
||||
Enabled: false
|
||||
|
@ -981,6 +1074,10 @@ RSpec/ExampleWording:
|
|||
RSpec/ExpectActual:
|
||||
Enabled: true
|
||||
|
||||
# Checks for opportunities to use `expect { … }.to output`.
|
||||
RSpec/ExpectOutput:
|
||||
Enabled: true
|
||||
|
||||
# Checks the file and folder naming of the spec file.
|
||||
RSpec/FilePath:
|
||||
Enabled: true
|
||||
|
@ -994,6 +1091,12 @@ RSpec/FilePath:
|
|||
RSpec/Focus:
|
||||
Enabled: true
|
||||
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: is_expected, should
|
||||
RSpec/ImplicitExpect:
|
||||
Enabled: true
|
||||
EnforcedStyle: is_expected
|
||||
|
||||
# Checks for the usage of instance variables.
|
||||
RSpec/InstanceVariable:
|
||||
Enabled: false
|
||||
|
@ -1039,6 +1142,13 @@ RSpec/NotToNot:
|
|||
RSpec/RepeatedDescription:
|
||||
Enabled: false
|
||||
|
||||
# Ensure RSpec hook blocks are always multi-line.
|
||||
RSpec/SingleLineHook:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'spec/factories/*'
|
||||
- 'spec/requests/api/v3/*'
|
||||
|
||||
# Checks for stubbed test subjects.
|
||||
RSpec/SubjectStub:
|
||||
Enabled: false
|
||||
|
|
|
@ -1,45 +1,89 @@
|
|||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --exclude-limit 0`
|
||||
# on 2017-04-07 20:17:35 -0400 using RuboCop version 0.47.1.
|
||||
# on 2017-07-10 01:48:30 +0900 using RuboCop version 0.49.1.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Offense count: 54
|
||||
RSpec/BeforeAfterAll:
|
||||
# Offense count: 181
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
|
||||
Layout/ExtraSpacing:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 15
|
||||
# Configuration parameters: CustomIncludeMethods.
|
||||
RSpec/EmptyExampleGroup:
|
||||
# Offense count: 119
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
|
||||
Layout/IndentArray:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 233
|
||||
# Offense count: 208
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_braces
|
||||
Layout/IndentHash:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 174
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: space, no_space
|
||||
Layout/SpaceBeforeBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment.
|
||||
Layout/SpaceBeforeFirstArg:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 64
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: require_no_space, require_space
|
||||
Layout/SpaceInLambdaLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 256
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
|
||||
# SupportedStyles: space, no_space
|
||||
# SupportedStylesForEmptyBraces: space, no_space
|
||||
Layout/SpaceInsideBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 135
|
||||
# Cop supports --auto-correct.
|
||||
Layout/SpaceInsideParens:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 14
|
||||
# Cop supports --auto-correct.
|
||||
Layout/SpaceInsidePercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 89
|
||||
# Cop supports --auto-correct.
|
||||
Layout/TrailingWhitespace:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 272
|
||||
RSpec/EmptyLineAfterFinalLet:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 167
|
||||
# Offense count: 181
|
||||
RSpec/EmptyLineAfterSubject:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
RSpec/ExpectOutput:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 72
|
||||
# Offense count: 78
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: implicit, each, example
|
||||
RSpec/HookArgument:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 12
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: is_expected, should
|
||||
RSpec/ImplicitExpect:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 11
|
||||
# Offense count: 9
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: it_behaves_like, it_should_behave_like
|
||||
RSpec/ItBehavesLike:
|
||||
|
@ -49,19 +93,19 @@ RSpec/ItBehavesLike:
|
|||
RSpec/IteratedExpectation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 2
|
||||
RSpec/OverwritingSetup:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 34
|
||||
# Offense count: 36
|
||||
RSpec/RepeatedExample:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 43
|
||||
# Offense count: 86
|
||||
RSpec/ScatteredLet:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 32
|
||||
# Offense count: 20
|
||||
RSpec/ScatteredSetup:
|
||||
Enabled: false
|
||||
|
||||
|
@ -69,7 +113,7 @@ RSpec/ScatteredSetup:
|
|||
RSpec/SharedContext:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 150
|
||||
# Offense count: 115
|
||||
Rails/FilePath:
|
||||
Enabled: false
|
||||
|
||||
|
@ -79,97 +123,71 @@ Rails/FilePath:
|
|||
Rails/ReversibleMigration:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 302
|
||||
# Offense count: 336
|
||||
# Configuration parameters: Blacklist.
|
||||
# Blacklist: decrement!, decrement_counter, increment!, increment_counter, toggle!, touch, update_all, update_attribute, update_column, update_columns, update_counters
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 7
|
||||
# Offense count: 11
|
||||
# Cop supports --auto-correct.
|
||||
Security/YAMLLoad:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 59
|
||||
# Offense count: 58
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: percent_q, bare_percent
|
||||
Style/BarePercentLiterals:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 1403
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: leading, trailing
|
||||
Style/DotPosition:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 5
|
||||
# Offense count: 6
|
||||
# Cop supports --auto-correct.
|
||||
Style/EachWithObject:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 28
|
||||
# Offense count: 31
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: empty, nil, both
|
||||
Style/EmptyElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 4
|
||||
# Offense count: 9
|
||||
# Cop supports --auto-correct.
|
||||
Style/EmptyLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 59
|
||||
# Offense count: 78
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: compact, expanded
|
||||
Style/EmptyMethod:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 214
|
||||
# Offense count: 23
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
|
||||
Style/ExtraSpacing:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 9
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: format, sprintf, percent
|
||||
Style/FormatString:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 285
|
||||
# Offense count: 301
|
||||
# Configuration parameters: MinBodyLength.
|
||||
Style/GuardClause:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 16
|
||||
# Offense count: 18
|
||||
Style/IfInsideElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 186
|
||||
# Offense count: 182
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: MaxLineLength.
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 99
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_brackets
|
||||
Style/IndentArray:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 160
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
|
||||
# SupportedStyles: special_inside_parentheses, consistent, align_braces
|
||||
Style/IndentHash:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 50
|
||||
# Offense count: 52
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: line_count_dependent, lambda, literal
|
||||
|
@ -181,63 +199,63 @@ Style/Lambda:
|
|||
Style/LineEndConcatenation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 34
|
||||
# Offense count: 40
|
||||
# Cop supports --auto-correct.
|
||||
Style/MethodCallWithoutArgsParentheses:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 10
|
||||
# Offense count: 13
|
||||
Style/MethodMissing:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 6
|
||||
# Cop supports --auto-correct.
|
||||
Style/MultilineIfModifier:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 26
|
||||
# Cop supports --auto-correct.
|
||||
Style/NestedParenthesizedCalls:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 18
|
||||
# Offense count: 20
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
|
||||
# SupportedStyles: skip_modifier_ifs, always
|
||||
Style/Next:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 37
|
||||
# Offense count: 45
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedOctalStyle, SupportedOctalStyles.
|
||||
# SupportedOctalStyles: zero_with_o, zero_only
|
||||
Style/NumericLiteralPrefix:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 88
|
||||
# Offense count: 98
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: predicate, comparison
|
||||
Style/NumericPredicate:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 36
|
||||
# Offense count: 42
|
||||
# Cop supports --auto-correct.
|
||||
Style/ParallelAssignment:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 570
|
||||
# Offense count: 800
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: PreferredDelimiters.
|
||||
Style/PercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 14
|
||||
# Offense count: 15
|
||||
# Cop supports --auto-correct.
|
||||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 83
|
||||
# Offense count: 105
|
||||
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
|
||||
# NamePrefix: is_, has_, have_
|
||||
# NamePrefixBlacklist: is_, has_, have_
|
||||
|
@ -245,54 +263,47 @@ Style/PerlBackrefs:
|
|||
Style/PredicateName:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 45
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: short, verbose
|
||||
Style/PreferredHashMethods:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 65
|
||||
# Offense count: 58
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: compact, exploded
|
||||
Style/RaiseArgs:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 5
|
||||
# Offense count: 6
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantBegin:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 32
|
||||
# Offense count: 37
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantFreeze:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 15
|
||||
# Offense count: 14
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowMultipleReturnValues.
|
||||
Style/RedundantReturn:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 382
|
||||
# Offense count: 406
|
||||
# Cop supports --auto-correct.
|
||||
Style/RedundantSelf:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 111
|
||||
# Offense count: 115
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
|
||||
# SupportedStyles: slashes, percent_r, mixed
|
||||
Style/RegexpLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 29
|
||||
# Cop supports --auto-correct.
|
||||
Style/RescueModifier:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 7
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
Style/SelfAssignment:
|
||||
Enabled: false
|
||||
|
@ -303,108 +314,58 @@ Style/SelfAssignment:
|
|||
Style/SingleLineMethods:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 168
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: space, no_space
|
||||
Style/SpaceBeforeBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowForAlignment.
|
||||
Style/SpaceBeforeFirstArg:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 46
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: require_no_space, require_space
|
||||
Style/SpaceInLambdaLiteral:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 229
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
|
||||
# SupportedStyles: space, no_space
|
||||
# SupportedStylesForEmptyBraces: space, no_space
|
||||
Style/SpaceInsideBlockBraces:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 116
|
||||
# Cop supports --auto-correct.
|
||||
Style/SpaceInsideParens:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 12
|
||||
# Cop supports --auto-correct.
|
||||
Style/SpaceInsidePercentLiteralDelimiters:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 57
|
||||
# Offense count: 64
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: SupportedStyles.
|
||||
# SupportedStyles: use_perl_names, use_english_names
|
||||
Style/SpecialGlobalVars:
|
||||
EnforcedStyle: use_perl_names
|
||||
|
||||
# Offense count: 42
|
||||
# Offense count: 44
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
||||
# SupportedStyles: single_quotes, double_quotes
|
||||
Style/StringLiteralsInInterpolation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 64
|
||||
# Offense count: 84
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: IgnoredMethods.
|
||||
# IgnoredMethods: respond_to, define_method
|
||||
Style/SymbolProc:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 6
|
||||
# Offense count: 8
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle, SupportedStyles, AllowSafeAssignment.
|
||||
# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
|
||||
Style/TernaryParentheses:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 53
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline.
|
||||
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
|
||||
Style/TrailingCommaInArguments:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 18
|
||||
# Offense count: 17
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: AllowNamedUnderscoreVariables.
|
||||
Style/TrailingUnderscoreVariable:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 78
|
||||
# Cop supports --auto-correct.
|
||||
Style/TrailingWhitespace:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 3
|
||||
# Offense count: 4
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
|
||||
# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
|
||||
Style/TrivialAccessors:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 6
|
||||
# Offense count: 5
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnlessElse:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 24
|
||||
# Offense count: 28
|
||||
# Cop supports --auto-correct.
|
||||
Style/UnneededInterpolation:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 8
|
||||
# Offense count: 11
|
||||
# Cop supports --auto-correct.
|
||||
Style/ZeroLengthPredicate:
|
||||
Enabled: false
|
||||
|
|
|
@ -10,12 +10,12 @@ linters:
|
|||
# Reports when you use improper spacing around ! (the "bang") in !default,
|
||||
# !global, !important, and !optional flags.
|
||||
BangFormat:
|
||||
enabled: false
|
||||
|
||||
enabled: true
|
||||
|
||||
# Whether or not to prefer `border: 0` over `border: none`.
|
||||
BorderZero:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Reports when you define a rule set using a selector with chained classes
|
||||
# (a.k.a. adjoining classes).
|
||||
ChainedClasses:
|
||||
|
@ -25,13 +25,13 @@ linters:
|
|||
# (e.g. `color: green` is a color keyword)
|
||||
ColorKeyword:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Prefer color literals (keywords or hexadecimal codes) to be used only in
|
||||
# variable declarations. They should be referred to via variables everywhere
|
||||
# else.
|
||||
ColorVariable:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Which form of comments to prefer in CSS.
|
||||
Comment:
|
||||
enabled: false
|
||||
|
@ -39,14 +39,15 @@ linters:
|
|||
# Reports @debug statements (which you probably left behind accidentally).
|
||||
DebugStatement:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Rule sets should be ordered as follows:
|
||||
# - @extend declarations
|
||||
# - @include declarations without inner @content
|
||||
# - properties, @include declarations with inner @content
|
||||
# - properties
|
||||
# - @include declarations with inner @content
|
||||
# - nested rule sets.
|
||||
DeclarationOrder:
|
||||
enabled: false
|
||||
enabled: true
|
||||
|
||||
# `scss-lint:disable` control comments should be preceded by a comment
|
||||
# explaining why these linters are being disabled for this file.
|
||||
|
@ -54,19 +55,19 @@ linters:
|
|||
# more information.
|
||||
DisableLinterReason:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Reports when you define the same property twice in a single rule set.
|
||||
DuplicateProperty:
|
||||
enabled: false
|
||||
|
||||
enabled: true
|
||||
|
||||
# Separate rule, function, and mixin declarations with empty lines.
|
||||
EmptyLineBetweenBlocks:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Reports when you have an empty rule set.
|
||||
EmptyRule:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Reports when you have an @extend directive.
|
||||
ExtendDirective:
|
||||
enabled: false
|
||||
|
@ -75,49 +76,49 @@ linters:
|
|||
# when adding lines to the file, since SCM systems such as git won't
|
||||
# think that you touched the last line.
|
||||
FinalNewline:
|
||||
enabled: false
|
||||
|
||||
enabled: true
|
||||
|
||||
# HEX colors should use three-character values where possible.
|
||||
HexLength:
|
||||
enabled: false
|
||||
|
||||
|
||||
# HEX color values should use lower-case colors to differentiate between
|
||||
# letters and numbers, e.g. `#E3E3E3` vs. `#e3e3e3`.
|
||||
HexNotation:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Avoid using ID selectors.
|
||||
IdSelector:
|
||||
enabled: false
|
||||
|
||||
|
||||
# The basenames of @imported SCSS partials should not begin with an
|
||||
# underscore and should not include the filename extension.
|
||||
ImportPath:
|
||||
enabled: false
|
||||
|
||||
enabled: true
|
||||
|
||||
# Avoid using !important in properties. It is usually indicative of a
|
||||
# misunderstanding of CSS specificity and can lead to brittle code.
|
||||
ImportantRule:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Indentation should always be done in increments of 2 spaces.
|
||||
Indentation:
|
||||
enabled: true
|
||||
width: 2
|
||||
|
||||
|
||||
# Don't write leading zeros for numeric values with a decimal point.
|
||||
LeadingZero:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Reports when you define the same selector twice in a single sheet.
|
||||
MergeableSelector:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Functions, mixins, variables, and placeholders should be declared
|
||||
# with all lowercase letters and hyphens instead of underscores.
|
||||
NameFormat:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Avoid nesting selectors too deeply.
|
||||
NestingDepth:
|
||||
enabled: false
|
||||
|
@ -129,12 +130,12 @@ linters:
|
|||
# Sort properties in a strict order.
|
||||
PropertySortOrder:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Reports when you use an unknown or disabled CSS property
|
||||
# (ignoring vendor-prefixed properties).
|
||||
PropertySpelling:
|
||||
enabled: false
|
||||
|
||||
enabled: true
|
||||
|
||||
# Configure which units are allowed for property values.
|
||||
PropertyUnits:
|
||||
enabled: false
|
||||
|
@ -144,25 +145,25 @@ linters:
|
|||
# be declared with one colon.
|
||||
PseudoElement:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
|
||||
QualifyingElement:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Don't write selectors with a depth of applicability greater than 3.
|
||||
SelectorDepth:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Selectors should always use hyphenated-lowercase, rather than camelCase or
|
||||
# snake_case.
|
||||
SelectorFormat:
|
||||
enabled: false
|
||||
convention: hyphenated_lowercase
|
||||
|
||||
|
||||
# Prefer the shortest shorthand form possible for properties that support it.
|
||||
Shorthand:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Each property should have its own line, except in the special case of
|
||||
# single line rulesets.
|
||||
SingleLinePerProperty:
|
||||
|
@ -173,11 +174,15 @@ linters:
|
|||
# individual selector occupy a single line.
|
||||
SingleLinePerSelector:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Commas in lists should be followed by a space.
|
||||
SpaceAfterComma:
|
||||
enabled: true
|
||||
|
||||
# Comment literals should be followed by a space.
|
||||
SpaceAfterComment:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Properties should be formatted with a single space separating the colon
|
||||
# from the property's value.
|
||||
SpaceAfterPropertyColon:
|
||||
|
@ -197,12 +202,12 @@ linters:
|
|||
# colon.
|
||||
SpaceAfterVariableName:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Operators should be formatted with a single space on both sides of an
|
||||
# infix operator.
|
||||
SpaceAroundOperator:
|
||||
enabled: true
|
||||
|
||||
|
||||
# Opening braces should be preceded by a single space.
|
||||
SpaceBeforeBrace:
|
||||
enabled: true
|
||||
|
@ -210,7 +215,7 @@ linters:
|
|||
# Parentheses should not be padded with spaces.
|
||||
SpaceBetweenParens:
|
||||
enabled: false
|
||||
|
||||
|
||||
# Enforces that string literals should be written with a consistent form
|
||||
# of quotes (single or double).
|
||||
StringQuotes:
|
||||
|
@ -240,8 +245,8 @@ linters:
|
|||
# Do not use parent selector references (&) when they would otherwise
|
||||
# be unnecessary.
|
||||
UnnecessaryParentReference:
|
||||
enabled: false
|
||||
|
||||
enabled: true
|
||||
|
||||
# URLs should be valid and not contain protocols or domain names.
|
||||
UrlFormat:
|
||||
enabled: true
|
||||
|
|
796
CHANGELOG.md
796
CHANGELOG.md
|
@ -2,10 +2,712 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 9.2.10 (2017-08-09)
|
||||
## 9.5.4 (2017-09-06)
|
||||
|
||||
- [SECURITY] Upgrade mail and nokogiri gems due to security issues. !13662 (Markus Koller)
|
||||
- [SECURITY] Prevent a persistent XSS in the commit author block.
|
||||
- Fix XSS issue in go-get handling.
|
||||
- Resolve CSRF token leakage via pathname manipulation on environments page.
|
||||
- Fixes race condition in project uploads.
|
||||
- Disallow arbitrary properties in `th` and `td` `style` attributes.
|
||||
- Disallow the `name` attribute on all user-provided markup.
|
||||
|
||||
## 9.5.3 (2017-09-03)
|
||||
|
||||
- [SECURITY] Filter additional secrets from Rails logs.
|
||||
- [FIXED] Make username update fail if the namespace update fails. !13642
|
||||
- [FIXED] Fix failure when issue is authored by a deleted user. !13807
|
||||
- [FIXED] Reverts changes made to signin_enabled. !13956
|
||||
- [FIXED] Fix Merge when pipeline succeeds button dropdown caret icon horizontal alignment.
|
||||
- [FIXED] Fixed diff changes bar buttons from showing/hiding whilst scrolling.
|
||||
- [FIXED] Fix events error importing GitLab projects.
|
||||
- [FIXED] Fix pipeline trigger via API fails with 500 Internal Server Error in 9.5.
|
||||
- [FIXED] Fixed fly-out nav flashing in & out.
|
||||
- [FIXED] Remove closing external issues by reference error.
|
||||
- [FIXED] Re-allow appearances.description_html to be NULL.
|
||||
- [CHANGED] Update and fix resolvable note icons for easier recognition.
|
||||
- [OTHER] Eager load head pipeline projects for MRs index.
|
||||
- [OTHER] Instrument MergeRequest#fetch_ref.
|
||||
- [OTHER] Instrument MergeRequest#ensure_ref_fetched.
|
||||
|
||||
## 9.5.2 (2017-08-28)
|
||||
|
||||
- [FIXED] Fix signing in using LDAP when attribute mapping uses simple strings instead of arrays.
|
||||
- [FIXED] Show un-highlighted text diffs when we do not have references to the correct blobs.
|
||||
- [FIXED] Fix display of push events for removed refs.
|
||||
- [FIXED] Testing of some integrations were broken due to missing ServiceHook record.
|
||||
- [FIXED] Fire system hooks when a user is created via LDAP.
|
||||
- [FIXED] Fix new project form not resetting the template value.
|
||||
|
||||
## 9.5.1 (2017-08-23)
|
||||
|
||||
- [FIXED] Fix merge request pipeline status when pipeline has errors. !13664
|
||||
- [FIXED] Commit rows would occasionally render with the wrong language.
|
||||
- [FIXED] Fix caching of future broadcast messages.
|
||||
- [FIXED] Only require Sidekiq throttling library when enabled, to reduce cache misses.
|
||||
- Raise Housekeeping timeout to 24 hours. !13719
|
||||
|
||||
## 9.5.0 (2017-08-22)
|
||||
|
||||
- [FIXED] Fix timeouts when creating projects in groups with many members. !13508
|
||||
- [FIXED] Improve API pagination headers when no record found. !13629 (Jordan Patterson)
|
||||
- [FIXED] Fix deleting GitLab Pages files when a project is removed. !13631
|
||||
- [FIXED] Fix commit list not loading the correct page when scrolling.
|
||||
- [OTHER] Cache the number of forks of a project. !13535
|
||||
- GPG signed commits integration. !9546 (Alexis Reigel)
|
||||
- Alert the user if a Wiki page changed while they were editing it in order to prevent overwriting changes. !9707 (Hiroyuki Sato)
|
||||
- Add custom linter for inline JavaScript to haml_lint. !9742 (winniehell)
|
||||
- Add /shrug and /tableflip commands. !10068 (Alex Ives)
|
||||
- Allow wiki pages to be renamed in the UI. !10069 (wendy0402)
|
||||
- Insert user name directly without encoding. !10085 (Nathan Neulinger <nneul@neulinger.org>)
|
||||
- Avoid plucking Todo ids in TodoService. !10845
|
||||
- Handle errors while a project is being deleted asynchronously. !11088
|
||||
- Decrease ABC threshold to 56.96. !11227 (Maxim Rydkin)
|
||||
- Remove Mattermost team when deleting a group. !11362
|
||||
- Block access to failing repository storage. !11449
|
||||
- Add coordinator url to admin area runner page. !11603
|
||||
- Allow testing any events for project hooks and system hooks. !11728 (Alexander Randa (@randaalex))
|
||||
- Disallow running the pipeline if ref is protected and user cannot merge the branch or create the tag. !11910
|
||||
- Remove project_key from the Jira configuration. !12050
|
||||
- Add CSRF token verification to API. !12154 (Vitaliy @blackst0ne Klachkov)
|
||||
- Fixes needed when GitLab sign-in is not enabled. !12491 (Robin Bobbitt)
|
||||
- Lazy load images for better Frontend performance. !12503
|
||||
- Replaces dashboard/event_filters.feature spinach with rspec. !12651 (Alexander Randa (@randaalex))
|
||||
- Toggle import description with import_sources_enabled. !12691 (Brianna Kicia)
|
||||
- Bump scss-lint to 0.54.0. !12733 (Takuya Noguchi)
|
||||
- Enable SpaceAfterComma in scss-lint. !12734 (Takuya Noguchi)
|
||||
- Remove CSS for nprogress removed. !12737 (Takuya Noguchi)
|
||||
- Enable UnnecessaryParentReference in scss-lint. !12738 (Takuya Noguchi)
|
||||
- Extract "@request.env[devise.mapping] = Devise.mappings[:user]" to a test helper. !12742 (Jacopo Beschi @jacopo-beschi)
|
||||
- Enable ImportPath in scss-lint. !12749 (Takuya Noguchi)
|
||||
- Enable PropertySpelling in scss-lint. !12752 (Takuya Noguchi)
|
||||
- Add API for protected branches to allow for wildcard matching and no access restrictions. !12756 (Eric Yu)
|
||||
- refactor initializations in dropzone_input.js. !12768 (Brandon Everett)
|
||||
- Improve CSS for global nav dropdown UI. !12772 (Takuya Noguchi)
|
||||
- Remove public/ci/favicon.ico. !12803 (Takuya Noguchi)
|
||||
- Enable DeclarationOrder in scss-lint. !12805 (Takuya Noguchi)
|
||||
- Increase width of dropdown menus automatically. !12809 (Thomas Wucher)
|
||||
- Enable BangFormat in scss-lint [ci skip]. !12815 (Takuya Noguchi)
|
||||
- Added /duplicate quick action to close a duplicate issue. !12845 (Ryan Scott)
|
||||
- Make all application-settings accessible through the API. !12851
|
||||
- Remove Inactive Personal Access Tokens list from Access Tokens page. !12866
|
||||
- Replaces dashboard/dashboard.feature spinach with rspec. !12876 (Alexander Randa (@randaalex))
|
||||
- Reduce memory usage of the GitHub importer. !12886
|
||||
- Bump fog-core to 1.44.3 and fog providers' plugins to latest. !12897 (Takuya Noguchi)
|
||||
- Use only CSS to truncate commit message in blame. !12900 (Takuya Noguchi)
|
||||
- Protect manual actions against protected tag too. !12908
|
||||
- Allow to configure automatic retry of a failed CI/CD job. !12909
|
||||
- Remove help message about prioritized labels for non-members. !12912 (Takuya Noguchi)
|
||||
- Add link to doc/api/ci/lint.md. !12914 (Takuya Noguchi)
|
||||
- Add RequestCache which makes caching with RequestStore easier. !12920
|
||||
- Free up some top level words, reject top level groups named like files in the public folder. !12932
|
||||
- Extend API for Group Secret Variable. !12936
|
||||
- Hide description about protected branches to non-member. !12945 (Takuya Noguchi)
|
||||
- Support custom directory in gitlab:backup:create task. !12984 (Markus Koller)
|
||||
- Raise guessed encoding confidence threshold to 50. !12990
|
||||
- Add author_id & assignee_id param to /issues API. !13004
|
||||
- Fix today day highlight in calendar. !13048
|
||||
- Prevent LDAP login callback from being called with a GET request. !13059
|
||||
- Add top-level merge_requests API endpoint. !13060
|
||||
- Handle maximum pages artifacts size correctly. !13072
|
||||
- Enable gitaly_post_upload_pack by default. !13078
|
||||
- Add Prometheus metrics exporter to Sidekiq. !13082
|
||||
- Fix improperly skipped backups of wikis. !13096
|
||||
- Projects can be created from templates. !13108
|
||||
- Fix the /projects/:id/repository/branches endpoint to handle dots in the branch name when the project full path contains a `/`. !13115
|
||||
- Fix project logos that are not centered vertically on list pages. !13124 (Florian Lemaitre)
|
||||
- Derive project path from import URL. !13131
|
||||
- Fix deletion of deploy keys linked to other projects. !13162
|
||||
- repository archive download url now ends with selected file extension. !13178 (haseebeqx)
|
||||
- Show auto-generated avatars for Groups without avatars. !13188
|
||||
- Allow any logged in users to read_users_list even if it's restricted. !13201
|
||||
- Unlock stuck merge request and set the proper state. !13207
|
||||
- Fix timezone inconsistencies in user contribution graph. !13208
|
||||
- Fix Issue board when using Ruby 2.4. !13220
|
||||
- Don't rename namespace called system when upgrading from 9.1.x to 9.5. !13228
|
||||
- Fix encoding error for WebHook logging. !13230 (Alexander Randa (@randaalex))
|
||||
- Uniquify reserved word usernames on OAuth user creation. !13244 (Robin Bobbitt)
|
||||
- Expose target_iid in Events API. !13247 (sue445)
|
||||
- Add star for action scope, in order to delete image from registry. !13248 (jean)
|
||||
- Make Delete Merged Branches handle wildcard protected branches correctly. !13251
|
||||
- Fix an order of operations for CI connection error message in merge request widget. !13252
|
||||
- Don't send rejection mails for all auto-generated mails. !13254
|
||||
- Expose noteable_iid in Note. !13265 (sue445)
|
||||
- Fix pipeline_schedules pages when active schedule has an abnormal state. !13286
|
||||
- Move some code from services to workers in order to improve performance. !13326
|
||||
- Fix destroy of case-insensitive conflicting redirects. !13357
|
||||
- Fix the /projects/:id/repository/tags endpoint to handle dots in the tag name when the project full path contains a `/`. !13368
|
||||
- Fix the /projects/:id/repository/commits endpoint to handle dots in the ref name when the project full path contains a `/`. !13370
|
||||
- Project pending delete no longer return 500 error in admins projects view. !13389
|
||||
- Use full path of user's avatar in webhooks. !13401 (Vitaliy @blackst0ne Klachkov)
|
||||
- Make GPGME temporary directory handling thread safe. !13481 (Alexis Reigel)
|
||||
- Add support for kube_namespace in Metrics queries. !16169
|
||||
- Fix bar chart does not display label at 0 hour. !35136 (Jason Dai)
|
||||
- Use project_ref_path to create the link to a branch to fix links that 404.
|
||||
- Declare related resources into V4 API entities.
|
||||
- Add Slack and JIRA services counts to Usage Data.
|
||||
- Prevent web hook and project service background jobs from going to the dead jobs queue.
|
||||
- Display specific error message when JIRA test fails.
|
||||
- clean up merge request widget UI.
|
||||
- Associate Issues tab only with internal issues tracker.
|
||||
- Remove events column from notification settings table.
|
||||
- Clarifies and rearranges the input variables on the kubernetes integration page and adjusts the docs slightly to meet the same order.
|
||||
- Respect blockquote line breaks in markdown.
|
||||
- Update confidential issue UI - add confidential visibility and settings to sidebar.
|
||||
- Add icons to contextual sidebars.
|
||||
- Make contextual sidebar collapsible.
|
||||
- Update Pipeline's badge count in Merge Request and Commits view to match real-time content.
|
||||
- Added link to the MR widget that directs to the monitoring dashboard.
|
||||
- Use jQuery to control scroll behavior in job log for cross browser consistency.
|
||||
- move edit comment button outside of dropdown.
|
||||
- Updates vue resource and code according to breaking changes.
|
||||
- Add GitHub imported projects count to usage data.
|
||||
- Rename about to overview for group and project page.
|
||||
- Prevent disabled pagination button to be clicked.
|
||||
- Remove coffee-rails gem. (Takuya Noguchi)
|
||||
- Remove net-ssh gem. (Takuya Noguchi)
|
||||
- Bump rubocop to 0.49.1 and rubocop-rspec to 1.15.1. (Takuya Noguchi)
|
||||
- improve file upload/replace experience.
|
||||
- allow closing Cycle Analytics intro box in firefox.
|
||||
- Fix label creation from new list for subgroup projects.
|
||||
- fix transient js error in rspec tests.
|
||||
- fix jump to next discussion button.
|
||||
- Fix translations for Star/Unstar in JS file.
|
||||
- Improve mobile sidebar.
|
||||
- Rename Pipelines tab to CI / CD in new navigation.
|
||||
- Fix display of new diff comments after changing b between diff views.
|
||||
- Store & use ConvDev percentages returned by the Version app.
|
||||
- Fixes new issue button for failed job returning 404.
|
||||
- Align OR separator to center in new project page.
|
||||
- Add filtered search to group issue dashboard.
|
||||
- Cache Appearance instances in Redis.
|
||||
- Fixed breadcrumbs title aggressively collapsing.
|
||||
- Better caching and indexing of broadcast messages.
|
||||
- Moved diff changed files into a dropdown.
|
||||
- Improve performance of large (initial) push into default branch.
|
||||
- Improve performance of checking for projects on the projects dashboard.
|
||||
- Eager load project creators for project dashboards.
|
||||
- Modify if condition to be more readable.
|
||||
- Fix links to group milestones from issue and merge request sidebar.
|
||||
- Remove hidden symlinks from project import files.
|
||||
- Fixed sign-in restrictions buttons not toggling active state.
|
||||
- Fix replying to commit comments on merge requests created from forks.
|
||||
- Support Markdown references, autocomplete, and quick actions for group milestones.
|
||||
- Cache recent projects for group-level new resource creation.
|
||||
- Fix API responses when dealing with txt files.
|
||||
- Fix project milestones import when projects belongs to a group.
|
||||
- Fix Mattermost integration.
|
||||
- Memoize the number of personal projects a user has to reduce COUNT queries.
|
||||
- Merge issuable "reopened" state into "opened".
|
||||
- Migrate events into a new format to reduce the storage necessary and improve performance.
|
||||
- MR branch link now links to tree instead of commits.
|
||||
- Use Prev/Next pagination for exploring projects.
|
||||
- Pass before_script and script as-is preserving arrays.
|
||||
- Change project FK migration to skip existing FKs.
|
||||
- Remove redundant query when retrieving the most recent push of a user.
|
||||
- Re-organise "issues" indexes for faster ordering.
|
||||
- Disallow Git URLs that include a username or hostname beginning with a non-alphanumeric character.
|
||||
- Fix search box losing focus when typing.
|
||||
- Add structured logging for Rails processes.
|
||||
- Skip oAuth authorization for trusted applications.
|
||||
- Use a specialized class for querying events to improve performance.
|
||||
- Update build badges to be pipeline badges and display passing instead of success.
|
||||
|
||||
## 9.4.3 (2017-07-31)
|
||||
|
||||
- Fix Prometheus client PID reuse bug. !13130
|
||||
- Improve deploy environment chatops slash command. !13150
|
||||
- Fix asynchronous javascript paths when GitLab is installed under a relative URL. !13165
|
||||
- Fix LDAP authentication to Git repository or container registry.
|
||||
- Fixed new navigation breadcrumb title on help pages.
|
||||
- Ensure filesystem metrics test files are deleted.
|
||||
- Properly affixes nav bar in job view in microsoft edge.
|
||||
|
||||
## 9.4.2 (2017-07-28)
|
||||
|
||||
- Fix job merge request link to a forked source project. !12965
|
||||
- Improve redirect route query performance. !13062
|
||||
- Allow admin to read_users_list even if it's restricted. !13066
|
||||
- Fixes 500 error caused by pending delete projects in admin dashboard. !13067
|
||||
- Add instrumentation to MarkupHelper#link_to_gfm. !13069
|
||||
- Pending delete projects should not show in deploy keys. !13088
|
||||
- Fix sizing of custom header logo in new navigation.
|
||||
- Fix crash on /help/ui.
|
||||
- Fix creating merge request diffs when diff contains bytes that are invalid in UTF-8.
|
||||
- fix vertical alignment of New Project button.
|
||||
- Add LDAP SSL certificate verification option.
|
||||
- Fix vertical alignment in firefox and safari for pipeline mini graph.
|
||||
|
||||
## 9.4.1 (2017-07-25)
|
||||
|
||||
- Fix pipeline_schedules pages throwing error 500 (when ref is empty). !12983
|
||||
- Fix editing project with container images present. !13028
|
||||
- Fix some invalid entries in PO files. !13032
|
||||
- Fix cross site request protection when logging in as a regular user when LDAP is enabled. !13049
|
||||
- Fix bug causing metrics files to be truncated. !35420
|
||||
- Fix anonymous access to public projects in groups with pending invites.
|
||||
- Fixed issue boards sidebar close icon size.
|
||||
- Fixed duplicate new milestone buttons when new navigation is turned on.
|
||||
- Fix margins in the mini graph for pipeline in commits box.
|
||||
|
||||
## 9.4.0 (2017-07-22)
|
||||
|
||||
- Add blame view age mapping. !7198 (Jeff Stubler)
|
||||
- Add support for image and services configuration in .gitlab-ci.yml. !8578
|
||||
- Fix an email parsing bug where brackets would be inserted in emails from some Outlook clients. !9045 (jneen)
|
||||
- Use fa-chevron-down on dropdown arrows for consistency. !9659 (TM Lee)
|
||||
- Update the devise mail templates to match the design of the pipeline emails. !10483 (Alexis Reigel)
|
||||
- Handle renamed submodules in repository browser. !10798 (David Turner)
|
||||
- Display all current broadcast messages, not just the last one. !11113 (rickettm)
|
||||
- Fix CI/CD status in case there are only allowed to failed jobs in the pipeline. !11166
|
||||
- Omit trailing / leading hyphens in CI_COMMIT_REF_SLUG variable to make it usable as a hostname. !11218 (Stefan Hanreich)
|
||||
- Moved "Members in a project" menu entry and path locations. !11560
|
||||
- Additional Prometheus metrics support. !11712
|
||||
- Rename all reserved paths that could have been created. !11713
|
||||
- Move uploads from `uploads/system` to `uploads/-/system` to free up `system` as a group name. !11713
|
||||
- Fix offline runner detection. !11751 (Alessio Caiazza)
|
||||
- Use authorize_update_pipeline_schedule in PipelineSchedulesController. !11846
|
||||
- Rollback project repo move if there is an error in Projects::TransferService. !11877
|
||||
- Help landing page customizations. !11878 (Robin Bobbitt)
|
||||
- Fixes "sign in / Register" active state underline misalignment. !11890 (Frank Sierra)
|
||||
- Honor the "Remember me" parameter for OAuth-based login. !11963
|
||||
- Instruct user to use personal access token for Git over HTTP. !11986 (Robin Bobbitt)
|
||||
- Accept image for avatar in project API. !11988 (Ivan Chernov)
|
||||
- Supplement Simplified Chinese translation of Project Page & Repository Page. !11994 (Huang Tao)
|
||||
- Supplement Traditional Chinese in Hong Kong translation of Project Page & Repository Page. !11995 (Huang Tao)
|
||||
- Make the revision on the `/help` page clickable. !12016
|
||||
- Display issue state in issue links section of merge request widget. !12021
|
||||
- Enable support for webpack code-splitting by dynamically setting publicPath at runtime. !12032
|
||||
- Replace PhantomJS with headless Chrome for karma test suite. !12036
|
||||
- Prevent description change notes when toggling tasks. !12057 (Jared Deckard <jared.deckard@gmail.com>)
|
||||
- Update QA Dockerfile to lock Chrome browser version. !12071
|
||||
- Fix FIDO U2F for Opera browser. !12082 (Jakub Kramarz and Jonas Kalderstam)
|
||||
- Supplement Bulgarian translation of Project Page & Repository Page. !12083 (Lyubomir Vasilev)
|
||||
- Removes deleted_at and pending_delete occurrences in Project related queries. !12091
|
||||
- Provide hint to create a personal access token for Git over HTTP. !12105 (Robin Bobbitt)
|
||||
- Display own user id in account settings page. !12141 (Riccardo Padovani)
|
||||
- Accept image for avatar in user API. !12143 (Ivan Chernov)
|
||||
- Disable fork button on project limit. !12145 (Ivan Chernov)
|
||||
- Added "created_after" and "created_before" params to issuables. !12151 (Kyle Bishop @kybishop)
|
||||
- Supplement Portuguese Brazil translation of Project Page & Repository Page. !12156 (Huang Tao)
|
||||
- Add review apps to usage metrics. !12185
|
||||
- Adding French translations. !12200 (Erwan "Dremor" Georget)
|
||||
- Ensures default user limits when external user is unchecked. !12218
|
||||
- Provide KUBECONFIG from KubernetesService for runners. !12223
|
||||
- Filter archived project in API v3 only if param present. !12245 (Ivan Chernov)
|
||||
- Add explicit message when no runners on admin. !12266 (Takuya Noguchi)
|
||||
- Split pipelines as internal and external in the usage data. !12277
|
||||
- Fix API Scoping. !12300
|
||||
- Remove registry image delete button if user cant delete it. !12317 (Ivan Chernov)
|
||||
- Allow the feature flags to be enabled/disabled with more granularity. !12357
|
||||
- Allow to enable the performance bar per user or Feature group. !12362
|
||||
- Rename duplicated variables with the same key for projects. Add environment_scope column to variables and add unique constraint to make sure that no variables could be created with the same key within a project. !12363
|
||||
- Add variables to pipelines schedules. !12372
|
||||
- Add User#full_private_access? to check if user has access to all private groups & projects. !12373
|
||||
- Change milestone endpoint for groups. !12374 (Takuya Noguchi)
|
||||
- Improve performance of the pipeline charts page. !12378
|
||||
- Add option to run Gitaly on a remote server. !12381
|
||||
- #20628 Enable implicit grant in GitLab as OAuth Provider. !12384 (Mateusz Pytel)
|
||||
- Replace 'snippets/snippets.feature' spinach with rspec. !12385 (Alexander Randa @randaalex)
|
||||
- Add Simplified Chinese translations of Commits Page. !12405 (Huang Tao)
|
||||
- Add Traditional Chinese in HongKong translations of Commits Page. !12406 (Huang Tao)
|
||||
- Add Traditional Chinese in Taiwan translations of Commits Page. !12407 (Huang Tao)
|
||||
- Add Portuguese Brazil translations of Commits Page. !12408 (Huang Tao)
|
||||
- Add French translations of Commits Page. !12409 (Huang Tao)
|
||||
- Add Esperanto translations of Commits Page. !12410 (Huang Tao)
|
||||
- Add Bulgarian translations of Commits Page. !12411 (Huang Tao)
|
||||
- Remove bin/ci/upgrade.rb as not working all. !12414 (Takuya Noguchi)
|
||||
- Store merge request ref_fetched status in the database. !12424
|
||||
- Replace 'dashboard/merge_requests' spinach with rspec. !12440 (Alexander Randa (@randaalex))
|
||||
- Add Esperanto translations for Cycle Analytics, Project, and Repository pages. !12442 (Huang Tao)
|
||||
- Allow unauthenticated access to the /api/v4/users API. !12445
|
||||
- Drop GFM support for the title of Milestone/MergeRequest in template. !12451 (Takuya Noguchi)
|
||||
- Replace 'dashboard/todos' spinach with rspec. !12453 (Alexander Randa (@randaalex))
|
||||
- Cache open issue and merge request counts for project tabs to speed up project pages. !12457
|
||||
- Introduce cache policies for CI jobs. !12483
|
||||
- Improve support for external issue references. !12485
|
||||
- Fix errors caused by attempts to report already blocked or deleted users. !12502 (Horacio Bertorello)
|
||||
- Allow customize CI config path. !12509 (Keith Pope)
|
||||
- Supplement Traditional Chinese in Taiwan translation of Project Page & Repository Page. !12514 (Huang Tao)
|
||||
- Closes any open Autocomplete of the markdown editor when the form is closed. !12521
|
||||
- Inserts exact matches of name, username and email to the top of the search list. !12525
|
||||
- Use smaller min-width for dropdown-menu-nav only on mobile. !12528 (Takuya Noguchi)
|
||||
- Hide archived project labels from group issue tracker. !12547 (Horacio Bertorello)
|
||||
- Replace 'dashboard/new-project.feature' spinach with rspec. !12550 (Alexander Randa (@randaalex))
|
||||
- Remove group modal like remove project modal (requires typing + confirmation). !12569 (Diego Souza)
|
||||
- Add Italian translation of Cycle Analytics Page & Project Page & Repository Page. !12578 (Huang Tao)
|
||||
- Add Group secret variables. !12582
|
||||
- Update jobs page output to have a scrollable page. !12587
|
||||
- Add user projects API. !12596 (Ivan Chernov)
|
||||
- Allow creation of files and directories with spaces through Web UI. !12608
|
||||
- Improve members view on mobile. !12619
|
||||
- Fixed the chart legend not being set correctly. !12628
|
||||
- Add Italian translations of Commits Page. !12645 (Huang Tao)
|
||||
- Allow admins to disable all restricted visibility levels. !12649
|
||||
- Allow admins to retrieve user agent details for an issue or snippet. !12655
|
||||
- Update welcome page UX for new users. !12662
|
||||
- N+1 problems on milestone page. !12670 (Takuya Noguchi)
|
||||
- Upgrade GitLab Workhorse to v2.3.0. !12676
|
||||
- Remove option to disable Gitaly. !12677
|
||||
- Improve the performance of the project list API. !12679
|
||||
- Add creation time filters to user search API for admins. !12682
|
||||
- Add Japanese translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12693 (Huang Tao)
|
||||
- Undo adding the /reassign quick action. !12701
|
||||
- Fix dashboard labels dropdown. !12708
|
||||
- Username and password are no longer stripped from import url on mirror update. !12725
|
||||
- Add Russian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12743 (Huang Tao)
|
||||
- Add Ukrainian translations for Cycle Analytics & Project pages & Repository pages & Commits pages & Pipeline Charts. !12744 (Huang Tao)
|
||||
- Prevent bad data being added to application settings when Redis is unavailable. !12750
|
||||
- Do not show pipeline schedule button for non-member. !12757 (Takuya Noguchi)
|
||||
- Return `is_admin` attribute in the GET /user endpoint for admins. !12811
|
||||
- Recover from renaming project that has container images. !12840
|
||||
- Exact matches of username and email are now on top of the user search. !12868
|
||||
- Use Ghost user for last_edited_by and merge_user when original user is deleted. !12933
|
||||
- Fix docker tag reference routing constraints. !12961
|
||||
- Optimize creation of commit API by using Repository#commit instead of Repository#commits.
|
||||
- Speed up used languages calculation on charts page.
|
||||
- Make loading new merge requests (those created after the 9.4 upgrade) faster.
|
||||
- Ensure participants for issues, merge requests, etc. are calculated correctly when sending notifications.
|
||||
- Handle nameless legacy jobs.
|
||||
- Bump Faraday and dependent OAuth2 gem version to support no_proxy variable.
|
||||
- Renders 404 if given project is not readable by the user on Todos dashboard.
|
||||
- Render CI statuses with warnings in orange.
|
||||
- Document the Delete Merged Branches functionality.
|
||||
- Add wells to admin dashboard overview to fix spacing problems.
|
||||
- Removes hover style for nodes that are either links or buttons in the pipeline graph.
|
||||
- more visual contrast in pagination widget.
|
||||
- Deprecate Healthcheck Access Token in favor of IP whitelist.
|
||||
- Drop GFM support for issuable title on milestone for consistency and performance. (Takuya Noguchi)
|
||||
- fix left & right padding on sidebar.
|
||||
- Cleanup minor UX issues in the performance dashboard.
|
||||
- Remove two columned layout from project member settings.
|
||||
- Make font size of contextual sub menu items 14px.
|
||||
- Fix vertical space in job details sidebar.
|
||||
- Fix alignment of controls in mr issuable list.
|
||||
- Add wip message to new navigation preference section.
|
||||
- Add group members counting and plan related data on namespaces API.
|
||||
- Fix spacing on runner buttons.
|
||||
- Remove uploads/appearance symlink. A leftover from a previous migration.
|
||||
- Change order of monospace fonts to fix bug on some linux distros.
|
||||
- Limit commit & snippets comments width.
|
||||
- Fixed dashboard milestone tabs not loading.
|
||||
- Detect if file that appears to be text in the first 1024 bytes is actually binary afer loading all data.
|
||||
- Fix inconsistent display of the "Browse files" button in the commit list.
|
||||
- Implement diff viewers.
|
||||
- Fix 'New merge request' button for users who don't have push access to canonical project.
|
||||
- Fix issues with non-UTF8 filenames by always fixing the encoding of tree and blob paths.
|
||||
- Show group name instead of path on group page.
|
||||
- Don't check if MailRoom is running on Omnibus.
|
||||
- Limit OpenGraph image size to 64x64.
|
||||
- Don't show auxiliary blob viewer for README when there is no wiki.
|
||||
- Strip trailing whitespace in relative submodule URL.
|
||||
- Update /target_branch slash command description to be more consistent.
|
||||
- Remove unnecessary top padding on group MR index.
|
||||
- Added printing_merge_requst_link_enabled to the API. (David Turner <dturner@twosigma.com>)
|
||||
- Re-enable realtime for environments table.
|
||||
- Create responsive mobile view for pipelines table.
|
||||
- Adds realtime feature to job show view header and sidebar info. Updates UX.
|
||||
- Use color inputs for broadcast messages.
|
||||
- Center dropdown for mini graph.
|
||||
- Users can subscribe to group labels on the group labels page.
|
||||
- Add issuable-list class to shared mr/issue lists to fix new responsive layout design.
|
||||
- Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor of "slash commands".
|
||||
- Don't mark empty MRs as merged on push to the target branch.
|
||||
- Improve issue rendering performance with lots of notes from other users.
|
||||
- Fixed overflow on mobile screens for the slash commands.
|
||||
- Fix an infinite loop when handling user-supplied regular expressions.
|
||||
- Fixed sidebar not collapsing on merge requests in mobile screens.
|
||||
- Speed up project removals by adding foreign keys with cascading deletes to various tables.
|
||||
- Fix mobile view of files view buttons.
|
||||
- Fixed dropdown filter input not focusing after transition.
|
||||
- Fixed GFM references not being included when updating issues inline.
|
||||
- Remove issues/merge requests drag n drop and sorting from milestone view.
|
||||
- Add native group milestones.
|
||||
- Fix API bug accepting wrong parameter to create merge request.
|
||||
- Clean up UI of issuable lists and make more responsive.
|
||||
- Improve the overall UX for the new monitoring dashboard.
|
||||
- Fixed the y_label not setting correctly for each graph on the monitoring dashboard.
|
||||
- Changed utilities imports from ~ to relative paths.
|
||||
- Remove unused space in sidebar todo toggle when not signed in.
|
||||
- Limit the width of the projects README text.
|
||||
- Add a simple mode to merge request API.
|
||||
- Make Project#ensure_repository force create a repo.
|
||||
- Use uploads/system directory for personal snippets.
|
||||
- Defer project destroys within a namespace in Groups::DestroyService#async_execute.
|
||||
- Log rescued exceptions to Sentry.
|
||||
- Remove remaining N+1 queries in merge requests API with emojis and labels.
|
||||
|
||||
## 9.3.9 (2017-07-20)
|
||||
|
||||
- Fix an infinite loop when handling user-supplied regular expressions.
|
||||
|
||||
## 9.3.8 (2017-07-19)
|
||||
|
||||
- Improve support for external issue references. !12485
|
||||
- Renders 404 if given project is not readable by the user on Todos dashboard.
|
||||
- Use uploads/system directory for personal snippets.
|
||||
- Remove uploads/appearance symlink. A leftover from a previous migration.
|
||||
|
||||
## 9.3.7 (2017-07-18)
|
||||
|
||||
- Prevent bad data being added to application settings when Redis is unavailable. !12750
|
||||
- Return `is_admin` attribute in the GET /user endpoint for admins. !12811
|
||||
|
||||
## 9.3.6 (2017-07-12)
|
||||
|
||||
- Fix API Scoping. !12300
|
||||
- Username and password are no longer stripped from import url on mirror update. !12725
|
||||
- Fix issues with non-UTF8 filenames by always fixing the encoding of tree and blob paths.
|
||||
- Fixed GFM references not being included when updating issues inline.
|
||||
|
||||
## 9.3.5 (2017-07-05)
|
||||
|
||||
- Remove "Remove from board" button from backlog and closed list. !12430
|
||||
- Do not delete protected branches when deleting all merged branches. !12624
|
||||
- Set default for Remove source branch to false.
|
||||
- Prevent accidental deletion of protected MR source branch by repeating checks before actual deletion.
|
||||
- Expires full_path cache after a repository is renamed/transferred.
|
||||
|
||||
## 9.3.4 (2017-07-03)
|
||||
|
||||
- Update gitlab-shell to 5.1.1 !12615
|
||||
|
||||
## 9.3.3 (2017-06-30)
|
||||
|
||||
- Fix head pipeline stored in merge request for external pipelines. !12478
|
||||
- Bring back branches badge to main project page. !12548
|
||||
- Fix diff of requirements.txt file by not matching newlines as part of package names.
|
||||
- Perform housekeeping only when an import of a fresh project is completed.
|
||||
- Fixed issue boards closed list not showing all closed issues.
|
||||
- Fixed multi-line markdown tooltip buttons in issue edit form.
|
||||
|
||||
## 9.3.2 (2017-06-27)
|
||||
|
||||
- API: Fix optional arugments for POST :id/variables. !12474
|
||||
- Bump premailer-rails gem to 1.9.7 and its dependencies to prevent network retrieval of assets.
|
||||
|
||||
## 9.3.1 (2017-06-26)
|
||||
|
||||
- Fix reversed breadcrumb order for nested groups. !12322
|
||||
- Fix 500 when failing to create private group. !12394
|
||||
- Fix linking to line number on side-by-side diff creating empty discussion box.
|
||||
- Don't match tilde and exclamation mark as part of requirements.txt package name.
|
||||
- Perform project housekeeping after importing projects.
|
||||
- Fixed ctrl+enter not submit issue edit form.
|
||||
|
||||
## 9.3.0 (2017-06-22)
|
||||
|
||||
- Refactored gitlab:app:check into SystemCheck liberary and improve some checks. !9173
|
||||
- Add an ability to cancel attaching file and redesign attaching files UI. !9431 (blackst0ne)
|
||||
- Add Aliyun OSS as the backup storage provider. !9721 (Yuanfei Zhu)
|
||||
- Add suport for find_local_branches GRPC from Gitaly. !10059
|
||||
- Allow manual bypass of auto_sign_in_with_provider with a new param. !10187 (Maxime Besson)
|
||||
- Redirect to user's keys index instead of user's index after a key is deleted in the admin. !10227 (Cyril Jouve)
|
||||
- Changed Blame to Annotate in the UI to promote blameless culture. !10378 (Ilya Vassilevsky)
|
||||
- Implement ability to update deploy keys. !10383 (Alexander Randa)
|
||||
- Allow numeric values in gitlab-ci.yml. !10607 (blackst0ne)
|
||||
- Add a feature test for Unicode trace. !10736 (dosuken123)
|
||||
- Notes: Warning message should go away once resolved. !10823 (Jacopo Beschi @jacopo-beschi)
|
||||
- Project authorizations are calculated much faster when using PostgreSQL, and nested groups support for MySQL has been removed
|
||||
. !10885
|
||||
- Fix long urls in the title of commit. !10938 (Alexander Randa)
|
||||
- Update gem sidekiq-cron from 0.4.4 to 0.6.0 and rufus-scheduler from 3.1.10 to 3.4.0. !10976 (dosuken123)
|
||||
- Use relative paths for group/project/user avatars. !11001 (blackst0ne)
|
||||
- Enable cancelling non-HEAD pending pipelines by default for all projects. !11023
|
||||
- Implement web hook logging. !11027 (Alexander Randa)
|
||||
- Add indices for auto_canceled_by_id for ci_pipelines and ci_builds on PostgreSQL. !11034
|
||||
- Add post-deploy migration to clean up projects in `pending_delete` state. !11044
|
||||
- Limit User's trackable attributes, like `current_sign_in_at`, to update at most once/hour. !11053
|
||||
- Disallow multiple selections for Milestone dropdown. !11084
|
||||
- Link to commit author user page from pipelines. !11100
|
||||
- Fix the last coverage in trace log should be extracted. !11128 (dosuken123)
|
||||
- Remove redirect for old issue url containing id instead of iid. !11135 (blackst0ne)
|
||||
- Backported new SystemHook event: `repository_update`. !11140
|
||||
- Keep input data after creating a tag that already exists. !11155
|
||||
- Fix support for external CI services. !11176
|
||||
- Translate backend for Project & Repository pages. !11183
|
||||
- Fix LaTeX formatting for AsciiDoc wiki. !11212
|
||||
- Add foreign key for pipeline schedule owner. !11233
|
||||
- Print Go version in rake gitlab:env:info. !11241
|
||||
- Include the blob content when printing a blob page. !11247
|
||||
- Sync email address from specified omniauth provider. !11268 (Robin Bobbitt)
|
||||
- Disable reference prefixes in notes for Snippets. !11278
|
||||
- Rename build_events to job_events. !11287
|
||||
- Add API support for pipeline schedule. !11307 (dosuken123)
|
||||
- Use route.cache_key for project list cache key. !11325
|
||||
- Make environment table realtime. !11333
|
||||
- Cache npm modules between pipelines with yarn to speed up setup-test-env. !11343
|
||||
- Allow GitLab instance to start when InfluxDB hostname cannot be resolved. !11356
|
||||
- Add ConvDev Index page to admin area. !11377
|
||||
- Fix Git-over-HTTP error statuses and improve error messages. !11398
|
||||
- Renamed users 'Audit Log'' to 'Authentication Log'. !11400
|
||||
- Style people in issuable search bar. !11402
|
||||
- Change /builds in the URL to /-/jobs. Backward URLs were also added. !11407
|
||||
- Update password field label while editing service settings. !11431
|
||||
- Add an optional performance bar to view performance metrics for the current page. !11439
|
||||
- Update task_list to version 2.0.0. !11525 (Jared Deckard <jared.deckard@gmail.com>)
|
||||
- Avoid resource intensive login checks if password is not provided. !11537 (Horatiu Eugen Vlad)
|
||||
- Allow numeric pages domain. !11550
|
||||
- Exclude manual actions when checking if pipeline can be canceled. !11562
|
||||
- Add server uptime to System Info page in admin dashboard. !11590 (Justin Boltz)
|
||||
- Simplify testing and saving service integrations. !11599
|
||||
- Fixed handling of the `can_push` attribute in the v3 deploy_keys api. !11607 (Richard Clamp)
|
||||
- Improve user experience around slash commands in instant comments. !11612
|
||||
- Show current user immediately in issuable filters. !11630
|
||||
- Add extra context-sensitive functionality for the top right menu button. !11632
|
||||
- Reorder Issue action buttons in order of usability. !11642
|
||||
- Expose atom links with an RSS token instead of using the private token. !11647 (Alexis Reigel)
|
||||
- Respect merge, instead of push, permissions for protected actions. !11648
|
||||
- Job details page update real time. !11651
|
||||
- Improve performance of ProjectFinder used in /projects API endpoint. !11666
|
||||
- Remove redundant data-turbolink attributes from links. !11672 (blackst0ne)
|
||||
- Minimum postgresql version is now 9.2. !11677
|
||||
- Add protected variables which would only be passed to protected branches or protected tags. !11688
|
||||
- Introduce optimistic locking support via optional parameter last_commit_sha on File Update API. !11694 (electroma)
|
||||
- Add $CI_ENVIRONMENT_URL to predefined variables for pipelines. !11695
|
||||
- Simplify project repository settings page. !11698
|
||||
- Fix pipeline_schedules pages throwing error 500. !11706 (dosuken123)
|
||||
- Add performance deltas between app deployments on Merge Request widget. !11730
|
||||
- Add feature toggles and API endpoints for admins. !11747
|
||||
- Replace 'starred_projects.feature' spinach test with an rspec analog. !11752 (blackst0ne)
|
||||
- Introduce an Events API. !11755
|
||||
- Display Shared Runner status in Admin Dashboard. !11783 (Ivan Chernov)
|
||||
- Persist pipeline stages in the database. !11790
|
||||
- Revert the feature that would include the current user's username in the HTTP clone URL. !11792
|
||||
- Enable Gitaly by default in installations from source. !11796
|
||||
- Use zopfli compression for frontend assets. !11798
|
||||
- Add tag_list param to project api. !11799 (Ivan Chernov)
|
||||
- Add changelog for improved Registry description. !11816
|
||||
- Automatically adjust project settings to match changes in project visibility. !11831
|
||||
- Add slugify project path to CI enviroment variables. !11838 (Ivan Chernov)
|
||||
- Add all pipeline sources as special keywords to 'only' and 'except'. !11844 (Filip Krakowski)
|
||||
- Allow pulling of container images using personal access tokens. !11845
|
||||
- Expose import_status in Projects API. !11851 (Robin Bobbitt)
|
||||
- Allow admins to delete users from the admin users page. !11852
|
||||
- Allow users to be hard-deleted from the API. !11853
|
||||
- Fix hard-deleting users when they have authored issues. !11855
|
||||
- Fix missing optional path parameter in "Create project for user" API. !11868
|
||||
- Allow users to be hard-deleted from the admin panel. !11874
|
||||
- Add a Rake task to aid in rotating otp_key_base. !11881
|
||||
- Fix submodule link to then project under subgroup. !11906
|
||||
- Fix binary encoding error on MR diffs. !11929
|
||||
- Limit non-administrators to adding 100 members at a time to groups and projects. !11940
|
||||
- add bulgarian translation of cycle analytics page to I18N. !11958 (Lyubomir Vasilev)
|
||||
- Make backup task to continue on corrupt repositories. !11962
|
||||
- Fix incorrect ETag cache key when relative instance URL is used. !11964
|
||||
- Reinstate is_admin flag in users api when authenticated user is an admin. !12211 (rickettm)
|
||||
- Fix edit button for deploy keys available from other projects. !12301 (Alexander Randa)
|
||||
- Fix passing CI_ENVIRONMENT_NAME and CI_ENVIRONMENT_SLUG for CI_ENVIRONMENT_URL. !12344
|
||||
- Disable environment list refresh due to bug https://gitlab.com/gitlab-org/gitlab-ee/issues/2677. !12347
|
||||
- Standardize timeline note margins across different viewport sizes. !12364
|
||||
- Fix Ordered Task List Items. !31483 (Jared Deckard <jared.deckard@gmail.com>)
|
||||
- Upgrade dependency to Go 1.8.3. !31943
|
||||
- Add prometheus metrics on pipeline creation.
|
||||
- Fix etag route not being a match for environments.
|
||||
- Sort folder for environments.
|
||||
- Support descriptions for snippets.
|
||||
- Hide clone panel and file list when user is only a guest. (James Clark)
|
||||
- Don’t create comment on JIRA if it already exists for the entity.
|
||||
- Update Dashboard Groups UI with better support for subgroups.
|
||||
- Confirm Project forking behaviour via the API.
|
||||
- Add prometheus based metrics collection to gitlab webapp.
|
||||
- Fix: Wiki is not searchable with Guest permissions.
|
||||
- Center all empty states.
|
||||
- Remove 'New issue' button when issues search returns no results.
|
||||
- Add API URL to JIRA settings.
|
||||
- animate adding issue to boards.
|
||||
- Update session cookie key name to be unique to instance in development.
|
||||
- Single click on filter to open filtered search dropdown.
|
||||
- Makes header information of pipeline show page realtine.
|
||||
- Creates a mediator for pipeline details vue in order to mount several vue apps with the same data.
|
||||
- Scope issue/merge request recent searches to project.
|
||||
- Increase individual diff collapse limit to 100 KB, and render limit to 200 KB.
|
||||
- Fix Pipelines table empty state - only render empty state if we receive 0 pipelines.
|
||||
- Make New environment empty state btn lowercase.
|
||||
- Removes duplicate environment variable in documentation.
|
||||
- Change links in issuable meta to black.
|
||||
- Fix border-bottom for project activity tab.
|
||||
- Adds new icon for CI skipped status.
|
||||
- Create equal padding for emoji.
|
||||
- Use briefcase icon for company in profile page.
|
||||
- Remove overflow from comment form for confidential issues and vertically aligns confidential issue icon.
|
||||
- Keep trailing newline when resolving conflicts by picking sides.
|
||||
- Fix /unsubscribe slash command creating extra todos when you were already mentioned in an issue.
|
||||
- Fix math rendering on blob pages.
|
||||
- Allow group reporters to manage group labels.
|
||||
- Use pre-wrap for commit messages to keep lists indented.
|
||||
- Count badges depend on translucent color to better adjust to different background colors and permission badges now feature a pill shaped design similar to labels.
|
||||
- Allow reporters to promote project labels to group labels.
|
||||
- Enabled keyboard shortcuts on artifacts pages.
|
||||
- Perform filtered search when state tab is changed.
|
||||
- Remove duplication for sharing projects with groups in project settings.
|
||||
- Change order of commits ahead and behind on divergence graph for branch list view.
|
||||
- Creates CI Header component for Pipelines and Jobs details pages.
|
||||
- Invalidate cache for issue and MR counters more granularly.
|
||||
- disable blocked manual actions.
|
||||
- Load tree readme asynchronously.
|
||||
- Display extra info about files on .gitlab-ci.yml, .gitlab/route-map.yml and LICENSE blob pages.
|
||||
- Fix replying to a commit discussion displayed in the context of an MR.
|
||||
- Consistently use monospace font for commit SHAs and branch and tag names.
|
||||
- Consistently display last push event widget.
|
||||
- Don't copy empty elements that were not selected on purpose as GFM.
|
||||
- Copy as GFM even when parts of other elements are selected.
|
||||
- Autolink package names in Gemfile.
|
||||
- Resolve N+1 query issue with discussions.
|
||||
- Don't match email addresses or foo@bar as user references.
|
||||
- Fix title of discussion jump button at top of page.
|
||||
- Don't return nil for missing objects from parser cache.
|
||||
- Make .gitmodules parsing more resilient to syntax errors.
|
||||
- Add username parameter to gravatar URL.
|
||||
- Autolink package names in more dependency files.
|
||||
- Return nil when looking up config for unknown LDAP provider.
|
||||
- Add system note with link to diff comparison when MR discussion becomes outdated.
|
||||
- Don't wrap pasted code when it's already inside code tags.
|
||||
- Revert 'New file from interface on existing branch'.
|
||||
- Show last commit for current tree on tree page.
|
||||
- Add documentation about adding foreign keys.
|
||||
- add username field to push webhook. (David Turner)
|
||||
- Rename CI/CD Pipelines to Pipelines in the project settings.
|
||||
- Make environment tables responsive.
|
||||
- Expand/collapse backlog & closed lists in issue boards.
|
||||
- Fix GitHub importer performance on branch existence check.
|
||||
- Fix counter cache for acts as taggable.
|
||||
- Github - Fix token interpolation when cloning wiki repository.
|
||||
- Fix token interpolation when setting the Github remote.
|
||||
- Fix N+1 queries for non-members in comment threads.
|
||||
- Fix terminals support for Kubernetes Service.
|
||||
- Fix: A diff comment on a change at last line of a file shows as two comments in discussion.
|
||||
- Instrument MergeRequestDiff#load_commits.
|
||||
- Introduce source to Pipeline entity.
|
||||
- Fixed create new label form in issue form not working for sub-group projects.
|
||||
- Fixed style on unsubscribe page. (Gustav Ernberg)
|
||||
- Enables inline editing for an issues title & description.
|
||||
- Ask for an example project for bug reports.
|
||||
- Add summary lines for collapsed details in the bug report template.
|
||||
- Prevent commits from upstream repositories to be re-processed by forks.
|
||||
- Avoid repeated queries for pipeline builds on merge requests.
|
||||
- Preloads head pipeline for merge request collection.
|
||||
- Handle head pipeline when creating merge requests.
|
||||
- Migrate artifacts to a new path.
|
||||
- Rescue OpenSSL::SSL::SSLError in JiraService & IssueTrackerService.
|
||||
- Repository browser: handle in-repository submodule urls. (David Turner)
|
||||
- Prevent project transfers if a new group is not selected.
|
||||
- Allow 'no one' as an option for allowed to merge on a procted branch.
|
||||
- Reduce time spent waiting for certain Sidekiq jobs to complete.
|
||||
- Refactor ProjectsFinder#init_collection to produce more efficient queries for retrieving projects.
|
||||
- Remove unused code and uses underscore.
|
||||
- Restricts search projects dropdown to group projects when group is selected.
|
||||
- Properly handle container registry redirects to fix metadata stored on a S3 backend.
|
||||
- Fix LFS timeouts when trying to save large files.
|
||||
- Set artifact working directory to be in the destination store to prevent unnecessary I/O.
|
||||
- Strip trailing whitespaces in submodule URLs.
|
||||
- Make sure reCAPTCHA configuration is loaded when spam checks are initiated.
|
||||
- Fix up arrow not editing last discussion comment.
|
||||
- Added application readiness endpoints to the monitoring health check admin view.
|
||||
- Use wait_for_requests for both ajax and Vue requests.
|
||||
- Cleanup ci_variables schema and table.
|
||||
- Remove foreigh key on ci_trigger_schedules only if it exists.
|
||||
- Allow translation of Pipeline Schedules.
|
||||
|
||||
## 9.2.9 (2017-07-20)
|
||||
|
||||
|
@ -262,6 +964,42 @@ entry.
|
|||
- Fix preemptive scroll bar on user activity calendar.
|
||||
- Pipeline chat notifications convert seconds to minutes and hours.
|
||||
|
||||
## 9.1.9 (2017-07-20)
|
||||
|
||||
- Fix an infinite loop when handling user-supplied regular expressions.
|
||||
|
||||
## 9.1.8 (2017-07-19)
|
||||
|
||||
- Improve support for external issue references. !12485
|
||||
- Renders 404 if given project is not readable by the user on Todos dashboard.
|
||||
- Fix incorrect project authorizations.
|
||||
- Remove uploads/appearance symlink. A leftover from a previous migration.
|
||||
|
||||
## 9.1.7 (2017-06-07)
|
||||
|
||||
- No changes.
|
||||
|
||||
## 9.1.6 (2017-06-02)
|
||||
|
||||
- Fix visibility when referencing snippets.
|
||||
|
||||
## 9.1.5 (2017-05-31)
|
||||
|
||||
- Move uploads from 'public/uploads' to 'public/uploads/system'.
|
||||
- Restrict API X-Frame-Options to same origin.
|
||||
- Allow users autocomplete by author_id only for authenticated users.
|
||||
|
||||
## 9.1.4 (2017-05-12)
|
||||
|
||||
- Fix error on CI/CD Settings page related to invalid pipeline trigger. !10948 (dosuken123)
|
||||
- Sort the network graph both by commit date and topographically. !11057
|
||||
- Fix cross referencing for private and internal projects. !11243
|
||||
- Handle incoming emails from aliases correctly.
|
||||
- Gracefully handle failures for incoming emails which do not match on the To header, and have no References header.
|
||||
- Add missing project attributes to Import/Export.
|
||||
- Fixed search terms not correctly highlighting.
|
||||
- Fixed bug where merge request JSON would be displayed.
|
||||
|
||||
## 9.1.3 (2017-05-05)
|
||||
|
||||
- Do not show private groups on subgroups page if user doesn't have access to.
|
||||
|
@ -302,6 +1040,7 @@ entry.
|
|||
|
||||
## 9.1.0 (2017-04-22)
|
||||
|
||||
- Add Jupyter notebook rendering !10017
|
||||
- Added merge requests empty state. !7342
|
||||
- Add option to start a new resolvable discussion in an MR. !7527
|
||||
- Hide form inputs for group member without editing rights. !7816
|
||||
|
@ -548,6 +1287,30 @@ entry.
|
|||
- Only send chat notifications for the default branch.
|
||||
- Don't fill in the default kubernetes namespace.
|
||||
|
||||
## 9.0.12 (2017-07-20)
|
||||
|
||||
- Fix an infinite loop when handling user-supplied regular expressions.
|
||||
|
||||
## 9.0.11 (2017-07-19)
|
||||
|
||||
- Renders 404 if given project is not readable by the user on Todos dashboard.
|
||||
- Fix incorrect project authorizations.
|
||||
- Remove uploads/appearance symlink. A leftover from a previous migration.
|
||||
|
||||
## 9.0.10 (2017-06-07)
|
||||
|
||||
- No changes.
|
||||
|
||||
## 9.0.9 (2017-06-02)
|
||||
|
||||
- Fix visibility when referencing snippets.
|
||||
|
||||
## 9.0.8 (2017-05-31)
|
||||
|
||||
- Move uploads from 'public/uploads' to 'public/uploads/system'.
|
||||
- Restrict API X-Frame-Options to same origin.
|
||||
- Allow users autocomplete by author_id only for authenticated users.
|
||||
|
||||
## 9.0.7 (2017-05-05)
|
||||
|
||||
- Enforce project features when searching blobs and wikis.
|
||||
|
@ -904,6 +1667,11 @@ entry.
|
|||
- Change development tanuki favicon colors to match logo color order.
|
||||
- API issues - support filtering by iids.
|
||||
|
||||
## 8.17.7 (2017-07-19)
|
||||
|
||||
- Renders 404 if given project is not readable by the user on Todos dashboard.
|
||||
- Fix incorrect project authorizations.
|
||||
|
||||
## 8.17.6 (2017-05-05)
|
||||
|
||||
- Enforce project features when searching blobs and wikis.
|
||||
|
@ -1151,8 +1919,6 @@ entry.
|
|||
|
||||
## 8.16.7 (2017-02-27)
|
||||
|
||||
- No changes.
|
||||
- No changes.
|
||||
- Fix MR changes tab size count when there are over 100 files in the diff.
|
||||
|
||||
## 8.16.6 (2017-02-17)
|
||||
|
@ -1366,6 +2132,14 @@ entry.
|
|||
- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects.
|
||||
- Patch XSS vulnerability in RDOC support.
|
||||
|
||||
## 8.15.5 (2017-01-20)
|
||||
|
||||
- Ensure export files are removed after a namespace is deleted.
|
||||
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
|
||||
- Prevent users from creating notes on resources they can't access.
|
||||
- Prevent users from deleting system deploy keys via the project deploy key API.
|
||||
- Upgrade omniauth gem to 1.3.2.
|
||||
|
||||
## 8.15.4 (2017-01-09)
|
||||
|
||||
- Make successful pipeline emails off for watchers. !8176
|
||||
|
@ -1648,6 +2422,14 @@ entry.
|
|||
- Speed up group milestone index by passing group_id to IssuesFinder. !8363
|
||||
- Ensure issuable state changes only fire webhooks once.
|
||||
|
||||
## 8.14.7 (2017-01-21)
|
||||
|
||||
- Ensure export files are removed after a namespace is deleted.
|
||||
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
|
||||
- Prevent users from creating notes on resources they can't access.
|
||||
- Prevent users from deleting system deploy keys via the project deploy key API.
|
||||
- Upgrade omniauth gem to 1.3.2.
|
||||
|
||||
## 8.14.6 (2017-01-10)
|
||||
|
||||
- Update the gitlab-markup gem to the version 1.5.1. !8509
|
||||
|
@ -1930,6 +2712,14 @@ entry.
|
|||
- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
|
||||
- Fix 404 when visit /projects page
|
||||
|
||||
## 8.13.12 (2017-01-21)
|
||||
|
||||
- Ensure export files are removed after a namespace is deleted.
|
||||
- Don't allow project guests to subscribe to merge requests through the API. (Robert Schilling)
|
||||
- Prevent users from creating notes on resources they can't access.
|
||||
- Prevent users from deleting system deploy keys via the project deploy key API.
|
||||
- Upgrade omniauth gem to 1.3.2.
|
||||
|
||||
## 8.13.11 (2017-01-10)
|
||||
|
||||
- Update the gitlab-markup gem to the version 1.5.1. !8509
|
||||
|
|
|
@ -31,7 +31,7 @@ _This notice should stay as the first item in the CONTRIBUTING.MD file._
|
|||
- [Issue tracker guidelines](#issue-tracker-guidelines)
|
||||
- [Issue weight](#issue-weight)
|
||||
- [Regression issues](#regression-issues)
|
||||
- [Technical debt](#technical-debt)
|
||||
- [Technical and UX debt](#technical-and-ux-debt)
|
||||
- [Stewardship](#stewardship)
|
||||
- [Merge requests](#merge-requests)
|
||||
- [Merge request guidelines](#merge-request-guidelines)
|
||||
|
@ -49,6 +49,8 @@ _This notice should stay as the first item in the CONTRIBUTING.MD file._
|
|||
Thank you for your interest in contributing to GitLab. This guide details how
|
||||
to contribute to GitLab in a way that is efficient for everyone.
|
||||
|
||||
Looking for something to work on? Look for the label [Accepting Merge Requests](#i-want-to-contribute).
|
||||
|
||||
GitLab comes into two flavors, GitLab Community Edition (CE) our free and open
|
||||
source edition, and GitLab Enterprise Edition (EE) which is our commercial
|
||||
edition. Throughout this guide you will see references to CE and EE for
|
||||
|
@ -112,8 +114,8 @@ scheduling into milestones. Labelling is a task for everyone.
|
|||
Most issues will have labels for at least one of the following:
|
||||
|
||||
- Type: ~"feature proposal", ~bug, ~customer, etc.
|
||||
- Subject: ~wiki, ~"container registry", ~ldap, ~api, etc.
|
||||
- Team: ~CI, ~Discussion, ~Edge, ~Frontend, ~Platform, etc.
|
||||
- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
|
||||
- Team: ~CI, ~Discussion, ~Edge, ~Platform, etc.
|
||||
- Priority: ~Deliverable, ~Stretch
|
||||
|
||||
All labels, their meaning and priority are defined on the
|
||||
|
@ -276,7 +278,7 @@ For feature proposals for EE, open an issue on the
|
|||
In order to help track the feature proposals, we have created a
|
||||
[`feature proposal`][fpl] label. For the time being, users that are not members
|
||||
of the project cannot add labels. You can instead ask one of the [core team]
|
||||
members to add the label `feature proposal` to the issue or add the following
|
||||
members to add the label ~"feature proposal" to the issue or add the following
|
||||
code snippet right after your description in a new line: `~"feature proposal"`.
|
||||
|
||||
Please keep feature proposals as small and simple as possible, complex ones
|
||||
|
@ -342,27 +344,29 @@ addressed.
|
|||
[8.3 Regressions]: https://gitlab.com/gitlab-org/gitlab-ce/issues/4127
|
||||
[update the notes]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/pro-tips.md#update-the-regression-issue
|
||||
|
||||
### Technical debt
|
||||
### Technical and UX debt
|
||||
|
||||
In order to track things that can be improved in GitLab's codebase, we created
|
||||
the ~"technical debt" label in [GitLab's issue tracker][ce-tracker].
|
||||
In order to track things that can be improved in GitLab's codebase,
|
||||
we use the ~"technical debt" label in [GitLab's issue tracker][ce-tracker].
|
||||
For user experience improvements, we use the ~"UX debt" label.
|
||||
|
||||
This label should be added to issues that describe things that can be improved,
|
||||
shortcuts that have been taken, code that needs refactoring, features that need
|
||||
additional attention, and all other things that have been left behind due to
|
||||
high velocity of development.
|
||||
These labels should be added to issues that describe things that can be improved,
|
||||
shortcuts that have been taken, features that need additional attention, and all
|
||||
other things that have been left behind due to high velocity of development.
|
||||
For example, code that needs refactoring should use the ~"technical debt" label,
|
||||
user experience refinements should use the ~"UX debt" label.
|
||||
|
||||
Everyone can create an issue, though you may need to ask for adding a specific
|
||||
label, if you do not have permissions to do it by yourself. Additional labels
|
||||
can be combined with the `technical debt` label, to make it easier to schedule
|
||||
can be combined with these labels, to make it easier to schedule
|
||||
the improvements for a release.
|
||||
|
||||
Issues tagged with the `technical debt` label have the same priority like issues
|
||||
Issues tagged with these labels have the same priority like issues
|
||||
that describe a new feature to be introduced in GitLab, and should be scheduled
|
||||
for a release by the appropriate person.
|
||||
|
||||
Make sure to mention the merge request that the `technical debt` issue is
|
||||
associated with in the description of the issue.
|
||||
Make sure to mention the merge request that the ~"technical debt" issue or
|
||||
~"UX debt" issue is associated with in the description of the issue.
|
||||
|
||||
### Stewardship
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.10.0
|
||||
0.35.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.4.2
|
||||
0.5.1
|
||||
|
|
|
@ -1 +1 @@
|
|||
5.0.4
|
||||
5.8.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
2.0.0
|
||||
3.0.0
|
||||
|
|
111
Gemfile
111
Gemfile
|
@ -12,12 +12,13 @@ gem 'sprockets', '~> 3.7.0'
|
|||
gem 'default_value_for', '~> 3.0.0'
|
||||
|
||||
# Supported DBs
|
||||
gem 'mysql2', '~> 0.3.16', group: :mysql
|
||||
gem 'mysql2', '~> 0.4.5', group: :mysql
|
||||
gem 'pg', '~> 0.18.2', group: :postgres
|
||||
|
||||
gem 'rugged', '~> 0.25.1.1'
|
||||
gem 'rugged', '~> 0.26.0'
|
||||
gem 'grape-route-helpers', '~> 2.0.0'
|
||||
|
||||
gem 'faraday', '~> 0.11.0'
|
||||
gem 'faraday', '~> 0.12'
|
||||
|
||||
# Authentication libraries
|
||||
gem 'devise', '~> 4.2'
|
||||
|
@ -26,7 +27,7 @@ gem 'doorkeeper-openid_connect', '~> 1.1.0'
|
|||
gem 'omniauth', '~> 1.4.2'
|
||||
gem 'omniauth-auth0', '~> 1.4.1'
|
||||
gem 'omniauth-azure-oauth2', '~> 0.0.6'
|
||||
gem 'omniauth-cas3', '~> 1.1.2'
|
||||
gem 'omniauth-cas3', '~> 1.1.4'
|
||||
gem 'omniauth-facebook', '~> 4.0.0'
|
||||
gem 'omniauth-github', '~> 1.1.1'
|
||||
gem 'omniauth-gitlab', '~> 1.0.2'
|
||||
|
@ -37,7 +38,7 @@ gem 'omniauth-saml', '~> 1.7.0'
|
|||
gem 'omniauth-shibboleth', '~> 1.2.0'
|
||||
gem 'omniauth-twitter', '~> 1.2.0'
|
||||
gem 'omniauth_crowd', '~> 2.2.0'
|
||||
gem 'omniauth-authentiq', '~> 0.3.0'
|
||||
gem 'omniauth-authentiq', '~> 0.3.1'
|
||||
gem 'rack-oauth2', '~> 1.2.1'
|
||||
gem 'jwt', '~> 1.5.6'
|
||||
|
||||
|
@ -57,10 +58,14 @@ gem 'validates_hostname', '~> 1.0.6'
|
|||
# Browser detection
|
||||
gem 'browser', '~> 2.2'
|
||||
|
||||
# GPG
|
||||
gem 'gpgme'
|
||||
|
||||
# LDAP Auth
|
||||
# GitLab fork with several improvements to original library. For full list of changes
|
||||
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
|
||||
gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
|
||||
gem 'gitlab_omniauth-ldap', '~> 2.0.3', require: 'omniauth-ldap'
|
||||
gem 'net-ldap'
|
||||
|
||||
# Git Wiki
|
||||
# Required manually in config/initializers/gollum.rb to control load order
|
||||
|
@ -71,7 +76,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
|
|||
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
|
||||
|
||||
# API
|
||||
gem 'grape', '~> 0.19.0'
|
||||
gem 'grape', '~> 0.19.2'
|
||||
gem 'grape-entity', '~> 0.6.0'
|
||||
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
|
||||
|
||||
|
@ -79,24 +84,25 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
|
|||
gem 'hashie-forbidden_attributes'
|
||||
|
||||
# Pagination
|
||||
gem 'kaminari', '~> 0.17.0'
|
||||
gem 'kaminari', '~> 1.0'
|
||||
|
||||
# HAML
|
||||
gem 'hamlit', '~> 2.6.1'
|
||||
|
||||
# Files attachments
|
||||
gem 'carrierwave', '~> 1.0'
|
||||
gem 'carrierwave', '~> 1.1'
|
||||
|
||||
# Drag and Drop UI
|
||||
gem 'dropzonejs-rails', '~> 0.7.1'
|
||||
|
||||
# for backups
|
||||
gem 'fog-aws', '~> 0.9'
|
||||
gem 'fog-aws', '~> 1.4'
|
||||
gem 'fog-core', '~> 1.44'
|
||||
gem 'fog-google', '~> 0.5'
|
||||
gem 'fog-local', '~> 0.3'
|
||||
gem 'fog-openstack', '~> 0.1'
|
||||
gem 'fog-rackspace', '~> 0.1.1'
|
||||
gem 'fog-aliyun', '~> 0.1.0'
|
||||
|
||||
# for Google storage
|
||||
gem 'google-api-client', '~> 0.8.6'
|
||||
|
@ -109,7 +115,7 @@ gem 'seed-fu', '~> 2.3.5'
|
|||
|
||||
# Markdown and HTML processing
|
||||
gem 'html-pipeline', '~> 1.11.0'
|
||||
gem 'deckar01-task_list', '1.0.6', require: 'task_list/railtie'
|
||||
gem 'deckar01-task_list', '2.0.0'
|
||||
gem 'gitlab-markup', '~> 1.5.1'
|
||||
gem 'redcarpet', '~> 3.4'
|
||||
gem 'RedCloth', '~> 4.3.2'
|
||||
|
@ -120,11 +126,9 @@ gem 'wikicloth', '0.8.1'
|
|||
gem 'asciidoctor', '~> 1.5.2'
|
||||
gem 'asciidoctor-plantuml', '0.0.7'
|
||||
gem 'rouge', '~> 2.0'
|
||||
gem 'truncato', '~> 0.7.8'
|
||||
|
||||
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
|
||||
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
|
||||
gem 'nokogiri', '~> 1.6.7', '>= 1.6.7.2'
|
||||
gem 'truncato', '~> 0.7.9'
|
||||
gem 'bootstrap_form', '~> 2.7.0'
|
||||
gem 'nokogiri', '~> 1.8.0'
|
||||
|
||||
# Diffs
|
||||
gem 'diffy', '~> 3.1.0'
|
||||
|
@ -145,24 +149,24 @@ gem 'acts-as-taggable-on', '~> 4.0'
|
|||
|
||||
# Background jobs
|
||||
gem 'sidekiq', '~> 5.0'
|
||||
gem 'sidekiq-cron', '~> 0.4.4'
|
||||
gem 'sidekiq-cron', '~> 0.6.0'
|
||||
gem 'redis-namespace', '~> 1.5.2'
|
||||
gem 'sidekiq-limit_fetch', '~> 3.4'
|
||||
gem 'sidekiq-limit_fetch', '~> 3.4', require: false
|
||||
|
||||
# Cron Parser
|
||||
gem 'rufus-scheduler', '~> 3.1.10'
|
||||
gem 'rufus-scheduler', '~> 3.4'
|
||||
|
||||
# HTTP requests
|
||||
gem 'httparty', '~> 0.13.3'
|
||||
|
||||
# Colored output to console
|
||||
gem 'rainbow', '~> 2.1.0'
|
||||
gem 'rainbow', '~> 2.2'
|
||||
|
||||
# GitLab settings
|
||||
gem 'settingslogic', '~> 2.0.9'
|
||||
|
||||
# Linear-time regex library for untrusted regular expressions
|
||||
gem 're2', '~> 1.0.0'
|
||||
gem 're2', '~> 1.1.1'
|
||||
|
||||
# Misc
|
||||
|
||||
|
@ -238,38 +242,52 @@ gem 'webpack-rails', '~> 0.9.10'
|
|||
gem 'rack-proxy', '~> 0.6.0'
|
||||
|
||||
gem 'sass-rails', '~> 5.0.6'
|
||||
gem 'coffee-rails', '~> 4.1.0'
|
||||
gem 'uglifier', '~> 2.7.2'
|
||||
|
||||
gem 'addressable', '~> 2.3.8'
|
||||
gem 'bootstrap-sass', '~> 3.3.0'
|
||||
gem 'font-awesome-rails', '~> 4.7'
|
||||
gem 'gemojione', '~> 3.0'
|
||||
gem 'gemojione', '~> 3.3'
|
||||
gem 'gon', '~> 6.1.0'
|
||||
gem 'jquery-atwho-rails', '~> 1.3.2'
|
||||
gem 'jquery-rails', '~> 4.1.0'
|
||||
gem 'request_store', '~> 1.3'
|
||||
gem 'select2-rails', '~> 3.5.9'
|
||||
gem 'virtus', '~> 1.0.1'
|
||||
gem 'net-ssh', '~> 3.0.1'
|
||||
gem 'base32', '~> 0.3.0'
|
||||
|
||||
# Sentry integration
|
||||
gem 'sentry-raven', '~> 2.4.0'
|
||||
gem 'sentry-raven', '~> 2.5.3'
|
||||
|
||||
gem 'premailer-rails', '~> 1.9.0'
|
||||
gem 'premailer-rails', '~> 1.9.7'
|
||||
|
||||
# I18n
|
||||
gem 'ruby_parser', '~> 3.8.4', require: false
|
||||
gem 'ruby_parser', '~> 3.8', require: false
|
||||
gem 'rails-i18n', '~> 4.0.9'
|
||||
gem 'gettext_i18n_rails', '~> 1.8.0'
|
||||
gem 'gettext_i18n_rails_js', '~> 1.2.0'
|
||||
gem 'gettext', '~> 3.2.2', require: false, group: :development
|
||||
|
||||
# Perf bar
|
||||
gem 'peek', '~> 1.0.1'
|
||||
gem 'peek-gc', '~> 0.0.2'
|
||||
gem 'peek-host', '~> 1.0.0'
|
||||
gem 'peek-mysql2', '~> 1.1.0', group: :mysql
|
||||
gem 'peek-performance_bar', '~> 1.3.0'
|
||||
gem 'peek-pg', '~> 1.3.0', group: :postgres
|
||||
gem 'peek-rblineprof', '~> 0.2.0'
|
||||
gem 'peek-redis', '~> 1.2.0'
|
||||
gem 'peek-sidekiq', '~> 1.0.3'
|
||||
|
||||
# Metrics
|
||||
group :metrics do
|
||||
gem 'allocations', '~> 1.0', require: false, platform: :mri
|
||||
gem 'method_source', '~> 0.8', require: false
|
||||
gem 'influxdb', '~> 0.2', require: false
|
||||
|
||||
# Prometheus
|
||||
gem 'prometheus-client-mmap', '~>0.7.0.beta14'
|
||||
gem 'raindrops', '~> 0.18'
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
@ -293,11 +311,11 @@ group :development, :test do
|
|||
gem 'pry-rails', '~> 0.3.4'
|
||||
|
||||
gem 'awesome_print', '~> 1.2.0', require: false
|
||||
gem 'fuubar', '~> 2.0.0'
|
||||
gem 'fuubar', '~> 2.2.0'
|
||||
|
||||
gem 'database_cleaner', '~> 1.5.0'
|
||||
gem 'factory_girl_rails', '~> 4.7.0'
|
||||
gem 'rspec-rails', '~> 3.5.0'
|
||||
gem 'rspec-rails', '~> 3.6.0'
|
||||
gem 'rspec-retry', '~> 0.4.5'
|
||||
gem 'spinach-rails', '~> 0.2.1'
|
||||
gem 'spinach-rerun-reporter', '~> 0.0.2'
|
||||
|
@ -318,10 +336,10 @@ group :development, :test do
|
|||
gem 'spring-commands-rspec', '~> 1.0.4'
|
||||
gem 'spring-commands-spinach', '~> 1.1.0'
|
||||
|
||||
gem 'rubocop', '~> 0.47.1', require: false
|
||||
gem 'rubocop-rspec', '~> 1.15.0', require: false
|
||||
gem 'scss_lint', '~> 0.47.0', require: false
|
||||
gem 'haml_lint', '~> 0.21.0', require: false
|
||||
gem 'rubocop', '~> 0.49.1', require: false
|
||||
gem 'rubocop-rspec', '~> 1.15.1', require: false
|
||||
gem 'scss_lint', '~> 0.54.0', require: false
|
||||
gem 'haml_lint', '~> 0.26.0', require: false
|
||||
gem 'simplecov', '~> 0.14.0', require: false
|
||||
gem 'flay', '~> 2.8.0', require: false
|
||||
gem 'bundler-audit', '~> 0.5.0', require: false
|
||||
|
@ -337,10 +355,10 @@ group :development, :test do
|
|||
end
|
||||
|
||||
group :test do
|
||||
gem 'shoulda-matchers', '~> 2.8.0', require: false
|
||||
gem 'shoulda-matchers', '~> 3.1.2', require: false
|
||||
gem 'email_spec', '~> 1.6.0'
|
||||
gem 'json-schema', '~> 2.6.2'
|
||||
gem 'webmock', '~> 1.24.0'
|
||||
gem 'webmock', '~> 2.3.2'
|
||||
gem 'test_after_commit', '~> 1.1'
|
||||
gem 'sham_rack', '~> 1.3.6'
|
||||
gem 'timecop', '~> 0.8.0'
|
||||
|
@ -357,10 +375,10 @@ gem 'html2text'
|
|||
gem 'ruby-prof', '~> 0.16.2'
|
||||
|
||||
# OAuth
|
||||
gem 'oauth2', '~> 1.3.0'
|
||||
gem 'oauth2', '~> 1.4'
|
||||
|
||||
# Soft deletion
|
||||
gem 'paranoia', '~> 2.2'
|
||||
gem 'paranoia', '~> 2.3.1'
|
||||
|
||||
# Health check
|
||||
gem 'health_check', '~> 2.6.0'
|
||||
|
@ -369,7 +387,24 @@ gem 'health_check', '~> 2.6.0'
|
|||
gem 'vmstat', '~> 2.3.0'
|
||||
gem 'sys-filesystem', '~> 1.1.6'
|
||||
|
||||
# SSH host key support
|
||||
gem 'net-ssh', '~> 4.1.0'
|
||||
|
||||
# Required for ED25519 SSH host key support
|
||||
group :ed25519 do
|
||||
gem 'rbnacl-libsodium'
|
||||
gem 'rbnacl', '~> 3.2'
|
||||
gem 'bcrypt_pbkdf', '~> 1.0'
|
||||
end
|
||||
|
||||
# Gitaly GRPC client
|
||||
gem 'gitaly', '~> 0.6.0'
|
||||
gem 'gitaly', '~> 0.26.0'
|
||||
|
||||
gem 'toml-rb', '~> 0.3.15', require: false
|
||||
|
||||
# Feature toggles
|
||||
gem 'flipper', '~> 0.10.2'
|
||||
gem 'flipper-active_record', '~> 0.10.2'
|
||||
|
||||
# Structured logging
|
||||
gem 'lograge', '~> 0.5'
|
||||
|
|
397
Gemfile.lock
397
Gemfile.lock
|
@ -56,6 +56,7 @@ GEM
|
|||
asciidoctor-plantuml (0.0.7)
|
||||
asciidoctor (~> 1.5)
|
||||
ast (2.3.0)
|
||||
atomic (1.1.99)
|
||||
attr_encrypted (3.0.3)
|
||||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.0)
|
||||
|
@ -74,6 +75,7 @@ GEM
|
|||
babosa (1.0.2)
|
||||
base32 (0.3.2)
|
||||
bcrypt (3.1.11)
|
||||
bcrypt_pbkdf (1.0.0)
|
||||
benchmark-ips (2.3.0)
|
||||
better_errors (2.1.1)
|
||||
coderay (>= 1.0.0)
|
||||
|
@ -85,6 +87,7 @@ GEM
|
|||
bootstrap-sass (3.3.6)
|
||||
autoprefixer-rails (>= 5.2.1)
|
||||
sass (>= 3.3.4)
|
||||
bootstrap_form (2.7.0)
|
||||
brakeman (3.6.1)
|
||||
browser (2.2.0)
|
||||
builder (3.2.3)
|
||||
|
@ -105,7 +108,7 @@ GEM
|
|||
capybara-screenshot (1.0.14)
|
||||
capybara (>= 1.0, < 3)
|
||||
launchy
|
||||
carrierwave (1.0.0)
|
||||
carrierwave (1.1.0)
|
||||
activemodel (>= 4.0.0)
|
||||
activesupport (>= 4.0.0)
|
||||
mime-types (>= 1.16)
|
||||
|
@ -120,20 +123,15 @@ GEM
|
|||
coderay (1.1.1)
|
||||
coercible (1.0.0)
|
||||
descendants_tracker (~> 0.0.1)
|
||||
coffee-rails (4.1.1)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0, < 5.1.x)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.10.0)
|
||||
colorize (0.7.7)
|
||||
concurrent-ruby (1.0.5)
|
||||
concurrent-ruby-ext (1.0.5)
|
||||
concurrent-ruby (= 1.0.5)
|
||||
connection_pool (2.2.1)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
creole (0.5.0)
|
||||
css_parser (1.4.1)
|
||||
css_parser (1.5.0)
|
||||
addressable
|
||||
d3_rails (3.5.11)
|
||||
railties (>= 3.1.0)
|
||||
|
@ -141,10 +139,8 @@ GEM
|
|||
database_cleaner (1.5.3)
|
||||
debug_inspector (0.0.2)
|
||||
debugger-ruby_core_source (1.3.8)
|
||||
deckar01-task_list (1.0.6)
|
||||
activesupport (~> 4.0)
|
||||
deckar01-task_list (2.0.0)
|
||||
html-pipeline
|
||||
rack (~> 1.0)
|
||||
default_value_for (3.0.2)
|
||||
activerecord (>= 3.2.0, < 5.1)
|
||||
descendants_tracker (0.0.4)
|
||||
|
@ -161,7 +157,7 @@ GEM
|
|||
devise (~> 4.0)
|
||||
railties
|
||||
rotp (~> 2.0)
|
||||
diff-lcs (1.2.5)
|
||||
diff-lcs (1.3)
|
||||
diffy (3.1.0)
|
||||
docile (1.1.5)
|
||||
domain_name (0.5.20161021)
|
||||
|
@ -181,8 +177,10 @@ GEM
|
|||
equalizer (0.0.11)
|
||||
erubis (2.7.0)
|
||||
escape_utils (1.1.1)
|
||||
et-orbi (1.0.3)
|
||||
tzinfo
|
||||
eventmachine (1.0.8)
|
||||
excon (0.55.0)
|
||||
excon (0.57.1)
|
||||
execjs (2.6.0)
|
||||
expression_parser (0.9.0)
|
||||
extlib (0.9.16)
|
||||
|
@ -191,7 +189,7 @@ GEM
|
|||
factory_girl_rails (4.7.0)
|
||||
factory_girl (~> 4.7.0)
|
||||
railties (>= 3.0.0)
|
||||
faraday (0.11.0)
|
||||
faraday (0.12.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday_middleware (0.11.0.1)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
|
@ -206,29 +204,38 @@ GEM
|
|||
path_expander (~> 1.0)
|
||||
ruby_parser (~> 3.0)
|
||||
sexp_processor (~> 4.0)
|
||||
flipper (0.10.2)
|
||||
flipper-active_record (0.10.2)
|
||||
activerecord (>= 3.2, < 6)
|
||||
flipper (~> 0.10.2)
|
||||
flowdock (0.7.1)
|
||||
httparty (~> 0.7)
|
||||
multi_json
|
||||
fog-aws (0.13.0)
|
||||
fog-aliyun (0.1.0)
|
||||
fog-core (~> 1.27)
|
||||
fog-json (~> 1.0)
|
||||
ipaddress (~> 0.8)
|
||||
xml-simple (~> 1.1)
|
||||
fog-aws (1.4.0)
|
||||
fog-core (~> 1.38)
|
||||
fog-json (~> 1.0)
|
||||
fog-xml (~> 0.1)
|
||||
ipaddress (~> 0.8)
|
||||
fog-core (1.44.1)
|
||||
fog-core (1.44.3)
|
||||
builder
|
||||
excon (~> 0.49)
|
||||
formatador (~> 0.2)
|
||||
fog-google (0.5.0)
|
||||
fog-google (0.5.3)
|
||||
fog-core
|
||||
fog-json
|
||||
fog-xml
|
||||
fog-json (1.0.2)
|
||||
fog-core (~> 1.0)
|
||||
multi_json (~> 1.10)
|
||||
fog-local (0.3.0)
|
||||
fog-local (0.3.1)
|
||||
fog-core (~> 1.27)
|
||||
fog-openstack (0.1.6)
|
||||
fog-core (>= 1.39)
|
||||
fog-openstack (0.1.21)
|
||||
fog-core (>= 1.40)
|
||||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
fog-rackspace (0.1.1)
|
||||
|
@ -244,12 +251,12 @@ GEM
|
|||
foreman (0.78.0)
|
||||
thor (~> 0.19.1)
|
||||
formatador (0.2.5)
|
||||
fuubar (2.0.0)
|
||||
rspec (~> 3.0)
|
||||
fuubar (2.2.0)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
gemnasium-gitlab-service (0.2.6)
|
||||
rugged (~> 0.21)
|
||||
gemojione (3.0.1)
|
||||
gemojione (3.3.0)
|
||||
json
|
||||
get_process_mem (0.2.0)
|
||||
gettext (3.2.2)
|
||||
|
@ -263,7 +270,7 @@ GEM
|
|||
po_to_json (>= 1.0.0)
|
||||
rails (>= 3.2.0)
|
||||
gherkin-ruby (0.3.2)
|
||||
gitaly (0.6.0)
|
||||
gitaly (0.26.0)
|
||||
google-protobuf (~> 3.1)
|
||||
grpc (~> 1.0)
|
||||
github-linguist (4.7.6)
|
||||
|
@ -271,7 +278,7 @@ GEM
|
|||
escape_utils (~> 1.1.0)
|
||||
mime-types (>= 1.19)
|
||||
rugged (>= 0.23.0b)
|
||||
github-markup (1.4.0)
|
||||
github-markup (1.6.1)
|
||||
gitlab-flowdock-git-hook (1.0.1)
|
||||
flowdock (~> 0.7)
|
||||
gitlab-grit (>= 2.4.1)
|
||||
|
@ -282,22 +289,23 @@ GEM
|
|||
mime-types (>= 1.16, < 3)
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab-markup (1.5.1)
|
||||
gitlab_omniauth-ldap (1.2.1)
|
||||
net-ldap (~> 0.9)
|
||||
omniauth (~> 1.0)
|
||||
pyu-ruby-sasl (~> 0.0.3.1)
|
||||
rubyntlm (~> 0.3)
|
||||
gitlab_omniauth-ldap (2.0.3)
|
||||
net-ldap (~> 0.16)
|
||||
omniauth (~> 1.3)
|
||||
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
|
||||
rubyntlm (~> 0.5)
|
||||
globalid (0.3.7)
|
||||
activesupport (>= 4.1.0)
|
||||
gollum-grit_adapter (1.0.1)
|
||||
gitlab-grit (~> 2.7, >= 2.7.1)
|
||||
gollum-lib (4.2.1)
|
||||
github-markup (~> 1.4.0)
|
||||
gollum-lib (4.2.7)
|
||||
gemojione (~> 3.2)
|
||||
github-markup (~> 1.6)
|
||||
gollum-grit_adapter (~> 1.0)
|
||||
nokogiri (~> 1.6.4)
|
||||
rouge (~> 2.0)
|
||||
sanitize (~> 2.1.0)
|
||||
stringex (~> 2.5.1)
|
||||
nokogiri (>= 1.6.1, < 2.0)
|
||||
rouge (~> 2.1)
|
||||
sanitize (~> 2.1)
|
||||
stringex (~> 2.6)
|
||||
gollum-rugged_adapter (0.4.4)
|
||||
mime-types (>= 1.15)
|
||||
rugged (~> 0.25)
|
||||
|
@ -317,7 +325,7 @@ GEM
|
|||
multi_json (~> 1.10)
|
||||
retriable (~> 1.4)
|
||||
signet (~> 0.6)
|
||||
google-protobuf (3.2.0.2)
|
||||
google-protobuf (3.3.0)
|
||||
googleauth (0.5.1)
|
||||
faraday (~> 0.9)
|
||||
jwt (~> 1.4)
|
||||
|
@ -326,34 +334,41 @@ GEM
|
|||
multi_json (~> 1.11)
|
||||
os (~> 0.9)
|
||||
signet (~> 0.7)
|
||||
grape (0.19.1)
|
||||
gpgme (2.0.13)
|
||||
mini_portile2 (~> 2.1)
|
||||
grape (0.19.2)
|
||||
activesupport
|
||||
builder
|
||||
hashie (>= 2.1.0)
|
||||
multi_json (>= 1.3.2)
|
||||
multi_xml (>= 0.5.2)
|
||||
mustermann-grape (~> 0.4.0)
|
||||
mustermann-grape (~> 1.0.0)
|
||||
rack (>= 1.3.0)
|
||||
rack-accept
|
||||
virtus (>= 1.0.0)
|
||||
grape-entity (0.6.0)
|
||||
activesupport
|
||||
multi_json (>= 1.3.2)
|
||||
grpc (1.2.5)
|
||||
grape-route-helpers (2.0.0)
|
||||
activesupport
|
||||
grape (~> 0.16, >= 0.16.0)
|
||||
rake
|
||||
grpc (1.4.0)
|
||||
google-protobuf (~> 3.1)
|
||||
googleauth (~> 0.5.1)
|
||||
haml (4.0.7)
|
||||
tilt
|
||||
haml_lint (0.21.0)
|
||||
haml (~> 4.0)
|
||||
haml_lint (0.26.0)
|
||||
haml (>= 4.0, < 5.1)
|
||||
rainbow
|
||||
rake (>= 10, < 13)
|
||||
rubocop (>= 0.47.0)
|
||||
rubocop (>= 0.49.0)
|
||||
sysexits (~> 1.1)
|
||||
hamlit (2.6.1)
|
||||
temple (~> 0.7.6)
|
||||
thor
|
||||
tilt
|
||||
hashdiff (0.3.2)
|
||||
hashdiff (0.3.4)
|
||||
hashie (3.5.5)
|
||||
hashie-forbidden_attributes (0.1.1)
|
||||
hashie (>= 3.0)
|
||||
|
@ -381,7 +396,7 @@ GEM
|
|||
json (~> 1.8)
|
||||
multi_xml (>= 0.5.2)
|
||||
httpclient (2.8.2)
|
||||
i18n (0.8.1)
|
||||
i18n (0.8.6)
|
||||
ice_nine (0.11.2)
|
||||
influxdb (0.2.3)
|
||||
cause
|
||||
|
@ -405,9 +420,18 @@ GEM
|
|||
json-schema (2.6.2)
|
||||
addressable (~> 2.3.8)
|
||||
jwt (1.5.6)
|
||||
kaminari (0.17.0)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kaminari (1.0.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.0.1)
|
||||
kaminari-activerecord (= 1.0.1)
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-actionview (1.0.1)
|
||||
actionview
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-activerecord (1.0.1)
|
||||
activerecord
|
||||
kaminari-core (= 1.0.1)
|
||||
kaminari-core (1.0.1)
|
||||
kgio (2.10.0)
|
||||
knapsack (1.11.0)
|
||||
rake
|
||||
|
@ -437,35 +461,39 @@ GEM
|
|||
logging (2.2.2)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
lograge (0.5.1)
|
||||
actionpack (>= 4, < 5.2)
|
||||
activesupport (>= 4, < 5.2)
|
||||
railties (>= 4, < 5.2)
|
||||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.6.5)
|
||||
mail (2.6.6)
|
||||
mime-types (>= 1.16, < 4)
|
||||
mail_room (0.9.1)
|
||||
memoist (0.15.0)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.99.3)
|
||||
mimemagic (0.3.0)
|
||||
mini_portile2 (2.1.0)
|
||||
mini_portile2 (2.2.0)
|
||||
minitest (5.7.0)
|
||||
mmap2 (2.2.7)
|
||||
mousetrap-rails (1.4.6)
|
||||
multi_json (1.12.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mustermann (0.4.0)
|
||||
tool (~> 0.2)
|
||||
mustermann-grape (0.4.0)
|
||||
mustermann (= 0.4.0)
|
||||
mysql2 (0.3.20)
|
||||
net-ldap (0.12.1)
|
||||
net-ssh (3.0.1)
|
||||
mustermann (1.0.0)
|
||||
mustermann-grape (1.0.0)
|
||||
mustermann (~> 1.0.0)
|
||||
mysql2 (0.4.5)
|
||||
net-ldap (0.16.0)
|
||||
net-ssh (4.1.0)
|
||||
netrc (0.11.0)
|
||||
nokogiri (1.6.8.1)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
nokogiri (1.8.0)
|
||||
mini_portile2 (~> 2.2.0)
|
||||
numerizer (0.1.1)
|
||||
oauth (0.5.1)
|
||||
oauth2 (1.3.1)
|
||||
faraday (>= 0.8, < 0.12)
|
||||
oauth2 (1.4.0)
|
||||
faraday (>= 0.8, < 0.13)
|
||||
jwt (~> 1.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
|
@ -478,15 +506,15 @@ GEM
|
|||
rack (>= 1.0, < 3)
|
||||
omniauth-auth0 (1.4.1)
|
||||
omniauth-oauth2 (~> 1.1)
|
||||
omniauth-authentiq (0.3.0)
|
||||
omniauth-authentiq (0.3.1)
|
||||
omniauth-oauth2 (~> 1.3, >= 1.3.1)
|
||||
omniauth-azure-oauth2 (0.0.6)
|
||||
jwt (~> 1.0)
|
||||
omniauth (~> 1.0)
|
||||
omniauth-oauth2 (~> 1.1)
|
||||
omniauth-cas3 (1.1.3)
|
||||
omniauth-cas3 (1.1.4)
|
||||
addressable (~> 2.3)
|
||||
nokogiri (~> 1.6.6)
|
||||
nokogiri (~> 1.7, >= 1.7.1)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-facebook (4.0.0)
|
||||
omniauth-oauth2 (~> 1.2)
|
||||
|
@ -530,11 +558,42 @@ GEM
|
|||
rubypants (~> 0.2)
|
||||
orm_adapter (0.5.0)
|
||||
os (0.9.6)
|
||||
paranoia (2.2.0)
|
||||
activerecord (>= 4.0, < 5.1)
|
||||
parallel (1.11.2)
|
||||
paranoia (2.3.1)
|
||||
activerecord (>= 4.0, < 5.2)
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
path_expander (1.0.1)
|
||||
peek (1.0.1)
|
||||
concurrent-ruby (>= 0.9.0)
|
||||
concurrent-ruby-ext (>= 0.9.0)
|
||||
railties (>= 4.0.0)
|
||||
peek-gc (0.0.2)
|
||||
peek
|
||||
peek-host (1.0.0)
|
||||
peek
|
||||
peek-mysql2 (1.1.0)
|
||||
atomic (>= 1.0.0)
|
||||
mysql2
|
||||
peek
|
||||
peek-performance_bar (1.3.0)
|
||||
peek (>= 0.1.0)
|
||||
peek-pg (1.3.0)
|
||||
concurrent-ruby
|
||||
concurrent-ruby-ext
|
||||
peek
|
||||
pg
|
||||
peek-rblineprof (0.2.0)
|
||||
peek
|
||||
rblineprof
|
||||
peek-redis (1.2.0)
|
||||
atomic (>= 1.0.0)
|
||||
peek
|
||||
redis
|
||||
peek-sidekiq (1.0.3)
|
||||
atomic (>= 1.0.0)
|
||||
peek
|
||||
sidekiq
|
||||
pg (0.18.4)
|
||||
po_to_json (1.0.1)
|
||||
json (>= 1.6.0)
|
||||
|
@ -543,14 +602,22 @@ GEM
|
|||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
posix-spawn (0.3.11)
|
||||
posix-spawn (0.3.13)
|
||||
powerpack (0.1.1)
|
||||
premailer (1.8.6)
|
||||
css_parser (>= 1.3.6)
|
||||
premailer (1.10.4)
|
||||
addressable
|
||||
css_parser (>= 1.4.10)
|
||||
htmlentities (>= 4.0.0)
|
||||
premailer-rails (1.9.2)
|
||||
premailer-rails (1.9.7)
|
||||
actionmailer (>= 3, < 6)
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
proc_to_ast (0.1.0)
|
||||
coderay
|
||||
parser
|
||||
unparser
|
||||
procto (0.0.3)
|
||||
prometheus-client-mmap (0.7.0.beta14)
|
||||
mmap2 (~> 2.2, >= 2.2.7)
|
||||
pry (0.10.4)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
|
@ -561,7 +628,7 @@ GEM
|
|||
pry-rails (0.3.5)
|
||||
pry (>= 0.9.10)
|
||||
pyu-ruby-sasl (0.0.3.3)
|
||||
rack (1.6.5)
|
||||
rack (1.6.8)
|
||||
rack-accept (0.4.5)
|
||||
rack (>= 0.4)
|
||||
rack-attack (4.4.1)
|
||||
|
@ -598,19 +665,27 @@ GEM
|
|||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
rails-i18n (4.0.9)
|
||||
i18n (~> 0.7)
|
||||
railties (~> 4.0)
|
||||
railties (4.2.8)
|
||||
actionpack (= 4.2.8)
|
||||
activesupport (= 4.2.8)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.1.0)
|
||||
raindrops (0.17.0)
|
||||
rake (10.5.0)
|
||||
rainbow (2.2.2)
|
||||
rake
|
||||
raindrops (0.18.0)
|
||||
rake (12.0.0)
|
||||
rblineprof (0.3.6)
|
||||
debugger-ruby_core_source (~> 1.3)
|
||||
rbnacl (3.4.0)
|
||||
ffi
|
||||
rbnacl-libsodium (1.0.11)
|
||||
rbnacl (>= 3.0.1)
|
||||
rdoc (4.2.2)
|
||||
json (~> 1.4)
|
||||
re2 (1.0.0)
|
||||
re2 (1.1.1)
|
||||
recaptcha (3.0.0)
|
||||
json
|
||||
recursive-open-struct (1.0.0)
|
||||
|
@ -644,47 +719,44 @@ GEM
|
|||
retriable (1.4.1)
|
||||
rinku (2.0.0)
|
||||
rotp (2.1.2)
|
||||
rouge (2.0.7)
|
||||
rouge (2.1.0)
|
||||
rqrcode (0.7.0)
|
||||
chunky_png
|
||||
rqrcode-rails3 (0.1.7)
|
||||
rqrcode (>= 0.4.2)
|
||||
rspec (3.5.0)
|
||||
rspec-core (~> 3.5.0)
|
||||
rspec-expectations (~> 3.5.0)
|
||||
rspec-mocks (~> 3.5.0)
|
||||
rspec-core (3.5.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-expectations (3.5.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-mocks (3.5.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-mocks (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-rails (3.5.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-rails (3.6.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.5.0)
|
||||
rspec-expectations (~> 3.5.0)
|
||||
rspec-mocks (~> 3.5.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-retry (0.4.5)
|
||||
rspec-core
|
||||
rspec-set (0.1.3)
|
||||
rspec-support (3.5.0)
|
||||
rspec-support (3.6.0)
|
||||
rspec_profiling (0.0.5)
|
||||
activerecord
|
||||
pg
|
||||
rails
|
||||
sqlite3
|
||||
rubocop (0.47.1)
|
||||
rubocop (0.49.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.3.3.1, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
rubocop-rspec (1.15.0)
|
||||
rubocop-rspec (1.15.1)
|
||||
rubocop (>= 0.42.0)
|
||||
ruby-fogbugz (0.2.1)
|
||||
crack (~> 0.4)
|
||||
|
@ -692,13 +764,14 @@ GEM
|
|||
ruby-progressbar (1.8.1)
|
||||
ruby-saml (1.4.1)
|
||||
nokogiri (>= 1.5.10)
|
||||
ruby_parser (3.8.4)
|
||||
ruby_parser (3.9.0)
|
||||
sexp_processor (~> 4.1)
|
||||
rubyntlm (0.5.2)
|
||||
rubyntlm (0.6.2)
|
||||
rubypants (0.2.0)
|
||||
rubyzip (1.2.1)
|
||||
rufus-scheduler (3.1.10)
|
||||
rugged (0.25.1.1)
|
||||
rufus-scheduler (3.4.0)
|
||||
et-orbi (~> 1.0)
|
||||
rugged (0.26.0)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (2.1.0)
|
||||
nokogiri (>= 1.4.4)
|
||||
|
@ -712,31 +785,30 @@ GEM
|
|||
sawyer (0.8.1)
|
||||
addressable (>= 2.3.5, < 2.6)
|
||||
faraday (~> 0.8, < 1.0)
|
||||
scss_lint (0.47.1)
|
||||
rake (>= 0.9, < 11)
|
||||
sass (~> 3.4.15)
|
||||
scss_lint (0.54.0)
|
||||
rake (>= 0.9, < 13)
|
||||
sass (~> 3.4.20)
|
||||
securecompare (1.0.0)
|
||||
seed-fu (2.3.6)
|
||||
activerecord (>= 3.1)
|
||||
activesupport (>= 3.1)
|
||||
select2-rails (3.5.9.3)
|
||||
thor (~> 0.14)
|
||||
sentry-raven (2.4.0)
|
||||
sentry-raven (2.5.3)
|
||||
faraday (>= 0.7.6, < 1.0)
|
||||
settingslogic (2.0.9)
|
||||
sexp_processor (4.8.0)
|
||||
sexp_processor (4.9.0)
|
||||
sham_rack (1.3.6)
|
||||
rack
|
||||
shoulda-matchers (2.8.0)
|
||||
activesupport (>= 3.0.0)
|
||||
sidekiq (5.0.0)
|
||||
shoulda-matchers (3.1.2)
|
||||
activesupport (>= 4.0.0)
|
||||
sidekiq (5.0.4)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (~> 3.3, >= 3.3.3)
|
||||
sidekiq-cron (0.4.4)
|
||||
redis-namespace (>= 1.5.2)
|
||||
rufus-scheduler (>= 2.0.24)
|
||||
sidekiq-cron (0.6.0)
|
||||
rufus-scheduler (>= 3.3.0)
|
||||
sidekiq (>= 4.2.1)
|
||||
sidekiq-limit_fetch (3.4.0)
|
||||
sidekiq (>= 4)
|
||||
|
@ -784,7 +856,7 @@ GEM
|
|||
state_machines-activerecord (0.4.0)
|
||||
activerecord (>= 4.1, < 5.1)
|
||||
state_machines-activemodel (>= 0.3.0)
|
||||
stringex (2.5.2)
|
||||
stringex (2.7.1)
|
||||
sys-filesystem (1.1.6)
|
||||
ffi
|
||||
sysexits (1.2.0)
|
||||
|
@ -803,11 +875,10 @@ GEM
|
|||
timfel-krb5-auth (0.8.3)
|
||||
toml-rb (0.3.15)
|
||||
citrus (~> 3.0, > 3.0)
|
||||
tool (0.2.3)
|
||||
truncato (0.7.8)
|
||||
truncato (0.7.10)
|
||||
htmlentities (~> 4.3.1)
|
||||
nokogiri (~> 1.6.1)
|
||||
tzinfo (1.2.2)
|
||||
nokogiri (~> 1.8.0, >= 1.7.0)
|
||||
tzinfo (1.2.3)
|
||||
thread_safe (~> 0.1)
|
||||
u2f (0.2.1)
|
||||
uglifier (2.7.2)
|
||||
|
@ -817,7 +888,7 @@ GEM
|
|||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.2)
|
||||
unicode-display_width (1.1.3)
|
||||
unicode-display_width (1.3.0)
|
||||
unicorn (5.1.0)
|
||||
kgio (~> 2.6)
|
||||
raindrops (~> 0.7)
|
||||
|
@ -838,7 +909,7 @@ GEM
|
|||
vmstat (2.3.0)
|
||||
warden (1.2.6)
|
||||
rack (>= 1.0)
|
||||
webmock (1.24.6)
|
||||
webmock (2.3.2)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff
|
||||
|
@ -874,27 +945,28 @@ DEPENDENCIES
|
|||
awesome_print (~> 1.2.0)
|
||||
babosa (~> 1.0.2)
|
||||
base32 (~> 0.3.0)
|
||||
bcrypt_pbkdf (~> 1.0)
|
||||
benchmark-ips (~> 2.3.0)
|
||||
better_errors (~> 2.1.0)
|
||||
binding_of_caller (~> 0.7.2)
|
||||
bootstrap-sass (~> 3.3.0)
|
||||
bootstrap_form (~> 2.7.0)
|
||||
brakeman (~> 3.6.0)
|
||||
browser (~> 2.2)
|
||||
bullet (~> 5.5.0)
|
||||
bundler-audit (~> 0.5.0)
|
||||
capybara (~> 2.6.2)
|
||||
capybara-screenshot (~> 1.0.0)
|
||||
carrierwave (~> 1.0)
|
||||
carrierwave (~> 1.1)
|
||||
charlock_holmes (~> 0.7.3)
|
||||
chronic (~> 0.10.2)
|
||||
chronic_duration (~> 0.10.6)
|
||||
coffee-rails (~> 4.1.0)
|
||||
concurrent-ruby (~> 1.0.5)
|
||||
connection_pool (~> 2.0)
|
||||
creole (~> 0.5.0)
|
||||
d3_rails (~> 3.5.0)
|
||||
database_cleaner (~> 1.5.0)
|
||||
deckar01-task_list (= 1.0.6)
|
||||
deckar01-task_list (= 2.0.0)
|
||||
default_value_for (~> 3.0.0)
|
||||
devise (~> 4.2)
|
||||
devise-two-factor (~> 3.0.0)
|
||||
|
@ -905,10 +977,13 @@ DEPENDENCIES
|
|||
email_reply_trimmer (~> 0.1)
|
||||
email_spec (~> 1.6.0)
|
||||
factory_girl_rails (~> 4.7.0)
|
||||
faraday (~> 0.11.0)
|
||||
faraday (~> 0.12)
|
||||
ffaker (~> 2.4)
|
||||
flay (~> 2.8.0)
|
||||
fog-aws (~> 0.9)
|
||||
flipper (~> 0.10.2)
|
||||
flipper-active_record (~> 0.10.2)
|
||||
fog-aliyun (~> 0.1.0)
|
||||
fog-aws (~> 1.4)
|
||||
fog-core (~> 1.44)
|
||||
fog-google (~> 0.5)
|
||||
fog-local (~> 0.3)
|
||||
|
@ -916,24 +991,26 @@ DEPENDENCIES
|
|||
fog-rackspace (~> 0.1.1)
|
||||
font-awesome-rails (~> 4.7)
|
||||
foreman (~> 0.78.0)
|
||||
fuubar (~> 2.0.0)
|
||||
fuubar (~> 2.2.0)
|
||||
gemnasium-gitlab-service (~> 0.2)
|
||||
gemojione (~> 3.0)
|
||||
gemojione (~> 3.3)
|
||||
gettext (~> 3.2.2)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.2.0)
|
||||
gitaly (~> 0.6.0)
|
||||
gitaly (~> 0.26.0)
|
||||
github-linguist (~> 4.7.0)
|
||||
gitlab-flowdock-git-hook (~> 1.0.1)
|
||||
gitlab-markup (~> 1.5.1)
|
||||
gitlab_omniauth-ldap (~> 1.2.1)
|
||||
gitlab_omniauth-ldap (~> 2.0.3)
|
||||
gollum-lib (~> 4.2)
|
||||
gollum-rugged_adapter (~> 0.4.4)
|
||||
gon (~> 6.1.0)
|
||||
google-api-client (~> 0.8.6)
|
||||
grape (~> 0.19.0)
|
||||
gpgme
|
||||
grape (~> 0.19.2)
|
||||
grape-entity (~> 0.6.0)
|
||||
haml_lint (~> 0.21.0)
|
||||
grape-route-helpers (~> 2.0.0)
|
||||
haml_lint (~> 0.26.0)
|
||||
hamlit (~> 2.6.1)
|
||||
hashie-forbidden_attributes
|
||||
health_check (~> 2.6.0)
|
||||
|
@ -947,28 +1024,30 @@ DEPENDENCIES
|
|||
jquery-rails (~> 4.1.0)
|
||||
json-schema (~> 2.6.2)
|
||||
jwt (~> 1.5.6)
|
||||
kaminari (~> 0.17.0)
|
||||
kaminari (~> 1.0)
|
||||
knapsack (~> 1.11.0)
|
||||
kubeclient (~> 2.2.0)
|
||||
letter_opener_web (~> 1.3.0)
|
||||
license_finder (~> 2.1.0)
|
||||
licensee (~> 8.7.0)
|
||||
lograge (~> 0.5)
|
||||
loofah (~> 2.0.3)
|
||||
mail_room (~> 0.9.1)
|
||||
method_source (~> 0.8)
|
||||
minitest (~> 5.7.0)
|
||||
mousetrap-rails (~> 1.4.6)
|
||||
mysql2 (~> 0.3.16)
|
||||
net-ssh (~> 3.0.1)
|
||||
nokogiri (~> 1.6.7, >= 1.6.7.2)
|
||||
oauth2 (~> 1.3.0)
|
||||
mysql2 (~> 0.4.5)
|
||||
net-ldap
|
||||
net-ssh (~> 4.1.0)
|
||||
nokogiri (~> 1.8.0)
|
||||
oauth2 (~> 1.4)
|
||||
octokit (~> 4.6.2)
|
||||
oj (~> 2.17.4)
|
||||
omniauth (~> 1.4.2)
|
||||
omniauth-auth0 (~> 1.4.1)
|
||||
omniauth-authentiq (~> 0.3.0)
|
||||
omniauth-authentiq (~> 0.3.1)
|
||||
omniauth-azure-oauth2 (~> 0.0.6)
|
||||
omniauth-cas3 (~> 1.1.2)
|
||||
omniauth-cas3 (~> 1.1.4)
|
||||
omniauth-facebook (~> 4.0.0)
|
||||
omniauth-github (~> 1.1.1)
|
||||
omniauth-gitlab (~> 1.0.2)
|
||||
|
@ -980,10 +1059,20 @@ DEPENDENCIES
|
|||
omniauth-twitter (~> 1.2.0)
|
||||
omniauth_crowd (~> 2.2.0)
|
||||
org-ruby (~> 0.9.12)
|
||||
paranoia (~> 2.2)
|
||||
paranoia (~> 2.3.1)
|
||||
peek (~> 1.0.1)
|
||||
peek-gc (~> 0.0.2)
|
||||
peek-host (~> 1.0.0)
|
||||
peek-mysql2 (~> 1.1.0)
|
||||
peek-performance_bar (~> 1.3.0)
|
||||
peek-pg (~> 1.3.0)
|
||||
peek-rblineprof (~> 0.2.0)
|
||||
peek-redis (~> 1.2.0)
|
||||
peek-sidekiq (~> 1.0.3)
|
||||
pg (~> 0.18.2)
|
||||
poltergeist (~> 1.9.0)
|
||||
premailer-rails (~> 1.9.0)
|
||||
premailer-rails (~> 1.9.7)
|
||||
prometheus-client-mmap (~> 0.7.0.beta14)
|
||||
pry-byebug (~> 3.4.1)
|
||||
pry-rails (~> 0.3.4)
|
||||
rack-attack (~> 4.4.1)
|
||||
|
@ -992,10 +1081,14 @@ DEPENDENCIES
|
|||
rack-proxy (~> 0.6.0)
|
||||
rails (= 4.2.8)
|
||||
rails-deprecated_sanitizer (~> 1.0.3)
|
||||
rainbow (~> 2.1.0)
|
||||
rails-i18n (~> 4.0.9)
|
||||
rainbow (~> 2.2)
|
||||
raindrops (~> 0.18)
|
||||
rblineprof (~> 0.3.6)
|
||||
rbnacl (~> 3.2)
|
||||
rbnacl-libsodium
|
||||
rdoc (~> 4.2)
|
||||
re2 (~> 1.0.0)
|
||||
re2 (~> 1.1.1)
|
||||
recaptcha (~> 3.0)
|
||||
redcarpet (~> 3.4)
|
||||
redis (~> 3.2)
|
||||
|
@ -1005,28 +1098,28 @@ DEPENDENCIES
|
|||
responders (~> 2.0)
|
||||
rouge (~> 2.0)
|
||||
rqrcode-rails3 (~> 0.1.7)
|
||||
rspec-rails (~> 3.5.0)
|
||||
rspec-rails (~> 3.6.0)
|
||||
rspec-retry (~> 0.4.5)
|
||||
rspec-set (~> 0.1.3)
|
||||
rspec_profiling (~> 0.0.5)
|
||||
rubocop (~> 0.47.1)
|
||||
rubocop-rspec (~> 1.15.0)
|
||||
rubocop (~> 0.49.1)
|
||||
rubocop-rspec (~> 1.15.1)
|
||||
ruby-fogbugz (~> 0.2.1)
|
||||
ruby-prof (~> 0.16.2)
|
||||
ruby_parser (~> 3.8.4)
|
||||
rufus-scheduler (~> 3.1.10)
|
||||
rugged (~> 0.25.1.1)
|
||||
ruby_parser (~> 3.8)
|
||||
rufus-scheduler (~> 3.4)
|
||||
rugged (~> 0.26.0)
|
||||
sanitize (~> 2.0)
|
||||
sass-rails (~> 5.0.6)
|
||||
scss_lint (~> 0.47.0)
|
||||
scss_lint (~> 0.54.0)
|
||||
seed-fu (~> 2.3.5)
|
||||
select2-rails (~> 3.5.9)
|
||||
sentry-raven (~> 2.4.0)
|
||||
sentry-raven (~> 2.5.3)
|
||||
settingslogic (~> 2.0.9)
|
||||
sham_rack (~> 1.3.6)
|
||||
shoulda-matchers (~> 2.8.0)
|
||||
shoulda-matchers (~> 3.1.2)
|
||||
sidekiq (~> 5.0)
|
||||
sidekiq-cron (~> 0.4.4)
|
||||
sidekiq-cron (~> 0.6.0)
|
||||
sidekiq-limit_fetch (~> 3.4)
|
||||
simplecov (~> 0.14.0)
|
||||
slack-notifier (~> 1.5.1)
|
||||
|
@ -1043,7 +1136,7 @@ DEPENDENCIES
|
|||
thin (~> 1.7.0)
|
||||
timecop (~> 0.8.0)
|
||||
toml-rb (~> 0.3.15)
|
||||
truncato (~> 0.7.8)
|
||||
truncato (~> 0.7.9)
|
||||
u2f (~> 0.2.1)
|
||||
uglifier (~> 2.7.2)
|
||||
underscore-rails (~> 1.8.0)
|
||||
|
@ -1054,9 +1147,9 @@ DEPENDENCIES
|
|||
version_sorter (~> 2.1.0)
|
||||
virtus (~> 1.0.1)
|
||||
vmstat (~> 2.3.0)
|
||||
webmock (~> 1.24.0)
|
||||
webmock (~> 2.3.2)
|
||||
webpack-rails (~> 0.9.10)
|
||||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.15.0
|
||||
1.15.3
|
||||
|
|
20
PROCESS.md
20
PROCESS.md
|
@ -128,7 +128,7 @@ information, see
|
|||
|
||||
### After the 7th
|
||||
|
||||
Once the stable branch is frozen, only fixes for regressions (bugs introduced in that same release)
|
||||
Once the stable branch is frozen, only fixes for [regressions](#regressions)
|
||||
and security issues will be cherry-picked into the stable branch.
|
||||
Any merge requests cherry-picked into the stable branch for a previous release will also be picked into the latest stable branch.
|
||||
These fixes will be shipped in the next RC for that release if it is before the 22nd.
|
||||
|
@ -158,6 +158,24 @@ release should have the correct milestone assigned _and_ have the label
|
|||
Merge requests without a milestone and this label will
|
||||
not be merged into any stable branches.
|
||||
|
||||
### Regressions
|
||||
|
||||
A regression for a particular monthly release is a bug that exists in that
|
||||
release, but wasn't present in the release before. This includes bugs in
|
||||
features that were only added in that monthly release. Every regression **must**
|
||||
have the milestone of the release it was introduced in - if a regression doesn't
|
||||
have a milestone, it might be 'just' a bug!
|
||||
|
||||
For instance, if 10.5.0 adds a feature, and that feature doesn't work correctly,
|
||||
then this is a regression in 10.5. If 10.5.1 then fixes that, but 10.5.3 somehow
|
||||
reintroduces the bug, then this bug is still a regression in 10.5.
|
||||
|
||||
Because GitLab.com runs release candidates of new releases, a regression can be
|
||||
reported in a release before its 'official' release date on the 22nd of the
|
||||
month. When we say 'the most recent monthly release', this can refer to either
|
||||
the version currently running on GitLab.com, or the most recent version
|
||||
available in the package repositories.
|
||||
|
||||
## Release retrospective and kickoff
|
||||
|
||||
### Retrospective
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
## Test coverage
|
||||
|
||||
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
|
||||
- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=rake+karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
|
||||
- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
|
||||
|
||||
## Canonical source
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
9.2.10
|
||||
9.5.4
|
||||
|
|
4
app/assets/images/i2p-step.svg
Normal file
4
app/assets/images/i2p-step.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 120" enable-background="new 0 0 12 120">
|
||||
<path d="m12 6c0-3.309-2.691-6-6-6s-6 2.691-6 6c0 2.967 2.167 5.431 5 5.91v108.09h2v-108.09c2.833-.479 5-2.943 5-5.91m-6 4c-2.206 0-4-1.794-4-4s1.794-4 4-4 4 1.794 4 4-1.794 4-4 4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 311 B |
BIN
app/assets/images/new_nav.png
Normal file
BIN
app/assets/images/new_nav.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
app/assets/images/new_repo.png
Normal file
BIN
app/assets/images/new_repo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
app/assets/images/old_nav.png
Normal file
BIN
app/assets/images/old_nav.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
app/assets/images/old_repo.png
Normal file
BIN
app/assets/images/old_repo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -5,7 +5,8 @@ import Cookies from 'js-cookie';
|
|||
|
||||
class Activities {
|
||||
constructor() {
|
||||
Pager.init(20, true, false, this.updateTooltips);
|
||||
Pager.init(20, true, false, data => data, this.updateTooltips);
|
||||
|
||||
$('.event-filter-link').on('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.toggleFilter(e.currentTarget);
|
||||
|
@ -19,7 +20,7 @@ class Activities {
|
|||
|
||||
reloadActivities() {
|
||||
$('.content_list').html('');
|
||||
Pager.init(20, true, false, this.updateTooltips);
|
||||
Pager.init(20, true, false, data => data, this.updateTooltips);
|
||||
}
|
||||
|
||||
toggleFilter(sender) {
|
||||
|
|
|
@ -10,7 +10,7 @@ class AjaxLoadingSpinner {
|
|||
e.target.setAttribute('disabled', '');
|
||||
const iconElement = e.target.querySelector('i');
|
||||
// get first fa- icon
|
||||
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g).first();
|
||||
const originalIcon = iconElement.className.match(/(fa-)([^\s]+)/g)[0];
|
||||
iconElement.dataset.icon = originalIcon;
|
||||
AjaxLoadingSpinner.toggleLoadingIcon(iconElement);
|
||||
$(e.target).off('ajax:beforeSend', AjaxLoadingSpinner.ajaxBeforeSend);
|
||||
|
|
|
@ -1,148 +1,190 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, quotes, object-shorthand, camelcase, no-var, comma-dangle, prefer-arrow-callback, quote-props, no-param-reassign, max-len */
|
||||
import $ from 'jquery';
|
||||
|
||||
var Api = {
|
||||
groupsPath: "/api/:version/groups.json",
|
||||
groupPath: "/api/:version/groups/:id.json",
|
||||
namespacesPath: "/api/:version/namespaces.json",
|
||||
groupProjectsPath: "/api/:version/groups/:id/projects.json",
|
||||
projectsPath: "/api/:version/projects.json?simple=true",
|
||||
labelsPath: "/:namespace_path/:project_path/labels",
|
||||
licensePath: "/api/:version/templates/licenses/:key",
|
||||
gitignorePath: "/api/:version/templates/gitignores/:key",
|
||||
gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key",
|
||||
dockerfilePath: "/api/:version/templates/dockerfiles/:key",
|
||||
issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key",
|
||||
group: function(group_id, callback) {
|
||||
var url = Api.buildUrl(Api.groupPath)
|
||||
.replace(':id', group_id);
|
||||
const Api = {
|
||||
groupsPath: '/api/:version/groups.json',
|
||||
groupPath: '/api/:version/groups/:id.json',
|
||||
namespacesPath: '/api/:version/namespaces.json',
|
||||
groupProjectsPath: '/api/:version/groups/:id/projects.json',
|
||||
projectsPath: '/api/:version/projects.json?simple=true',
|
||||
labelsPath: '/:namespace_path/:project_path/labels',
|
||||
licensePath: '/api/:version/templates/licenses/:key',
|
||||
gitignorePath: '/api/:version/templates/gitignores/:key',
|
||||
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
|
||||
dockerfilePath: '/api/:version/templates/dockerfiles/:key',
|
||||
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
|
||||
usersPath: '/api/:version/users.json',
|
||||
commitPath: '/api/:version/projects/:id/repository/commits',
|
||||
|
||||
group(groupId, callback) {
|
||||
const url = Api.buildUrl(Api.groupPath)
|
||||
.replace(':id', groupId);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
dataType: "json"
|
||||
}).done(function(group) {
|
||||
return callback(group);
|
||||
});
|
||||
url,
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(group => callback(group));
|
||||
},
|
||||
|
||||
// Return groups list. Filtered by query
|
||||
groups: function(query, options, callback) {
|
||||
var url = Api.buildUrl(Api.groupsPath);
|
||||
groups(query, options, callback) {
|
||||
const url = Api.buildUrl(Api.groupsPath);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: $.extend({
|
||||
search: query,
|
||||
per_page: 20
|
||||
}, options),
|
||||
dataType: "json"
|
||||
}).done(function(groups) {
|
||||
return callback(groups);
|
||||
});
|
||||
},
|
||||
// Return namespaces list. Filtered by query
|
||||
namespaces: function(query, callback) {
|
||||
var url = Api.buildUrl(Api.namespacesPath);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: {
|
||||
search: query,
|
||||
per_page: 20
|
||||
},
|
||||
dataType: "json"
|
||||
}).done(function(namespaces) {
|
||||
return callback(namespaces);
|
||||
});
|
||||
},
|
||||
// Return projects list. Filtered by query
|
||||
projects: function(query, options, callback) {
|
||||
var url = Api.buildUrl(Api.projectsPath);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: $.extend({
|
||||
url,
|
||||
data: Object.assign({
|
||||
search: query,
|
||||
per_page: 20,
|
||||
membership: true
|
||||
}, options),
|
||||
dataType: "json"
|
||||
}).done(function(projects) {
|
||||
return callback(projects);
|
||||
});
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(groups => callback(groups));
|
||||
},
|
||||
newLabel: function(namespace_path, project_path, data, callback) {
|
||||
var url = Api.buildUrl(Api.labelsPath)
|
||||
.replace(':namespace_path', namespace_path)
|
||||
.replace(':project_path', project_path);
|
||||
|
||||
// Return namespaces list. Filtered by query
|
||||
namespaces(query, callback) {
|
||||
const url = Api.buildUrl(Api.namespacesPath);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
data: { 'label': data },
|
||||
dataType: "json"
|
||||
}).done(function(label) {
|
||||
return callback(label);
|
||||
}).error(function(message) {
|
||||
return callback(message.responseJSON);
|
||||
});
|
||||
},
|
||||
// Return group projects list. Filtered by query
|
||||
groupProjects: function(group_id, query, callback) {
|
||||
var url = Api.buildUrl(Api.groupProjectsPath)
|
||||
.replace(':id', group_id);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
url,
|
||||
data: {
|
||||
search: query,
|
||||
per_page: 20
|
||||
per_page: 20,
|
||||
},
|
||||
dataType: "json"
|
||||
}).done(function(projects) {
|
||||
return callback(projects);
|
||||
});
|
||||
dataType: 'json',
|
||||
}).done(namespaces => callback(namespaces));
|
||||
},
|
||||
|
||||
// Return projects list. Filtered by query
|
||||
projects(query, options, callback) {
|
||||
const url = Api.buildUrl(Api.projectsPath);
|
||||
return $.ajax({
|
||||
url,
|
||||
data: Object.assign({
|
||||
search: query,
|
||||
per_page: 20,
|
||||
membership: true,
|
||||
}, options),
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(projects => callback(projects));
|
||||
},
|
||||
|
||||
newLabel(namespacePath, projectPath, data, callback) {
|
||||
const url = Api.buildUrl(Api.labelsPath)
|
||||
.replace(':namespace_path', namespacePath)
|
||||
.replace(':project_path', projectPath);
|
||||
return $.ajax({
|
||||
url,
|
||||
type: 'POST',
|
||||
data: { label: data },
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(label => callback(label))
|
||||
.fail(message => callback(message.responseJSON));
|
||||
},
|
||||
|
||||
// Return group projects list. Filtered by query
|
||||
groupProjects(groupId, query, callback) {
|
||||
const url = Api.buildUrl(Api.groupProjectsPath)
|
||||
.replace(':id', groupId);
|
||||
return $.ajax({
|
||||
url,
|
||||
data: {
|
||||
search: query,
|
||||
per_page: 20,
|
||||
},
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(projects => callback(projects));
|
||||
},
|
||||
|
||||
commitMultiple(id, data, callback) {
|
||||
const url = Api.buildUrl(Api.commitPath)
|
||||
.replace(':id', id);
|
||||
return $.ajax({
|
||||
url,
|
||||
type: 'POST',
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
data: JSON.stringify(data),
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(commitData => callback(commitData))
|
||||
.fail(message => callback(message.responseJSON));
|
||||
},
|
||||
|
||||
// Return text for a specific license
|
||||
licenseText: function(key, data, callback) {
|
||||
var url = Api.buildUrl(Api.licensePath)
|
||||
licenseText(key, data, callback) {
|
||||
const url = Api.buildUrl(Api.licensePath)
|
||||
.replace(':key', key);
|
||||
return $.ajax({
|
||||
url: url,
|
||||
data: data
|
||||
}).done(function(license) {
|
||||
return callback(license);
|
||||
});
|
||||
url,
|
||||
data,
|
||||
})
|
||||
.done(license => callback(license));
|
||||
},
|
||||
gitignoreText: function(key, callback) {
|
||||
var url = Api.buildUrl(Api.gitignorePath)
|
||||
|
||||
gitignoreText(key, callback) {
|
||||
const url = Api.buildUrl(Api.gitignorePath)
|
||||
.replace(':key', key);
|
||||
return $.get(url, function(gitignore) {
|
||||
return callback(gitignore);
|
||||
});
|
||||
return $.get(url, gitignore => callback(gitignore));
|
||||
},
|
||||
gitlabCiYml: function(key, callback) {
|
||||
var url = Api.buildUrl(Api.gitlabCiYmlPath)
|
||||
|
||||
gitlabCiYml(key, callback) {
|
||||
const url = Api.buildUrl(Api.gitlabCiYmlPath)
|
||||
.replace(':key', key);
|
||||
return $.get(url, function(file) {
|
||||
return callback(file);
|
||||
});
|
||||
return $.get(url, file => callback(file));
|
||||
},
|
||||
dockerfileYml: function(key, callback) {
|
||||
var url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
|
||||
|
||||
dockerfileYml(key, callback) {
|
||||
const url = Api.buildUrl(Api.dockerfilePath).replace(':key', key);
|
||||
$.get(url, callback);
|
||||
},
|
||||
issueTemplate: function(namespacePath, projectPath, key, type, callback) {
|
||||
var url = Api.buildUrl(Api.issuableTemplatePath)
|
||||
|
||||
issueTemplate(namespacePath, projectPath, key, type, callback) {
|
||||
const url = Api.buildUrl(Api.issuableTemplatePath)
|
||||
.replace(':key', key)
|
||||
.replace(':type', type)
|
||||
.replace(':project_path', projectPath)
|
||||
.replace(':namespace_path', namespacePath);
|
||||
$.ajax({
|
||||
url: url,
|
||||
dataType: 'json'
|
||||
}).done(function(file) {
|
||||
callback(null, file);
|
||||
}).error(callback);
|
||||
url,
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(file => callback(null, file))
|
||||
.fail(callback);
|
||||
},
|
||||
buildUrl: function(url) {
|
||||
|
||||
users(query, options) {
|
||||
const url = Api.buildUrl(this.usersPath);
|
||||
return Api.wrapAjaxCall({
|
||||
url,
|
||||
data: Object.assign({
|
||||
search: query,
|
||||
per_page: 20,
|
||||
}, options),
|
||||
dataType: 'json',
|
||||
});
|
||||
},
|
||||
|
||||
buildUrl(url) {
|
||||
let urlRoot = '';
|
||||
if (gon.relative_url_root != null) {
|
||||
url = gon.relative_url_root + url;
|
||||
urlRoot = gon.relative_url_root;
|
||||
}
|
||||
return url.replace(':version', gon.api_version);
|
||||
}
|
||||
return urlRoot + url.replace(':version', gon.api_version);
|
||||
},
|
||||
|
||||
wrapAjaxCall(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// jQuery 2 is not Promises/A+ compatible (missing catch)
|
||||
$.ajax(options) // eslint-disable-line promise/catch-or-return
|
||||
.then(data => resolve(data),
|
||||
(jqXHR, textStatus, errorThrown) => {
|
||||
const error = new Error(`${options.url}: ${errorThrown}`);
|
||||
error.textStatus = textStatus;
|
||||
reject(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
window.Api = Api;
|
||||
export default Api;
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
/* global Flash */
|
||||
|
||||
import _ from 'underscore';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
import emojiMap from 'emojis/digests.json';
|
||||
import emojiAliases from 'emojis/aliases.json';
|
||||
import { glEmojiTag } from './behaviors/gl_emoji';
|
||||
import isEmojiNameValid from './behaviors/gl_emoji/is_emoji_name_valid';
|
||||
|
||||
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
|
||||
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
|
||||
const requestAnimationFrame = window.requestAnimationFrame ||
|
||||
|
@ -16,8 +12,6 @@ const requestAnimationFrame = window.requestAnimationFrame ||
|
|||
|
||||
const FROM_SENTENCE_REGEX = /(?:, and | and |, )/; // For separating lists produced by ruby's Array#toSentence
|
||||
|
||||
let categoryMap = null;
|
||||
|
||||
const categoryLabelMap = {
|
||||
activity: 'Activity',
|
||||
people: 'People',
|
||||
|
@ -29,186 +23,144 @@ const categoryLabelMap = {
|
|||
flags: 'Flags',
|
||||
};
|
||||
|
||||
function buildCategoryMap() {
|
||||
return Object.keys(emojiMap).reduce((currentCategoryMap, emojiNameKey) => {
|
||||
const emojiInfo = emojiMap[emojiNameKey];
|
||||
if (currentCategoryMap[emojiInfo.category]) {
|
||||
currentCategoryMap[emojiInfo.category].push(emojiNameKey);
|
||||
class AwardsHandler {
|
||||
constructor(emoji) {
|
||||
this.emoji = emoji;
|
||||
this.eventListeners = [];
|
||||
// If the user shows intent let's pre-build the menu
|
||||
this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
|
||||
const $menu = $('.emoji-menu');
|
||||
if ($menu.length === 0) {
|
||||
requestAnimationFrame(() => {
|
||||
this.createEmojiMenu();
|
||||
});
|
||||
}
|
||||
});
|
||||
this.registerEventListener('on', $(document), 'click', '.js-add-award', (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.showEmojiMenu($(e.currentTarget));
|
||||
});
|
||||
|
||||
this.registerEventListener('on', $('html'), 'click', (e) => {
|
||||
const $target = $(e.target);
|
||||
if (!$target.closest('.emoji-menu-content').length) {
|
||||
$('.js-awards-block.current').removeClass('current');
|
||||
}
|
||||
if (!$target.closest('.emoji-menu').length) {
|
||||
if ($('.emoji-menu').is(':visible')) {
|
||||
$('.js-add-award.is-active').removeClass('is-active');
|
||||
$('.emoji-menu').removeClass('is-visible');
|
||||
}
|
||||
}
|
||||
});
|
||||
this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', (e) => {
|
||||
e.preventDefault();
|
||||
const $target = $(e.currentTarget);
|
||||
const $glEmojiElement = $target.find('gl-emoji');
|
||||
const $spriteIconElement = $target.find('.icon');
|
||||
const emojiName = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
|
||||
|
||||
$target.closest('.js-awards-block').addClass('current');
|
||||
this.addAward(this.getVotesBlock(), this.getAwardUrl(), emojiName);
|
||||
});
|
||||
}
|
||||
|
||||
registerEventListener(method = 'on', element, ...args) {
|
||||
element[method].call(element, ...args);
|
||||
this.eventListeners.push({
|
||||
element,
|
||||
args,
|
||||
});
|
||||
}
|
||||
|
||||
showEmojiMenu($addBtn) {
|
||||
if ($addBtn.hasClass('js-note-emoji')) {
|
||||
$addBtn.closest('.note').find('.js-awards-block').addClass('current');
|
||||
} else {
|
||||
$addBtn.closest('.js-awards-block').addClass('current');
|
||||
}
|
||||
|
||||
return currentCategoryMap;
|
||||
}, {
|
||||
activity: [],
|
||||
people: [],
|
||||
nature: [],
|
||||
food: [],
|
||||
travel: [],
|
||||
objects: [],
|
||||
symbols: [],
|
||||
flags: [],
|
||||
});
|
||||
}
|
||||
|
||||
function renderCategory(name, emojiList, opts = {}) {
|
||||
return `
|
||||
<h5 class="emoji-menu-title">
|
||||
${name}
|
||||
</h5>
|
||||
<ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
|
||||
${emojiList.map(emojiName => `
|
||||
<li class="emoji-menu-list-item">
|
||||
<button class="emoji-menu-btn text-center js-emoji-btn" type="button">
|
||||
${glEmojiTag(emojiName, {
|
||||
sprite: true,
|
||||
})}
|
||||
</button>
|
||||
</li>
|
||||
`).join('\n')}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
function AwardsHandler() {
|
||||
this.eventListeners = [];
|
||||
this.aliases = emojiAliases;
|
||||
// If the user shows intent let's pre-build the menu
|
||||
this.registerEventListener('one', $(document), 'mouseenter focus', '.js-add-award', 'mouseenter focus', () => {
|
||||
const $menu = $('.emoji-menu');
|
||||
if ($menu.length === 0) {
|
||||
requestAnimationFrame(() => {
|
||||
this.createEmojiMenu();
|
||||
const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent();
|
||||
const $userAuthored = this.isUserAuthored($addBtn);
|
||||
if ($menu.length) {
|
||||
if ($menu.is('.is-visible')) {
|
||||
$addBtn.removeClass('is-active');
|
||||
$menu.removeClass('is-visible');
|
||||
$('.js-emoji-menu-search').blur();
|
||||
} else {
|
||||
$addBtn.addClass('is-active');
|
||||
this.positionMenu($menu, $addBtn);
|
||||
$menu.addClass('is-visible');
|
||||
$('.js-emoji-menu-search').focus();
|
||||
}
|
||||
} else {
|
||||
$addBtn.addClass('is-loading is-active');
|
||||
this.createEmojiMenu(() => {
|
||||
const $createdMenu = $('.emoji-menu');
|
||||
$addBtn.removeClass('is-loading');
|
||||
this.positionMenu($createdMenu, $addBtn);
|
||||
return setTimeout(() => {
|
||||
$createdMenu.addClass('is-visible');
|
||||
$('.js-emoji-menu-search').focus();
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
// Prebuild the categoryMap
|
||||
categoryMap = categoryMap || buildCategoryMap();
|
||||
});
|
||||
this.registerEventListener('on', $(document), 'click', '.js-add-award', (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.showEmojiMenu($(e.currentTarget));
|
||||
});
|
||||
|
||||
this.registerEventListener('on', $('html'), 'click', (e) => {
|
||||
const $target = $(e.target);
|
||||
if (!$target.closest('.emoji-menu-content').length) {
|
||||
$('.js-awards-block.current').removeClass('current');
|
||||
$thumbsBtn.toggleClass('disabled', $userAuthored);
|
||||
}
|
||||
|
||||
// Create the emoji menu with the first category of emojis.
|
||||
// Then render the remaining categories of emojis one by one to avoid jank.
|
||||
createEmojiMenu(callback) {
|
||||
if (this.isCreatingEmojiMenu) {
|
||||
return;
|
||||
}
|
||||
if (!$target.closest('.emoji-menu').length) {
|
||||
if ($('.emoji-menu').is(':visible')) {
|
||||
$('.js-add-award.is-active').removeClass('is-active');
|
||||
$('.emoji-menu').removeClass('is-visible');
|
||||
}
|
||||
this.isCreatingEmojiMenu = true;
|
||||
|
||||
// Render the first category
|
||||
const categoryMap = this.emoji.getEmojiCategoryMap();
|
||||
const categoryNameKey = Object.keys(categoryMap)[0];
|
||||
const emojisInCategory = categoryMap[categoryNameKey];
|
||||
const firstCategory = this.renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
|
||||
|
||||
// Render the frequently used
|
||||
const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
|
||||
let frequentlyUsedCatgegory = '';
|
||||
if (frequentlyUsedEmojis.length > 0) {
|
||||
frequentlyUsedCatgegory = this.renderCategory('Frequently used', frequentlyUsedEmojis, {
|
||||
menuListClass: 'frequent-emojis',
|
||||
});
|
||||
}
|
||||
});
|
||||
this.registerEventListener('on', $(document), 'click', '.js-emoji-btn', (e) => {
|
||||
e.preventDefault();
|
||||
const $target = $(e.currentTarget);
|
||||
const $glEmojiElement = $target.find('gl-emoji');
|
||||
const $spriteIconElement = $target.find('.icon');
|
||||
const emoji = ($glEmojiElement.length ? $glEmojiElement : $spriteIconElement).data('name');
|
||||
|
||||
$target.closest('.js-awards-block').addClass('current');
|
||||
this.addAward(this.getVotesBlock(), this.getAwardUrl(), emoji);
|
||||
});
|
||||
}
|
||||
const emojiMenuMarkup = `
|
||||
<div class="emoji-menu">
|
||||
<input type="text" name="emoji-menu-search" value="" class="js-emoji-menu-search emoji-search search-input form-control" placeholder="Search emoji" />
|
||||
|
||||
AwardsHandler.prototype.registerEventListener = function registerEventListener(method = 'on', element, ...args) {
|
||||
element[method].call(element, ...args);
|
||||
this.eventListeners.push({
|
||||
element,
|
||||
args,
|
||||
});
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.showEmojiMenu = function showEmojiMenu($addBtn) {
|
||||
if ($addBtn.hasClass('js-note-emoji')) {
|
||||
$addBtn.closest('.note').find('.js-awards-block').addClass('current');
|
||||
} else {
|
||||
$addBtn.closest('.js-awards-block').addClass('current');
|
||||
}
|
||||
|
||||
const $menu = $('.emoji-menu');
|
||||
const $thumbsBtn = $menu.find('[data-name="thumbsup"], [data-name="thumbsdown"]').parent();
|
||||
const $userAuthored = this.isUserAuthored($addBtn);
|
||||
if ($menu.length) {
|
||||
if ($menu.is('.is-visible')) {
|
||||
$addBtn.removeClass('is-active');
|
||||
$menu.removeClass('is-visible');
|
||||
$('.js-emoji-menu-search').blur();
|
||||
} else {
|
||||
$addBtn.addClass('is-active');
|
||||
this.positionMenu($menu, $addBtn);
|
||||
$menu.addClass('is-visible');
|
||||
$('.js-emoji-menu-search').focus();
|
||||
}
|
||||
} else {
|
||||
$addBtn.addClass('is-loading is-active');
|
||||
this.createEmojiMenu(() => {
|
||||
const $createdMenu = $('.emoji-menu');
|
||||
$addBtn.removeClass('is-loading');
|
||||
this.positionMenu($createdMenu, $addBtn);
|
||||
return setTimeout(() => {
|
||||
$createdMenu.addClass('is-visible');
|
||||
$('.js-emoji-menu-search').focus();
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
$thumbsBtn.toggleClass('disabled', $userAuthored);
|
||||
};
|
||||
|
||||
// Create the emoji menu with the first category of emojis.
|
||||
// Then render the remaining categories of emojis one by one to avoid jank.
|
||||
AwardsHandler.prototype.createEmojiMenu = function createEmojiMenu(callback) {
|
||||
if (this.isCreatingEmojiMenu) {
|
||||
return;
|
||||
}
|
||||
this.isCreatingEmojiMenu = true;
|
||||
|
||||
// Render the first category
|
||||
categoryMap = categoryMap || buildCategoryMap();
|
||||
const categoryNameKey = Object.keys(categoryMap)[0];
|
||||
const emojisInCategory = categoryMap[categoryNameKey];
|
||||
const firstCategory = renderCategory(categoryLabelMap[categoryNameKey], emojisInCategory);
|
||||
|
||||
// Render the frequently used
|
||||
const frequentlyUsedEmojis = this.getFrequentlyUsedEmojis();
|
||||
let frequentlyUsedCatgegory = '';
|
||||
if (frequentlyUsedEmojis.length > 0) {
|
||||
frequentlyUsedCatgegory = renderCategory('Frequently used', frequentlyUsedEmojis, {
|
||||
menuListClass: 'frequent-emojis',
|
||||
});
|
||||
}
|
||||
|
||||
const emojiMenuMarkup = `
|
||||
<div class="emoji-menu">
|
||||
<input type="text" name="emoji-menu-search" value="" class="js-emoji-menu-search emoji-search search-input form-control" placeholder="Search emoji" />
|
||||
|
||||
<div class="emoji-menu-content">
|
||||
${frequentlyUsedCatgegory}
|
||||
${firstCategory}
|
||||
<div class="emoji-menu-content">
|
||||
${frequentlyUsedCatgegory}
|
||||
${firstCategory}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup);
|
||||
document.body.insertAdjacentHTML('beforeend', emojiMenuMarkup);
|
||||
|
||||
this.addRemainingEmojiMenuCategories();
|
||||
this.setupSearch();
|
||||
if (callback) {
|
||||
callback();
|
||||
this.addRemainingEmojiMenuCategories();
|
||||
this.setupSearch();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler
|
||||
.prototype
|
||||
.addRemainingEmojiMenuCategories = function addRemainingEmojiMenuCategories() {
|
||||
addRemainingEmojiMenuCategories() {
|
||||
if (this.isAddingRemainingEmojiMenuCategories) {
|
||||
return;
|
||||
}
|
||||
this.isAddingRemainingEmojiMenuCategories = true;
|
||||
|
||||
categoryMap = categoryMap || buildCategoryMap();
|
||||
const categoryMap = this.emoji.getEmojiCategoryMap();
|
||||
|
||||
// Avoid the jank and render the remaining categories separately
|
||||
// This will take more time, but makes UI more responsive
|
||||
|
@ -220,7 +172,7 @@ AwardsHandler
|
|||
promiseChain.then(() =>
|
||||
new Promise((resolve) => {
|
||||
const emojisInCategory = categoryMap[categoryNameKey];
|
||||
const categoryMarkup = renderCategory(
|
||||
const categoryMarkup = this.renderCategory(
|
||||
categoryLabelMap[categoryNameKey],
|
||||
emojisInCategory,
|
||||
);
|
||||
|
@ -243,179 +195,186 @@ AwardsHandler
|
|||
emojiContentElement.insertAdjacentHTML('beforeend', '<p>We encountered an error while adding the remaining categories</p>');
|
||||
throw new Error(`Error occurred in addRemainingEmojiMenuCategories: ${err.message}`);
|
||||
});
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.positionMenu = function positionMenu($menu, $addBtn) {
|
||||
const position = $addBtn.data('position');
|
||||
// The menu could potentially be off-screen or in a hidden overflow element
|
||||
// So we position the element absolute in the body
|
||||
const css = {
|
||||
top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`,
|
||||
};
|
||||
if (position === 'right') {
|
||||
css.left = `${($addBtn.offset().left - $menu.outerWidth()) + 20}px`;
|
||||
$menu.addClass('is-aligned-right');
|
||||
} else {
|
||||
css.left = `${$addBtn.offset().left}px`;
|
||||
$menu.removeClass('is-aligned-right');
|
||||
}
|
||||
return $menu.css(css);
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.addAward = function addAward(
|
||||
votesBlock,
|
||||
awardUrl,
|
||||
emoji,
|
||||
checkMutuality,
|
||||
callback,
|
||||
) {
|
||||
const normalizedEmoji = this.normalizeEmojiName(emoji);
|
||||
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
|
||||
this.postEmoji($emojiButton, awardUrl, normalizedEmoji, () => {
|
||||
this.addAwardToEmojiBar(votesBlock, normalizedEmoji, checkMutuality);
|
||||
return typeof callback === 'function' ? callback() : undefined;
|
||||
});
|
||||
$('.emoji-menu').removeClass('is-visible');
|
||||
$('.js-add-award.is-active').removeClass('is-active');
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.addAwardToEmojiBar = function addAwardToEmojiBar(
|
||||
votesBlock,
|
||||
emoji,
|
||||
checkForMutuality,
|
||||
) {
|
||||
if (checkForMutuality || checkForMutuality === null) {
|
||||
this.checkMutuality(votesBlock, emoji);
|
||||
renderCategory(name, emojiList, opts = {}) {
|
||||
return `
|
||||
<h5 class="emoji-menu-title">
|
||||
${name}
|
||||
</h5>
|
||||
<ul class="clearfix emoji-menu-list ${opts.menuListClass || ''}">
|
||||
${emojiList.map(emojiName => `
|
||||
<li class="emoji-menu-list-item">
|
||||
<button class="emoji-menu-btn text-center js-emoji-btn" type="button">
|
||||
${this.emoji.glEmojiTag(emojiName, {
|
||||
sprite: true,
|
||||
})}
|
||||
</button>
|
||||
</li>
|
||||
`).join('\n')}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
this.addEmojiToFrequentlyUsedList(emoji);
|
||||
const normalizedEmoji = this.normalizeEmojiName(emoji);
|
||||
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
|
||||
if ($emojiButton.length > 0) {
|
||||
if (this.isActive($emojiButton)) {
|
||||
this.decrementCounter($emojiButton, normalizedEmoji);
|
||||
|
||||
positionMenu($menu, $addBtn) {
|
||||
const position = $addBtn.data('position');
|
||||
// The menu could potentially be off-screen or in a hidden overflow element
|
||||
// So we position the element absolute in the body
|
||||
const css = {
|
||||
top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`,
|
||||
};
|
||||
if (position === 'right') {
|
||||
css.left = `${($addBtn.offset().left - $menu.outerWidth()) + 20}px`;
|
||||
$menu.addClass('is-aligned-right');
|
||||
} else {
|
||||
const counter = $emojiButton.find('.js-counter');
|
||||
counter.text(parseInt(counter.text(), 10) + 1);
|
||||
$emojiButton.addClass('active');
|
||||
this.addYouToUserList(votesBlock, normalizedEmoji);
|
||||
this.animateEmoji($emojiButton);
|
||||
css.left = `${$addBtn.offset().left}px`;
|
||||
$menu.removeClass('is-aligned-right');
|
||||
}
|
||||
} else {
|
||||
votesBlock.removeClass('hidden');
|
||||
this.createEmoji(votesBlock, normalizedEmoji);
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.getVotesBlock = function getVotesBlock() {
|
||||
const currentBlock = $('.js-awards-block.current');
|
||||
let resultantVotesBlock = currentBlock;
|
||||
if (currentBlock.length === 0) {
|
||||
resultantVotesBlock = $('.js-awards-block').eq(0);
|
||||
return $menu.css(css);
|
||||
}
|
||||
|
||||
return resultantVotesBlock;
|
||||
};
|
||||
addAward(votesBlock, awardUrl, emoji, checkMutuality, callback) {
|
||||
const normalizedEmoji = this.emoji.normalizeEmojiName(emoji);
|
||||
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
|
||||
this.postEmoji($emojiButton, awardUrl, normalizedEmoji, () => {
|
||||
this.addAwardToEmojiBar(votesBlock, normalizedEmoji, checkMutuality);
|
||||
return typeof callback === 'function' ? callback() : undefined;
|
||||
});
|
||||
$('.emoji-menu').removeClass('is-visible');
|
||||
$('.js-add-award.is-active').removeClass('is-active');
|
||||
}
|
||||
|
||||
AwardsHandler.prototype.getAwardUrl = function getAwardUrl() {
|
||||
return this.getVotesBlock().data('award-url');
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.checkMutuality = function checkMutuality(votesBlock, emoji) {
|
||||
const awardUrl = this.getAwardUrl();
|
||||
if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
|
||||
const mutualVote = emoji === 'thumbsup' ? 'thumbsdown' : 'thumbsup';
|
||||
const $emojiButton = votesBlock.find(`[data-name="${mutualVote}"]`).parent();
|
||||
const isAlreadyVoted = $emojiButton.hasClass('active');
|
||||
if (isAlreadyVoted) {
|
||||
this.addAward(votesBlock, awardUrl, mutualVote, false);
|
||||
addAwardToEmojiBar(votesBlock, emoji, checkForMutuality) {
|
||||
if (checkForMutuality || checkForMutuality === null) {
|
||||
this.checkMutuality(votesBlock, emoji);
|
||||
}
|
||||
this.addEmojiToFrequentlyUsedList(emoji);
|
||||
const normalizedEmoji = this.emoji.normalizeEmojiName(emoji);
|
||||
const $emojiButton = this.findEmojiIcon(votesBlock, normalizedEmoji).parent();
|
||||
if ($emojiButton.length > 0) {
|
||||
if (this.isActive($emojiButton)) {
|
||||
this.decrementCounter($emojiButton, normalizedEmoji);
|
||||
} else {
|
||||
const counter = $emojiButton.find('.js-counter');
|
||||
counter.text(parseInt(counter.text(), 10) + 1);
|
||||
$emojiButton.addClass('active');
|
||||
this.addYouToUserList(votesBlock, normalizedEmoji);
|
||||
this.animateEmoji($emojiButton);
|
||||
}
|
||||
} else {
|
||||
votesBlock.removeClass('hidden');
|
||||
this.createEmoji(votesBlock, normalizedEmoji);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.isActive = function isActive($emojiButton) {
|
||||
return $emojiButton.hasClass('active');
|
||||
};
|
||||
getVotesBlock() {
|
||||
const currentBlock = $('.js-awards-block.current');
|
||||
let resultantVotesBlock = currentBlock;
|
||||
if (currentBlock.length === 0) {
|
||||
resultantVotesBlock = $('.js-awards-block').eq(0);
|
||||
}
|
||||
|
||||
AwardsHandler.prototype.isUserAuthored = function isUserAuthored($button) {
|
||||
return $button.hasClass('js-user-authored');
|
||||
};
|
||||
return resultantVotesBlock;
|
||||
}
|
||||
|
||||
AwardsHandler.prototype.decrementCounter = function decrementCounter($emojiButton, emoji) {
|
||||
const counter = $('.js-counter', $emojiButton);
|
||||
const counterNumber = parseInt(counter.text(), 10);
|
||||
if (counterNumber > 1) {
|
||||
counter.text(counterNumber - 1);
|
||||
this.removeYouFromUserList($emojiButton);
|
||||
} else if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
|
||||
$emojiButton.tooltip('destroy');
|
||||
counter.text('0');
|
||||
this.removeYouFromUserList($emojiButton);
|
||||
if ($emojiButton.parents('.note').length) {
|
||||
getAwardUrl() {
|
||||
return this.getVotesBlock().data('award-url');
|
||||
}
|
||||
|
||||
checkMutuality(votesBlock, emoji) {
|
||||
const awardUrl = this.getAwardUrl();
|
||||
if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
|
||||
const mutualVote = emoji === 'thumbsup' ? 'thumbsdown' : 'thumbsup';
|
||||
const $emojiButton = votesBlock.find(`[data-name="${mutualVote}"]`).parent();
|
||||
const isAlreadyVoted = $emojiButton.hasClass('active');
|
||||
if (isAlreadyVoted) {
|
||||
this.addAward(votesBlock, awardUrl, mutualVote, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isActive($emojiButton) {
|
||||
return $emojiButton.hasClass('active');
|
||||
}
|
||||
|
||||
isUserAuthored($button) {
|
||||
return $button.hasClass('js-user-authored');
|
||||
}
|
||||
|
||||
decrementCounter($emojiButton, emoji) {
|
||||
const counter = $('.js-counter', $emojiButton);
|
||||
const counterNumber = parseInt(counter.text(), 10);
|
||||
if (counterNumber > 1) {
|
||||
counter.text(counterNumber - 1);
|
||||
this.removeYouFromUserList($emojiButton);
|
||||
} else if (emoji === 'thumbsup' || emoji === 'thumbsdown') {
|
||||
$emojiButton.tooltip('destroy');
|
||||
counter.text('0');
|
||||
this.removeYouFromUserList($emojiButton);
|
||||
if ($emojiButton.parents('.note').length) {
|
||||
this.removeEmoji($emojiButton);
|
||||
}
|
||||
} else {
|
||||
this.removeEmoji($emojiButton);
|
||||
}
|
||||
} else {
|
||||
this.removeEmoji($emojiButton);
|
||||
}
|
||||
return $emojiButton.removeClass('active');
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.removeEmoji = function removeEmoji($emojiButton) {
|
||||
$emojiButton.tooltip('destroy');
|
||||
$emojiButton.remove();
|
||||
const $votesBlock = this.getVotesBlock();
|
||||
if ($votesBlock.find('.js-emoji-btn').length === 0) {
|
||||
$votesBlock.addClass('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.getAwardTooltip = function getAwardTooltip($awardBlock) {
|
||||
return $awardBlock.attr('data-original-title') || $awardBlock.attr('data-title') || '';
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.toSentence = function toSentence(list) {
|
||||
let sentence;
|
||||
if (list.length <= 2) {
|
||||
sentence = list.join(' and ');
|
||||
} else {
|
||||
sentence = `${list.slice(0, -1).join(', ')}, and ${list[list.length - 1]}`;
|
||||
return $emojiButton.removeClass('active');
|
||||
}
|
||||
|
||||
return sentence;
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.removeYouFromUserList = function removeYouFromUserList($emojiButton) {
|
||||
const awardBlock = $emojiButton;
|
||||
const originalTitle = this.getAwardTooltip(awardBlock);
|
||||
const authors = originalTitle.split(FROM_SENTENCE_REGEX);
|
||||
authors.splice(authors.indexOf('You'), 1);
|
||||
return awardBlock
|
||||
.closest('.js-emoji-btn')
|
||||
.removeData('title')
|
||||
.removeAttr('data-title')
|
||||
.removeAttr('data-original-title')
|
||||
.attr('title', this.toSentence(authors))
|
||||
.tooltip('fixTitle');
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.addYouToUserList = function addYouToUserList(votesBlock, emoji) {
|
||||
const awardBlock = this.findEmojiIcon(votesBlock, emoji).parent();
|
||||
const origTitle = this.getAwardTooltip(awardBlock);
|
||||
let users = [];
|
||||
if (origTitle) {
|
||||
users = origTitle.trim().split(FROM_SENTENCE_REGEX);
|
||||
removeEmoji($emojiButton) {
|
||||
$emojiButton.tooltip('destroy');
|
||||
$emojiButton.remove();
|
||||
const $votesBlock = this.getVotesBlock();
|
||||
if ($votesBlock.find('.js-emoji-btn').length === 0) {
|
||||
$votesBlock.addClass('hidden');
|
||||
}
|
||||
}
|
||||
users.unshift('You');
|
||||
return awardBlock
|
||||
.attr('title', this.toSentence(users))
|
||||
.tooltip('fixTitle');
|
||||
};
|
||||
|
||||
AwardsHandler
|
||||
.prototype
|
||||
.createAwardButtonForVotesBlock = function createAwardButtonForVotesBlock(votesBlock, emojiName) {
|
||||
getAwardTooltip($awardBlock) {
|
||||
return $awardBlock.attr('data-original-title') || $awardBlock.attr('data-title') || '';
|
||||
}
|
||||
|
||||
toSentence(list) {
|
||||
let sentence;
|
||||
if (list.length <= 2) {
|
||||
sentence = list.join(' and ');
|
||||
} else {
|
||||
sentence = `${list.slice(0, -1).join(', ')}, and ${list[list.length - 1]}`;
|
||||
}
|
||||
|
||||
return sentence;
|
||||
}
|
||||
|
||||
removeYouFromUserList($emojiButton) {
|
||||
const awardBlock = $emojiButton;
|
||||
const originalTitle = this.getAwardTooltip(awardBlock);
|
||||
const authors = originalTitle.split(FROM_SENTENCE_REGEX);
|
||||
authors.splice(authors.indexOf('You'), 1);
|
||||
return awardBlock
|
||||
.closest('.js-emoji-btn')
|
||||
.removeData('title')
|
||||
.removeAttr('data-title')
|
||||
.removeAttr('data-original-title')
|
||||
.attr('title', this.toSentence(authors))
|
||||
.tooltip('fixTitle');
|
||||
}
|
||||
|
||||
addYouToUserList(votesBlock, emoji) {
|
||||
const awardBlock = this.findEmojiIcon(votesBlock, emoji).parent();
|
||||
const origTitle = this.getAwardTooltip(awardBlock);
|
||||
let users = [];
|
||||
if (origTitle) {
|
||||
users = origTitle.trim().split(FROM_SENTENCE_REGEX);
|
||||
}
|
||||
users.unshift('You');
|
||||
return awardBlock
|
||||
.attr('title', this.toSentence(users))
|
||||
.tooltip('fixTitle');
|
||||
}
|
||||
|
||||
createAwardButtonForVotesBlock(votesBlock, emojiName) {
|
||||
const buttonHtml = `
|
||||
<button class="btn award-control js-emoji-btn has-tooltip active" title="You" data-placement="bottom">
|
||||
${glEmojiTag(emojiName)}
|
||||
${this.emoji.glEmojiTag(emojiName)}
|
||||
<span class="award-control-text js-counter">1</span>
|
||||
</button>
|
||||
`;
|
||||
|
@ -424,144 +383,136 @@ AwardsHandler
|
|||
this.animateEmoji($emojiButton);
|
||||
$('.award-control').tooltip();
|
||||
votesBlock.removeClass('current');
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.animateEmoji = function animateEmoji($emoji) {
|
||||
const className = 'pulse animated once short';
|
||||
$emoji.addClass(className);
|
||||
|
||||
this.registerEventListener('on', $emoji, animationEndEventString, (e) => {
|
||||
$(e.currentTarget).removeClass(className);
|
||||
});
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.createEmoji = function createEmoji(votesBlock, emoji) {
|
||||
if ($('.emoji-menu').length) {
|
||||
this.createAwardButtonForVotesBlock(votesBlock, emoji);
|
||||
}
|
||||
this.createEmojiMenu(() => {
|
||||
this.createAwardButtonForVotesBlock(votesBlock, emoji);
|
||||
});
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.postEmoji = function postEmoji($emojiButton, awardUrl, emoji, callback) {
|
||||
if (this.isUserAuthored($emojiButton)) {
|
||||
this.userAuthored($emojiButton);
|
||||
} else {
|
||||
$.post(awardUrl, {
|
||||
name: emoji,
|
||||
}, (data) => {
|
||||
if (data.ok) {
|
||||
callback();
|
||||
}
|
||||
}).fail(() => new Flash('Something went wrong on our end.'));
|
||||
animateEmoji($emoji) {
|
||||
const className = 'pulse animated once short';
|
||||
$emoji.addClass(className);
|
||||
|
||||
this.registerEventListener('on', $emoji, animationEndEventString, (e) => {
|
||||
$(e.currentTarget).removeClass(className);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.findEmojiIcon = function findEmojiIcon(votesBlock, emoji) {
|
||||
return votesBlock.find(`.js-emoji-btn [data-name="${emoji}"]`);
|
||||
};
|
||||
createEmoji(votesBlock, emoji) {
|
||||
if ($('.emoji-menu').length) {
|
||||
this.createAwardButtonForVotesBlock(votesBlock, emoji);
|
||||
}
|
||||
this.createEmojiMenu(() => {
|
||||
this.createAwardButtonForVotesBlock(votesBlock, emoji);
|
||||
});
|
||||
}
|
||||
|
||||
AwardsHandler.prototype.userAuthored = function userAuthored($emojiButton) {
|
||||
const oldTitle = this.getAwardTooltip($emojiButton);
|
||||
const newTitle = 'You cannot vote on your own issue, MR and note';
|
||||
gl.utils.updateTooltipTitle($emojiButton, newTitle).tooltip('show');
|
||||
// Restore tooltip back to award list
|
||||
return setTimeout(() => {
|
||||
$emojiButton.tooltip('hide');
|
||||
gl.utils.updateTooltipTitle($emojiButton, oldTitle);
|
||||
}, 2800);
|
||||
};
|
||||
postEmoji($emojiButton, awardUrl, emoji, callback) {
|
||||
if (this.isUserAuthored($emojiButton)) {
|
||||
this.userAuthored($emojiButton);
|
||||
} else {
|
||||
$.post(awardUrl, {
|
||||
name: emoji,
|
||||
}, (data) => {
|
||||
if (data.ok) {
|
||||
callback();
|
||||
}
|
||||
}).fail(() => new Flash('Something went wrong on our end.'));
|
||||
}
|
||||
}
|
||||
|
||||
AwardsHandler.prototype.scrollToAwards = function scrollToAwards() {
|
||||
const options = {
|
||||
scrollTop: $('.awards').offset().top - 110,
|
||||
};
|
||||
return $('body, html').animate(options, 200);
|
||||
};
|
||||
findEmojiIcon(votesBlock, emoji) {
|
||||
return votesBlock.find(`.js-emoji-btn [data-name="${emoji}"]`);
|
||||
}
|
||||
|
||||
AwardsHandler.prototype.normalizeEmojiName = function normalizeEmojiName(emoji) {
|
||||
return Object.prototype.hasOwnProperty.call(this.aliases, emoji) ? this.aliases[emoji] : emoji;
|
||||
};
|
||||
userAuthored($emojiButton) {
|
||||
const oldTitle = this.getAwardTooltip($emojiButton);
|
||||
const newTitle = 'You cannot vote on your own issue, MR and note';
|
||||
gl.utils.updateTooltipTitle($emojiButton, newTitle).tooltip('show');
|
||||
// Restore tooltip back to award list
|
||||
return setTimeout(() => {
|
||||
$emojiButton.tooltip('hide');
|
||||
gl.utils.updateTooltipTitle($emojiButton, oldTitle);
|
||||
}, 2800);
|
||||
}
|
||||
|
||||
AwardsHandler
|
||||
.prototype
|
||||
.addEmojiToFrequentlyUsedList = function addEmojiToFrequentlyUsedList(emoji) {
|
||||
if (isEmojiNameValid(emoji)) {
|
||||
scrollToAwards() {
|
||||
const options = {
|
||||
scrollTop: $('.awards').offset().top - 110,
|
||||
};
|
||||
return $('body, html').animate(options, 200);
|
||||
}
|
||||
|
||||
addEmojiToFrequentlyUsedList(emoji) {
|
||||
if (this.emoji.isEmojiNameValid(emoji)) {
|
||||
this.frequentlyUsedEmojis = _.uniq(this.getFrequentlyUsedEmojis().concat(emoji));
|
||||
Cookies.set('frequently_used_emojis', this.frequentlyUsedEmojis.join(','), { expires: 365 });
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.getFrequentlyUsedEmojis = function getFrequentlyUsedEmojis() {
|
||||
return this.frequentlyUsedEmojis || (() => {
|
||||
const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
|
||||
this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
|
||||
inputName => isEmojiNameValid(inputName),
|
||||
);
|
||||
|
||||
return this.frequentlyUsedEmojis;
|
||||
})();
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.setupSearch = function setupSearch() {
|
||||
const $search = $('.js-emoji-menu-search');
|
||||
|
||||
this.registerEventListener('on', $search, 'input', (e) => {
|
||||
const term = $(e.target).val().trim();
|
||||
this.searchEmojis(term);
|
||||
});
|
||||
|
||||
const $menu = $('.emoji-menu');
|
||||
this.registerEventListener('on', $menu, transitionEndEventString, (e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
// Clear the search
|
||||
this.searchEmojis('');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.searchEmojis = function searchEmojis(term) {
|
||||
const $search = $('.js-emoji-menu-search');
|
||||
$search.val(term);
|
||||
|
||||
// Clean previous search results
|
||||
$('ul.emoji-menu-search, h5.emoji-search-title').remove();
|
||||
if (term.length > 0) {
|
||||
// Generate a search result block
|
||||
const h5 = $('<h5 class="emoji-search-title"/>').text('Search results');
|
||||
const foundEmojis = this.findMatchingEmojiElements(term).show();
|
||||
const ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis);
|
||||
$('.emoji-menu-content ul, .emoji-menu-content h5').hide();
|
||||
$('.emoji-menu-content').append(h5).append(ul);
|
||||
} else {
|
||||
$('.emoji-menu-content').children().show();
|
||||
}
|
||||
};
|
||||
|
||||
AwardsHandler.prototype.findMatchingEmojiElements = function findMatchingEmojiElements(term) {
|
||||
const safeTerm = term.toLowerCase();
|
||||
getFrequentlyUsedEmojis() {
|
||||
return this.frequentlyUsedEmojis || (() => {
|
||||
const frequentlyUsedEmojis = _.uniq((Cookies.get('frequently_used_emojis') || '').split(','));
|
||||
this.frequentlyUsedEmojis = frequentlyUsedEmojis.filter(
|
||||
inputName => this.emoji.isEmojiNameValid(inputName),
|
||||
);
|
||||
|
||||
const namesMatchingAlias = [];
|
||||
Object.keys(emojiAliases).forEach((alias) => {
|
||||
if (alias.indexOf(safeTerm) >= 0) {
|
||||
namesMatchingAlias.push(emojiAliases[alias]);
|
||||
return this.frequentlyUsedEmojis;
|
||||
})();
|
||||
}
|
||||
|
||||
setupSearch() {
|
||||
const $search = $('.js-emoji-menu-search');
|
||||
|
||||
this.registerEventListener('on', $search, 'input', (e) => {
|
||||
const term = $(e.target).val().trim();
|
||||
this.searchEmojis(term);
|
||||
});
|
||||
|
||||
const $menu = $('.emoji-menu');
|
||||
this.registerEventListener('on', $menu, transitionEndEventString, (e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
// Clear the search
|
||||
this.searchEmojis('');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
searchEmojis(term) {
|
||||
const $search = $('.js-emoji-menu-search');
|
||||
$search.val(term);
|
||||
|
||||
// Clean previous search results
|
||||
$('ul.emoji-menu-search, h5.emoji-search-title').remove();
|
||||
if (term.length > 0) {
|
||||
// Generate a search result block
|
||||
const h5 = $('<h5 class="emoji-search-title"/>').text('Search results');
|
||||
const foundEmojis = this.findMatchingEmojiElements(term).show();
|
||||
const ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(foundEmojis);
|
||||
$('.emoji-menu-content ul, .emoji-menu-content h5').hide();
|
||||
$('.emoji-menu-content').append(h5).append(ul);
|
||||
} else {
|
||||
$('.emoji-menu-content').children().show();
|
||||
}
|
||||
});
|
||||
const $matchingElements = namesMatchingAlias.concat(safeTerm)
|
||||
.reduce(
|
||||
($result, searchTerm) =>
|
||||
$result.add($(`.emoji-menu-list:not(.frequent-emojis) [data-name*="${searchTerm}"]`)),
|
||||
$([]),
|
||||
);
|
||||
return $matchingElements.closest('li').clone();
|
||||
};
|
||||
}
|
||||
|
||||
AwardsHandler.prototype.destroy = function destroy() {
|
||||
this.eventListeners.forEach((entry) => {
|
||||
entry.element.off.call(entry.element, ...entry.args);
|
||||
});
|
||||
$('.emoji-menu').remove();
|
||||
};
|
||||
findMatchingEmojiElements(query) {
|
||||
const emojiMatches = this.emoji.filterEmojiNamesByAlias(query);
|
||||
const $emojiElements = $('.emoji-menu-list:not(.frequent-emojis) [data-name]');
|
||||
const $matchingElements = $emojiElements
|
||||
.filter((i, elm) => emojiMatches.indexOf(elm.dataset.name) >= 0);
|
||||
return $matchingElements.closest('li').clone();
|
||||
}
|
||||
|
||||
export default AwardsHandler;
|
||||
destroy() {
|
||||
this.eventListeners.forEach((entry) => {
|
||||
entry.element.off.call(entry.element, ...entry.args);
|
||||
});
|
||||
$('.emoji-menu').remove();
|
||||
}
|
||||
}
|
||||
|
||||
let awardsHandlerPromise = null;
|
||||
export default function loadAwardsHandler(reload = false) {
|
||||
if (!awardsHandlerPromise || reload) {
|
||||
awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji')
|
||||
.then(Emoji => new AwardsHandler(Emoji));
|
||||
}
|
||||
return awardsHandlerPromise;
|
||||
}
|
||||
|
|
|
@ -1,23 +1,8 @@
|
|||
import autosize from 'vendor/autosize';
|
||||
|
||||
$(() => {
|
||||
const $fields = $('.js-autosize');
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const autosizeEls = document.querySelectorAll('.js-autosize');
|
||||
|
||||
$fields.on('autosize:resized', function resized() {
|
||||
const $field = $(this);
|
||||
$field.data('height', $field.outerHeight());
|
||||
});
|
||||
|
||||
$fields.on('resize.autosize', function resize() {
|
||||
const $field = $(this);
|
||||
if ($field.data('height') !== $field.outerHeight()) {
|
||||
$field.data('height', $field.outerHeight());
|
||||
autosize.destroy($field);
|
||||
$field.css('max-height', window.outerHeight);
|
||||
}
|
||||
});
|
||||
|
||||
autosize($fields);
|
||||
autosize.update($fields);
|
||||
$fields.css('resize', 'vertical');
|
||||
autosize(autosizeEls);
|
||||
autosize.update(autosizeEls);
|
||||
});
|
||||
|
|
|
@ -1,75 +1,9 @@
|
|||
import installCustomElements from 'document-register-element';
|
||||
import emojiMap from 'emojis/digests.json';
|
||||
import emojiAliases from 'emojis/aliases.json';
|
||||
import { getUnicodeSupportMap } from './gl_emoji/unicode_support_map';
|
||||
import { isEmojiUnicodeSupported } from './gl_emoji/is_emoji_unicode_supported';
|
||||
import isEmojiUnicodeSupported from '../emoji/support';
|
||||
|
||||
installCustomElements(window);
|
||||
|
||||
const generatedUnicodeSupportMap = getUnicodeSupportMap();
|
||||
|
||||
function emojiImageTag(name, src) {
|
||||
return `<img class="emoji" title=":${name}:" alt=":${name}:" src="${src}" width="20" height="20" align="absmiddle" />`;
|
||||
}
|
||||
|
||||
function assembleFallbackImageSrc(inputName) {
|
||||
let name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ?
|
||||
emojiAliases[inputName] : inputName;
|
||||
let emojiInfo = emojiMap[name];
|
||||
// Fallback to question mark for unknown emojis
|
||||
if (!emojiInfo) {
|
||||
name = 'grey_question';
|
||||
emojiInfo = emojiMap[name];
|
||||
}
|
||||
const fallbackImageSrc = `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/emoji/${name}-${emojiInfo.digest}.png`;
|
||||
|
||||
return fallbackImageSrc;
|
||||
}
|
||||
const glEmojiTagDefaults = {
|
||||
sprite: false,
|
||||
forceFallback: false,
|
||||
};
|
||||
function glEmojiTag(inputName, options) {
|
||||
const opts = Object.assign({}, glEmojiTagDefaults, options);
|
||||
let name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ?
|
||||
emojiAliases[inputName] : inputName;
|
||||
let emojiInfo = emojiMap[name];
|
||||
// Fallback to question mark for unknown emojis
|
||||
if (!emojiInfo) {
|
||||
name = 'grey_question';
|
||||
emojiInfo = emojiMap[name];
|
||||
}
|
||||
|
||||
const fallbackImageSrc = assembleFallbackImageSrc(name);
|
||||
const fallbackSpriteClass = `emoji-${name}`;
|
||||
|
||||
const classList = [];
|
||||
if (opts.forceFallback && opts.sprite) {
|
||||
classList.push('emoji-icon');
|
||||
classList.push(fallbackSpriteClass);
|
||||
}
|
||||
const classAttribute = classList.length > 0 ? `class="${classList.join(' ')}"` : '';
|
||||
const fallbackSpriteAttribute = opts.sprite ? `data-fallback-sprite-class="${fallbackSpriteClass}"` : '';
|
||||
let contents = emojiInfo.moji;
|
||||
if (opts.forceFallback && !opts.sprite) {
|
||||
contents = emojiImageTag(name, fallbackImageSrc);
|
||||
}
|
||||
|
||||
return `
|
||||
<gl-emoji
|
||||
${classAttribute}
|
||||
data-name="${name}"
|
||||
data-fallback-src="${fallbackImageSrc}"
|
||||
${fallbackSpriteAttribute}
|
||||
data-unicode-version="${emojiInfo.unicodeVersion}"
|
||||
title="${emojiInfo.description}"
|
||||
>
|
||||
${contents}
|
||||
</gl-emoji>
|
||||
`;
|
||||
}
|
||||
|
||||
function installGlEmojiElement() {
|
||||
export default function installGlEmojiElement() {
|
||||
const GlEmojiElementProto = Object.create(HTMLElement.prototype);
|
||||
GlEmojiElementProto.createdCallback = function createdCallback() {
|
||||
const emojiUnicode = this.textContent.trim();
|
||||
|
@ -88,19 +22,28 @@ function installGlEmojiElement() {
|
|||
const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0;
|
||||
|
||||
if (
|
||||
emojiUnicode &&
|
||||
isEmojiUnicode &&
|
||||
!isEmojiUnicodeSupported(generatedUnicodeSupportMap, emojiUnicode, unicodeVersion)
|
||||
!isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)
|
||||
) {
|
||||
// CSS sprite fallback takes precedence over image fallback
|
||||
if (hasCssSpriteFalback) {
|
||||
// IE 11 doesn't like adding multiple at once :(
|
||||
this.classList.add('emoji-icon');
|
||||
this.classList.add(fallbackSpriteClass);
|
||||
} else if (hasImageFallback) {
|
||||
this.innerHTML = emojiImageTag(name, fallbackSrc);
|
||||
} else {
|
||||
const src = assembleFallbackImageSrc(name);
|
||||
this.innerHTML = emojiImageTag(name, src);
|
||||
import(/* webpackChunkName: 'emoji' */ '../emoji')
|
||||
.then(({ emojiImageTag, emojiFallbackImageSrc }) => {
|
||||
if (hasImageFallback) {
|
||||
this.innerHTML = emojiImageTag(name, fallbackSrc);
|
||||
} else {
|
||||
const src = emojiFallbackImageSrc(name);
|
||||
this.innerHTML = emojiImageTag(name, src);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// do nothing
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -109,9 +52,3 @@ function installGlEmojiElement() {
|
|||
prototype: GlEmojiElementProto,
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
installGlEmojiElement,
|
||||
glEmojiTag,
|
||||
emojiImageTag,
|
||||
};
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import emojiMap from 'emojis/digests.json';
|
||||
import emojiAliases from 'emojis/aliases.json';
|
||||
|
||||
function isEmojiNameValid(inputName) {
|
||||
const name = Object.prototype.hasOwnProperty.call(emojiAliases, inputName) ?
|
||||
emojiAliases[inputName] : inputName;
|
||||
|
||||
return name && emojiMap[name];
|
||||
}
|
||||
|
||||
export default isEmojiNameValid;
|
|
@ -1,7 +1,7 @@
|
|||
import './autosize';
|
||||
import './bind_in_out';
|
||||
import './details_behavior';
|
||||
import { installGlEmojiElement } from './gl_emoji';
|
||||
import installGlEmojiElement from './gl_emoji';
|
||||
import './quick_submit';
|
||||
import './requires_input';
|
||||
import './toggler_behavior';
|
||||
|
|
|
@ -40,7 +40,7 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => {
|
|||
|
||||
e.preventDefault();
|
||||
const $form = $(e.target).closest('form');
|
||||
const $submitButton = $form.find('input[type=submit], button[type=submit]');
|
||||
const $submitButton = $form.find('input[type=submit], button[type=submit]').first();
|
||||
|
||||
if (!$submitButton.attr('disabled')) {
|
||||
$submitButton.trigger('click', [e]);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import _ from 'underscore';
|
||||
import '../commons/bootstrap';
|
||||
|
||||
// Requires Input behavior
|
||||
|
@ -48,7 +49,9 @@ function hideOrShowHelpBlock(form) {
|
|||
|
||||
$(() => {
|
||||
const $form = $('form.js-requires-input');
|
||||
$form.requiresInput();
|
||||
hideOrShowHelpBlock($form);
|
||||
$('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form));
|
||||
if ($form) {
|
||||
$form.requiresInput();
|
||||
hideOrShowHelpBlock($form);
|
||||
$('.select2.js-select-namespace').change(() => hideOrShowHelpBlock($form));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
// Toggle button. Show/hide content inside parent container.
|
||||
// Button does not change visibility. If button has icon - it changes chevron style.
|
||||
//
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* global Flash */
|
||||
|
||||
import sqljs from 'sql.js';
|
||||
import { template as _template } from 'underscore';
|
||||
|
||||
|
@ -15,19 +13,27 @@ const PREVIEW_TEMPLATE = _template(`
|
|||
class BalsamiqViewer {
|
||||
constructor(viewer) {
|
||||
this.viewer = viewer;
|
||||
this.endpoint = this.viewer.dataset.endpoint;
|
||||
}
|
||||
|
||||
loadFile() {
|
||||
const xhr = new XMLHttpRequest();
|
||||
loadFile(endpoint) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', this.endpoint, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.open('GET', endpoint, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onload = loadEvent => this.fileLoaded(loadEvent, resolve, reject);
|
||||
xhr.onerror = reject;
|
||||
|
||||
xhr.onload = this.renderFile.bind(this);
|
||||
xhr.onerror = BalsamiqViewer.onError;
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
xhr.send();
|
||||
fileLoaded(loadEvent, resolve, reject) {
|
||||
if (loadEvent.target.status !== 200) return reject();
|
||||
|
||||
this.renderFile(loadEvent);
|
||||
|
||||
return resolve();
|
||||
}
|
||||
|
||||
renderFile(loadEvent) {
|
||||
|
@ -103,12 +109,6 @@ class BalsamiqViewer {
|
|||
static parseTitle(resource) {
|
||||
return JSON.parse(resource.values[0][2]).name;
|
||||
}
|
||||
|
||||
static onError() {
|
||||
const flash = new Flash('Balsamiq file could not be loaded.');
|
||||
|
||||
return flash;
|
||||
}
|
||||
}
|
||||
|
||||
export default BalsamiqViewer;
|
||||
|
|
|
@ -1,6 +1,22 @@
|
|||
/* global Flash */
|
||||
|
||||
import BalsamiqViewer from './balsamiq/balsamiq_viewer';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const balsamiqViewer = new BalsamiqViewer(document.getElementById('js-balsamiq-viewer'));
|
||||
balsamiqViewer.loadFile();
|
||||
});
|
||||
function onError() {
|
||||
const flash = new window.Flash('Balsamiq file could not be loaded.');
|
||||
|
||||
return flash;
|
||||
}
|
||||
|
||||
function loadBalsamiqFile() {
|
||||
const viewer = document.getElementById('js-balsamiq-viewer');
|
||||
|
||||
if (!(viewer instanceof Element)) return;
|
||||
|
||||
const endpoint = viewer.dataset.endpoint;
|
||||
|
||||
const balsamiqViewer = new BalsamiqViewer(viewer);
|
||||
balsamiqViewer.loadFile(endpoint).catch(onError);
|
||||
}
|
||||
|
||||
$(loadBalsamiqFile);
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
/* eslint-disable func-names, object-shorthand, prefer-arrow-callback */
|
||||
/* global Dropzone */
|
||||
|
||||
import '../lib/utils/url_utility';
|
||||
import { HIDDEN_CLASS } from '../lib/utils/constants';
|
||||
|
||||
function toggleLoading($el, $icon, loading) {
|
||||
if (loading) {
|
||||
$el.disable();
|
||||
$icon.removeClass(HIDDEN_CLASS);
|
||||
} else {
|
||||
$el.enable();
|
||||
$icon.addClass(HIDDEN_CLASS);
|
||||
}
|
||||
}
|
||||
export default class BlobFileDropzone {
|
||||
constructor(form, method) {
|
||||
const formDropzone = form.find('.dropzone');
|
||||
const submitButton = form.find('#submit-all');
|
||||
const submitButtonLoadingIcon = submitButton.find('.js-loading-icon');
|
||||
const dropzoneMessage = form.find('.dz-message');
|
||||
Dropzone.autoDiscover = false;
|
||||
|
||||
const dropzone = formDropzone.dropzone({
|
||||
|
@ -26,16 +41,24 @@ export default class BlobFileDropzone {
|
|||
},
|
||||
init: function () {
|
||||
this.on('addedfile', function () {
|
||||
toggleLoading(submitButton, submitButtonLoadingIcon, false);
|
||||
dropzoneMessage.addClass(HIDDEN_CLASS);
|
||||
$('.dropzone-alerts').html('').hide();
|
||||
});
|
||||
this.on('removedfile', function () {
|
||||
toggleLoading(submitButton, submitButtonLoadingIcon, false);
|
||||
dropzoneMessage.removeClass(HIDDEN_CLASS);
|
||||
});
|
||||
this.on('success', function (header, response) {
|
||||
window.location.href = response.filePath;
|
||||
$('#modal-upload-blob').modal('hide');
|
||||
window.gl.utils.visitUrl(response.filePath);
|
||||
});
|
||||
this.on('maxfilesexceeded', function (file) {
|
||||
dropzoneMessage.addClass(HIDDEN_CLASS);
|
||||
this.removeFile(file);
|
||||
});
|
||||
this.on('sending', function (file, xhr, formData) {
|
||||
formData.append('branch_name', form.find('input[name="branch_name"]').val());
|
||||
formData.append('branch_name', form.find('.js-branch-name').val());
|
||||
formData.append('create_merge_request', form.find('.js-create-merge-request').val());
|
||||
formData.append('commit_message', form.find('.js-commit-message').val());
|
||||
});
|
||||
|
@ -48,14 +71,15 @@ export default class BlobFileDropzone {
|
|||
},
|
||||
});
|
||||
|
||||
const submitButton = form.find('#submit-all')[0];
|
||||
submitButton.addEventListener('click', function (e) {
|
||||
submitButton.on('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert('Please select a file');
|
||||
return false;
|
||||
}
|
||||
toggleLoading(submitButton, submitButtonLoadingIcon, true);
|
||||
dropzone[0].dropzone.processQueue();
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
class CreateBranchDropdown {
|
||||
constructor(el, targetBranchDropdown) {
|
||||
this.targetBranchDropdown = targetBranchDropdown;
|
||||
this.el = el;
|
||||
this.dropdownBack = this.el.closest('.dropdown').querySelector('.dropdown-menu-back');
|
||||
this.cancelButton = this.el.querySelector('.js-cancel-branch-btn');
|
||||
this.newBranchField = this.el.querySelector('#new_branch_name');
|
||||
this.newBranchCreateButton = this.el.querySelector('.js-new-branch-btn');
|
||||
|
||||
this.newBranchCreateButton.setAttribute('disabled', '');
|
||||
|
||||
this.addBindings();
|
||||
this.cleanupWrapper = this.cleanup.bind(this);
|
||||
document.addEventListener('beforeunload', this.cleanupWrapper);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
this.cleanBindings();
|
||||
document.removeEventListener('beforeunload', this.cleanupWrapper);
|
||||
}
|
||||
|
||||
cleanBindings() {
|
||||
this.newBranchField.removeEventListener('keyup', this.enableBranchCreateButtonWrapper);
|
||||
this.newBranchField.removeEventListener('change', this.enableBranchCreateButtonWrapper);
|
||||
this.newBranchField.removeEventListener('keydown', this.handleNewBranchKeydownWrapper);
|
||||
this.dropdownBack.removeEventListener('click', this.resetFormWrapper);
|
||||
this.cancelButton.removeEventListener('click', this.handleCancelClickWrapper);
|
||||
this.newBranchCreateButton.removeEventListener('click', this.createBranchWrapper);
|
||||
}
|
||||
|
||||
addBindings() {
|
||||
this.enableBranchCreateButtonWrapper = this.enableBranchCreateButton.bind(this);
|
||||
this.handleNewBranchKeydownWrapper = this.handleNewBranchKeydown.bind(this);
|
||||
this.resetFormWrapper = this.resetForm.bind(this);
|
||||
this.handleCancelClickWrapper = this.handleCancelClick.bind(this);
|
||||
this.createBranchWrapper = this.createBranch.bind(this);
|
||||
|
||||
this.newBranchField.addEventListener('keyup', this.enableBranchCreateButtonWrapper);
|
||||
this.newBranchField.addEventListener('change', this.enableBranchCreateButtonWrapper);
|
||||
this.newBranchField.addEventListener('keydown', this.handleNewBranchKeydownWrapper);
|
||||
this.dropdownBack.addEventListener('click', this.resetFormWrapper);
|
||||
this.cancelButton.addEventListener('click', this.handleCancelClickWrapper);
|
||||
this.newBranchCreateButton.addEventListener('click', this.createBranchWrapper);
|
||||
}
|
||||
|
||||
handleCancelClick(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.resetForm();
|
||||
this.dropdownBack.click();
|
||||
}
|
||||
|
||||
handleNewBranchKeydown(e) {
|
||||
const keyCode = e.which;
|
||||
const ENTER_KEYCODE = 13;
|
||||
if (keyCode === ENTER_KEYCODE) {
|
||||
this.createBranch(e);
|
||||
}
|
||||
}
|
||||
|
||||
enableBranchCreateButton() {
|
||||
if (this.newBranchField.value !== '') {
|
||||
this.newBranchCreateButton.removeAttribute('disabled');
|
||||
} else {
|
||||
this.newBranchCreateButton.setAttribute('disabled', '');
|
||||
}
|
||||
}
|
||||
|
||||
resetForm() {
|
||||
this.newBranchField.value = '';
|
||||
this.enableBranchCreateButtonWrapper();
|
||||
}
|
||||
|
||||
createBranch(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.newBranchCreateButton.getAttribute('disabled') === '') {
|
||||
return;
|
||||
}
|
||||
const newBranchName = this.newBranchField.value;
|
||||
this.targetBranchDropdown.setNewBranch(newBranchName);
|
||||
this.resetForm();
|
||||
}
|
||||
}
|
||||
|
||||
window.gl = window.gl || {};
|
||||
gl.CreateBranchDropdown = CreateBranchDropdown;
|
|
@ -1,5 +1,3 @@
|
|||
/* global Api */
|
||||
|
||||
export default class FileTemplateSelector {
|
||||
constructor(mediator) {
|
||||
this.mediator = mediator;
|
||||
|
@ -65,4 +63,3 @@ export default class FileTemplateSelector {
|
|||
this.reportSelection(opts);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,8 +51,9 @@ export default () => {
|
|||
methods: {
|
||||
loadFile() {
|
||||
this.$http.get(el.dataset.endpoint)
|
||||
.then(response => response.json())
|
||||
.then((res) => {
|
||||
this.json = res.json();
|
||||
this.json = res;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch((e) => {
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
const SELECT_ITEM_MSG = 'Select';
|
||||
|
||||
class TargetBranchDropDown {
|
||||
constructor(dropdown) {
|
||||
this.dropdown = dropdown;
|
||||
this.$dropdown = $(dropdown);
|
||||
this.fieldName = this.dropdown.getAttribute('data-field-name');
|
||||
this.form = this.dropdown.closest('form');
|
||||
this.createDropdown();
|
||||
}
|
||||
|
||||
static bootstrap() {
|
||||
const dropdowns = document.querySelectorAll('.js-project-branches-dropdown');
|
||||
[].forEach.call(dropdowns, dropdown => new TargetBranchDropDown(dropdown));
|
||||
}
|
||||
|
||||
createDropdown() {
|
||||
const self = this;
|
||||
this.$dropdown.glDropdown({
|
||||
selectable: true,
|
||||
filterable: true,
|
||||
search: {
|
||||
fields: ['title'],
|
||||
},
|
||||
data: (term, callback) => $.ajax({
|
||||
url: self.dropdown.getAttribute('data-refs-url'),
|
||||
data: {
|
||||
ref: self.dropdown.getAttribute('data-ref'),
|
||||
show_all: true,
|
||||
},
|
||||
dataType: 'json',
|
||||
}).done(refs => callback(self.dropdownData(refs))),
|
||||
toggleLabel(item, el) {
|
||||
if (el.is('.is-active')) {
|
||||
return item.text;
|
||||
}
|
||||
return SELECT_ITEM_MSG;
|
||||
},
|
||||
clicked(options) {
|
||||
options.e.preventDefault();
|
||||
self.onClick.call(self);
|
||||
},
|
||||
fieldName: self.fieldName,
|
||||
});
|
||||
return new gl.CreateBranchDropdown(this.form.querySelector('.dropdown-new-branch'), this);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
this.enableSubmit();
|
||||
this.$dropdown.trigger('change.branch');
|
||||
}
|
||||
|
||||
enableSubmit() {
|
||||
const submitBtn = this.form.querySelector('[type="submit"]');
|
||||
if (this.branchInput && this.branchInput.value) {
|
||||
submitBtn.removeAttribute('disabled');
|
||||
} else {
|
||||
submitBtn.setAttribute('disabled', '');
|
||||
}
|
||||
}
|
||||
|
||||
dropdownData(refs) {
|
||||
const branchList = this.dropdownItems(refs);
|
||||
this.cachedRefs = refs;
|
||||
this.addDefaultBranch(branchList);
|
||||
this.addNewBranch(branchList);
|
||||
return { Branches: branchList };
|
||||
}
|
||||
|
||||
dropdownItems(refs) {
|
||||
return refs.map(this.dropdownItem);
|
||||
}
|
||||
|
||||
dropdownItem(ref) {
|
||||
return { id: ref, text: ref, title: ref };
|
||||
}
|
||||
|
||||
addDefaultBranch(branchList) {
|
||||
// when no branch is selected do nothing
|
||||
if (!this.branchInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const branchInputVal = this.branchInput.value;
|
||||
const currentBranchIndex = this.searchBranch(branchList, branchInputVal);
|
||||
|
||||
if (currentBranchIndex === -1) {
|
||||
this.unshiftBranch(branchList, this.dropdownItem(branchInputVal));
|
||||
}
|
||||
}
|
||||
|
||||
addNewBranch(branchList) {
|
||||
if (this.newBranch) {
|
||||
this.unshiftBranch(branchList, this.newBranch);
|
||||
}
|
||||
}
|
||||
|
||||
searchBranch(branchList, branchName) {
|
||||
return _.findIndex(branchList, el => branchName === el.id);
|
||||
}
|
||||
|
||||
unshiftBranch(branchList, branch) {
|
||||
const branchIndex = this.searchBranch(branchList, branch.id);
|
||||
|
||||
if (branchIndex === -1) {
|
||||
branchList.unshift(branch);
|
||||
}
|
||||
}
|
||||
|
||||
setNewBranch(newBranchName) {
|
||||
this.newBranch = this.dropdownItem(newBranchName);
|
||||
this.refreshData();
|
||||
this.selectBranch(this.searchBranch(this.glDropdown.fullData.Branches, newBranchName));
|
||||
}
|
||||
|
||||
refreshData() {
|
||||
this.glDropdown.fullData = this.dropdownData(this.cachedRefs);
|
||||
this.clearFilter();
|
||||
}
|
||||
|
||||
clearFilter() {
|
||||
// apply an empty filter in order to refresh the data
|
||||
this.glDropdown.filter.filter('');
|
||||
this.dropdown.closest('.dropdown').querySelector('.dropdown-page-one .dropdown-input-field').value = '';
|
||||
}
|
||||
|
||||
selectBranch(index) {
|
||||
const branch = this.dropdown.closest('.dropdown').querySelectorAll('li a')[index];
|
||||
|
||||
if (!branch.classList.contains('is-active')) {
|
||||
branch.click();
|
||||
} else {
|
||||
this.closeDropdown();
|
||||
}
|
||||
}
|
||||
|
||||
closeDropdown() {
|
||||
this.dropdown.closest('.dropdown').querySelector('.dropdown-menu-close').click();
|
||||
}
|
||||
|
||||
get branchInput() {
|
||||
return this.form.querySelector(`input[name="${this.fieldName}"]`);
|
||||
}
|
||||
|
||||
get glDropdown() {
|
||||
return this.$dropdown.data('glDropdown');
|
||||
}
|
||||
}
|
||||
|
||||
window.gl = window.gl || {};
|
||||
gl.TargetBranchDropDown = TargetBranchDropDown;
|
|
@ -1,4 +1,4 @@
|
|||
/* global Api */
|
||||
import Api from '../../api';
|
||||
|
||||
import FileTemplateSelector from '../file_template_selector';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global Api */
|
||||
import Api from '../../api';
|
||||
|
||||
import FileTemplateSelector from '../file_template_selector';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global Api */
|
||||
import Api from '../../api';
|
||||
|
||||
import FileTemplateSelector from '../file_template_selector';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global Api */
|
||||
import Api from '../../api';
|
||||
|
||||
import FileTemplateSelector from '../file_template_selector';
|
||||
|
||||
|
|
|
@ -1,17 +1,38 @@
|
|||
/* global Flash */
|
||||
export default class BlobViewer {
|
||||
constructor() {
|
||||
BlobViewer.initAuxiliaryViewer();
|
||||
|
||||
this.initMainViewers();
|
||||
}
|
||||
|
||||
static initAuxiliaryViewer() {
|
||||
const auxiliaryViewer = document.querySelector('.blob-viewer[data-type="auxiliary"]');
|
||||
if (!auxiliaryViewer) return;
|
||||
|
||||
BlobViewer.loadViewer(auxiliaryViewer);
|
||||
}
|
||||
|
||||
initMainViewers() {
|
||||
this.$fileHolder = $('.file-holder');
|
||||
if (!this.$fileHolder.length) return;
|
||||
|
||||
this.switcher = document.querySelector('.js-blob-viewer-switcher');
|
||||
this.switcherBtns = document.querySelectorAll('.js-blob-viewer-switch-btn');
|
||||
this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
|
||||
this.simpleViewer = document.querySelector('.blob-viewer[data-type="simple"]');
|
||||
this.richViewer = document.querySelector('.blob-viewer[data-type="rich"]');
|
||||
this.$fileHolder = $('.file-holder');
|
||||
|
||||
let initialViewerName = document.querySelector('.blob-viewer:not(.hidden)').getAttribute('data-type');
|
||||
this.simpleViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="simple"]');
|
||||
this.richViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="rich"]');
|
||||
|
||||
this.initBindings();
|
||||
|
||||
this.switchToInitialViewer();
|
||||
}
|
||||
|
||||
switchToInitialViewer() {
|
||||
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
|
||||
let initialViewerName = initialViewer.getAttribute('data-type');
|
||||
|
||||
if (this.switcher && location.hash.indexOf('#L') === 0) {
|
||||
initialViewerName = 'simple';
|
||||
}
|
||||
|
@ -61,41 +82,13 @@ export default class BlobViewer {
|
|||
$(this.copySourceBtn).tooltip('fixTitle');
|
||||
}
|
||||
|
||||
loadViewer(viewerParam) {
|
||||
const viewer = viewerParam;
|
||||
const url = viewer.getAttribute('data-url');
|
||||
|
||||
if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewer.setAttribute('data-loading', 'true');
|
||||
|
||||
$.ajax({
|
||||
url,
|
||||
dataType: 'JSON',
|
||||
})
|
||||
.fail(() => new Flash('Error loading source view'))
|
||||
.done((data) => {
|
||||
viewer.innerHTML = data.html;
|
||||
$(viewer).renderGFM();
|
||||
|
||||
viewer.setAttribute('data-loaded', 'true');
|
||||
|
||||
this.$fileHolder.trigger('highlight:line');
|
||||
gl.utils.handleLocationHash();
|
||||
|
||||
this.toggleCopyButtonState();
|
||||
});
|
||||
}
|
||||
|
||||
switchToViewer(name) {
|
||||
const newViewer = document.querySelector(`.blob-viewer[data-type='${name}']`);
|
||||
const newViewer = this.$fileHolder[0].querySelector(`.blob-viewer[data-type='${name}']`);
|
||||
if (this.activeViewer === newViewer) return;
|
||||
|
||||
const oldButton = document.querySelector('.js-blob-viewer-switch-btn.active');
|
||||
const newButton = document.querySelector(`.js-blob-viewer-switch-btn[data-viewer='${name}']`);
|
||||
const oldViewer = document.querySelector(`.blob-viewer:not([data-type='${name}'])`);
|
||||
const oldViewer = this.$fileHolder[0].querySelector(`.blob-viewer:not([data-type='${name}'])`);
|
||||
|
||||
if (oldButton) {
|
||||
oldButton.classList.remove('active');
|
||||
|
@ -116,6 +109,41 @@ export default class BlobViewer {
|
|||
|
||||
this.toggleCopyButtonState();
|
||||
|
||||
this.loadViewer(newViewer);
|
||||
BlobViewer.loadViewer(newViewer)
|
||||
.then((viewer) => {
|
||||
$(viewer).renderGFM();
|
||||
|
||||
this.$fileHolder.trigger('highlight:line');
|
||||
gl.utils.handleLocationHash();
|
||||
|
||||
this.toggleCopyButtonState();
|
||||
})
|
||||
.catch(() => new Flash('Error loading viewer'));
|
||||
}
|
||||
|
||||
static loadViewer(viewerParam) {
|
||||
const viewer = viewerParam;
|
||||
const url = viewer.getAttribute('data-url');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
|
||||
resolve(viewer);
|
||||
return;
|
||||
}
|
||||
|
||||
viewer.setAttribute('data-loading', 'true');
|
||||
|
||||
$.ajax({
|
||||
url,
|
||||
dataType: 'JSON',
|
||||
})
|
||||
.fail(reject)
|
||||
.done((data) => {
|
||||
viewer.innerHTML = data.html;
|
||||
viewer.setAttribute('data-loaded', 'true');
|
||||
|
||||
resolve(viewer);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import BlobFileDropzone from '../blob/blob_file_dropzone';
|
|||
$(() => {
|
||||
const editBlobForm = $('.js-edit-blob-form');
|
||||
const uploadBlobForm = $('.js-upload-blob-form');
|
||||
const deleteBlobForm = $('.js-delete-blob-form');
|
||||
|
||||
if (editBlobForm.length) {
|
||||
const urlRoot = editBlobForm.data('relative-url-root');
|
||||
|
@ -30,4 +31,8 @@ $(() => {
|
|||
'.btn-upload-file',
|
||||
);
|
||||
}
|
||||
|
||||
if (deleteBlobForm.length) {
|
||||
new NewCommitForm(deleteBlobForm);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,27 +2,27 @@
|
|||
/* global BoardService */
|
||||
/* global Flash */
|
||||
|
||||
import _ from 'underscore';
|
||||
import Vue from 'vue';
|
||||
import VueResource from 'vue-resource';
|
||||
import FilteredSearchBoards from './filtered_search_boards';
|
||||
import eventHub from './eventhub';
|
||||
|
||||
require('./models/issue');
|
||||
require('./models/label');
|
||||
require('./models/list');
|
||||
require('./models/milestone');
|
||||
require('./models/assignee');
|
||||
require('./stores/boards_store');
|
||||
require('./stores/modal_store');
|
||||
require('./services/board_service');
|
||||
require('./mixins/modal_mixins');
|
||||
require('./mixins/sortable_default_options');
|
||||
require('./filters/due_date_filters');
|
||||
require('./components/board');
|
||||
require('./components/board_sidebar');
|
||||
require('./components/new_list_dropdown');
|
||||
require('./components/modal/index');
|
||||
require('../vue_shared/vue_resource_interceptor');
|
||||
import './models/issue';
|
||||
import './models/label';
|
||||
import './models/list';
|
||||
import './models/milestone';
|
||||
import './models/assignee';
|
||||
import './stores/boards_store';
|
||||
import './stores/modal_store';
|
||||
import './services/board_service';
|
||||
import './mixins/modal_mixins';
|
||||
import './mixins/sortable_default_options';
|
||||
import './filters/due_date_filters';
|
||||
import './components/board';
|
||||
import './components/board_sidebar';
|
||||
import './components/new_list_dropdown';
|
||||
import './components/modal/index';
|
||||
import '../vue_shared/vue_resource_interceptor';
|
||||
|
||||
Vue.use(VueResource);
|
||||
|
||||
|
@ -71,6 +71,7 @@ $(() => {
|
|||
gl.boardService = new BoardService(this.endpoint, this.bulkUpdatePath, this.boardId);
|
||||
|
||||
this.filterManager = new FilteredSearchBoards(Store.filter, true);
|
||||
this.filterManager.setup();
|
||||
|
||||
// Listen for updateTokens event
|
||||
eventHub.$on('updateTokens', this.updateTokens);
|
||||
|
@ -81,13 +82,16 @@ $(() => {
|
|||
mounted () {
|
||||
Store.disabled = this.disabled;
|
||||
gl.boardService.all()
|
||||
.then(response => response.json())
|
||||
.then((resp) => {
|
||||
resp.json().forEach((board) => {
|
||||
resp.forEach((board) => {
|
||||
const list = Store.addList(board, this.defaultAvatar);
|
||||
|
||||
if (list.type === 'closed') {
|
||||
list.position = Infinity;
|
||||
list.label = { description: 'Shows all closed issues. Moving an issue to this list closes it' };
|
||||
} else if (list.type === 'backlog') {
|
||||
list.position = -1;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -95,7 +99,8 @@ $(() => {
|
|||
|
||||
Store.addBlankState();
|
||||
this.loading = false;
|
||||
}).catch(() => new Flash('An error occurred. Please try again.'));
|
||||
})
|
||||
.catch(() => new Flash('An error occurred. Please try again.'));
|
||||
},
|
||||
methods: {
|
||||
updateTokens() {
|
||||
|
@ -128,7 +133,7 @@ $(() => {
|
|||
},
|
||||
computed: {
|
||||
disabled() {
|
||||
return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length;
|
||||
return !this.store.lists.filter(list => !list.preset).length;
|
||||
},
|
||||
tooltipTitle() {
|
||||
if (this.disabled) {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
|
||||
/* global Sortable */
|
||||
import Vue from 'vue';
|
||||
import AccessorUtilities from '../../lib/utils/accessor';
|
||||
import boardList from './board_list';
|
||||
import boardBlankState from './board_blank_state';
|
||||
|
||||
require('./board_delete');
|
||||
require('./board_list');
|
||||
import './board_delete';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
|
@ -24,6 +23,10 @@ gl.issueBoards.Board = Vue.extend({
|
|||
disabled: Boolean,
|
||||
issueLinkBase: String,
|
||||
rootPath: String,
|
||||
boardId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
@ -80,7 +83,16 @@ gl.issueBoards.Board = Vue.extend({
|
|||
methods: {
|
||||
showNewIssueForm() {
|
||||
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
|
||||
}
|
||||
},
|
||||
toggleExpanded(e) {
|
||||
if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
|
||||
this.list.isExpanded = !this.list.isExpanded;
|
||||
|
||||
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||
localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
|
||||
|
@ -104,4 +116,11 @@ gl.issueBoards.Board = Vue.extend({
|
|||
|
||||
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
|
||||
},
|
||||
created() {
|
||||
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||
const isCollapsed = localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
|
||||
|
||||
this.list.isExpanded = !isCollapsed;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* global ListLabel */
|
||||
|
||||
import _ from 'underscore';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
@ -64,8 +65,9 @@ export default {
|
|||
|
||||
// Save the labels
|
||||
gl.boardService.generateDefaultLists()
|
||||
.then((resp) => {
|
||||
resp.json().forEach((listObj) => {
|
||||
.then(resp => resp.json())
|
||||
.then((data) => {
|
||||
data.forEach((listObj) => {
|
||||
const list = Store.findList('title', listObj.title);
|
||||
|
||||
list.id = listObj.id;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require('./issue_card_inner');
|
||||
import './issue_card_inner';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import boardNewIssue from './board_new_issue';
|
||||
import boardCard from './board_card';
|
||||
import eventHub from '../eventhub';
|
||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
|
@ -44,6 +45,7 @@ export default {
|
|||
components: {
|
||||
boardCard,
|
||||
boardNewIssue,
|
||||
loadingIcon,
|
||||
},
|
||||
methods: {
|
||||
listHeight() {
|
||||
|
@ -55,6 +57,9 @@ export default {
|
|||
scrollTop() {
|
||||
return this.$refs.list.scrollTop + this.listHeight();
|
||||
},
|
||||
scrollToTop() {
|
||||
this.$refs.list.scrollTop = 0;
|
||||
},
|
||||
loadNextPage() {
|
||||
const getIssues = this.list.nextPage();
|
||||
const loadingDone = () => {
|
||||
|
@ -106,6 +111,7 @@ export default {
|
|||
},
|
||||
created() {
|
||||
eventHub.$on(`hide-issue-form-${this.list.id}`, this.toggleForm);
|
||||
eventHub.$on(`scroll-board-list-${this.list.id}`, this.scrollToTop);
|
||||
},
|
||||
mounted() {
|
||||
const options = gl.issueBoards.getBoardSortableDefaultOptions({
|
||||
|
@ -148,6 +154,7 @@ export default {
|
|||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off(`hide-issue-form-${this.list.id}`, this.toggleForm);
|
||||
eventHub.$off(`scroll-board-list-${this.list.id}`, this.scrollToTop);
|
||||
this.$refs.list.removeEventListener('scroll', this.onScroll);
|
||||
},
|
||||
template: `
|
||||
|
@ -156,14 +163,13 @@ export default {
|
|||
class="board-list-loading text-center"
|
||||
aria-label="Loading issues"
|
||||
v-if="loading">
|
||||
<i
|
||||
class="fa fa-spinner fa-spin"
|
||||
aria-hidden="true">
|
||||
</i>
|
||||
<loading-icon />
|
||||
</div>
|
||||
<board-new-issue
|
||||
:list="list"
|
||||
v-if="list.type !== 'closed' && showIssueForm"/>
|
||||
<transition name="slide-down">
|
||||
<board-new-issue
|
||||
:list="list"
|
||||
v-if="list.type !== 'closed' && showIssueForm"/>
|
||||
</transition>
|
||||
<ul
|
||||
class="board-list"
|
||||
v-show="!loading"
|
||||
|
@ -184,12 +190,12 @@ export default {
|
|||
class="board-list-count text-center"
|
||||
v-if="showCount"
|
||||
data-id="-1">
|
||||
<i
|
||||
class="fa fa-spinner fa-spin"
|
||||
aria-label="Loading more issues"
|
||||
aria-hidden="true"
|
||||
v-show="list.loadingMore">
|
||||
</i>
|
||||
|
||||
<loading-icon
|
||||
v-show="list.loadingMore"
|
||||
label="Loading more issues"
|
||||
/>
|
||||
|
||||
<span v-if="list.issues.length === list.issuesSize">
|
||||
Showing all issues
|
||||
</span>
|
||||
|
|
|
@ -17,7 +17,7 @@ export default {
|
|||
methods: {
|
||||
submit(e) {
|
||||
e.preventDefault();
|
||||
if (this.title.trim() === '') return;
|
||||
if (this.title.trim() === '') return Promise.resolve();
|
||||
|
||||
this.error = false;
|
||||
|
||||
|
@ -29,7 +29,10 @@ export default {
|
|||
assignees: [],
|
||||
});
|
||||
|
||||
this.list.newIssue(issue)
|
||||
eventHub.$emit(`scroll-board-list-${this.list.id}`);
|
||||
this.cancel();
|
||||
|
||||
return this.list.newIssue(issue)
|
||||
.then(() => {
|
||||
// Need this because our jQuery very kindly disables buttons on ALL form submissions
|
||||
$(this.$refs.submitButton).enable();
|
||||
|
@ -47,8 +50,6 @@ export default {
|
|||
// Show error message
|
||||
this.error = true;
|
||||
});
|
||||
|
||||
this.cancel();
|
||||
},
|
||||
cancel() {
|
||||
this.title = '';
|
||||
|
@ -75,6 +76,7 @@ export default {
|
|||
type="text"
|
||||
v-model="title"
|
||||
ref="input"
|
||||
autocomplete="off"
|
||||
:id="list.id + '-title'" />
|
||||
<div class="clearfix prepend-top-10">
|
||||
<button class="btn btn-success pull-left"
|
||||
|
|
|
@ -7,11 +7,9 @@
|
|||
|
||||
import Vue from 'vue';
|
||||
import eventHub from '../../sidebar/event_hub';
|
||||
|
||||
import AssigneeTitle from '../../sidebar/components/assignees/assignee_title';
|
||||
import Assignees from '../../sidebar/components/assignees/assignees';
|
||||
|
||||
require('./sidebar/remove_issue');
|
||||
import './sidebar/remove_issue';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
||||
|
@ -34,9 +32,12 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
|||
showSidebar () {
|
||||
return Object.keys(this.issue).length;
|
||||
},
|
||||
assigneeId() {
|
||||
return this.issue.assignee ? this.issue.assignee.id : 0;
|
||||
}
|
||||
milestoneTitle() {
|
||||
return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
|
||||
},
|
||||
canRemove() {
|
||||
return !this.list.preset;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
detail: {
|
||||
|
@ -62,18 +63,6 @@ gl.issueBoards.BoardSidebar = Vue.extend({
|
|||
},
|
||||
deep: true
|
||||
},
|
||||
issue () {
|
||||
if (this.showSidebar) {
|
||||
this.$nextTick(() => {
|
||||
$('.right-sidebar').getNiceScroll(0).doScrollTop(0, 0);
|
||||
$('.right-sidebar').getNiceScroll().resize();
|
||||
});
|
||||
}
|
||||
|
||||
this.issue = this.detail.issue;
|
||||
this.list = this.detail.list;
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
methods: {
|
||||
closeSidebar () {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||
import eventHub from '../eventhub';
|
||||
|
||||
const Store = gl.issueBoards.BoardsStore;
|
||||
|
@ -38,6 +39,9 @@ gl.issueBoards.IssueCardInner = Vue.extend({
|
|||
maxCounter: 99,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
userAvatarLink,
|
||||
},
|
||||
computed: {
|
||||
numberOverLimit() {
|
||||
return this.issue.assignees.length - this.limitBeforeCounter;
|
||||
|
@ -93,9 +97,8 @@ gl.issueBoards.IssueCardInner = Vue.extend({
|
|||
return `Avatar for ${assignee.name}`;
|
||||
},
|
||||
showLabel(label) {
|
||||
if (!this.list) return true;
|
||||
|
||||
return !this.list.label || label.id !== this.list.label.id;
|
||||
if (!label.id) return false;
|
||||
return true;
|
||||
},
|
||||
filterByLabel(label, e) {
|
||||
if (!this.updateFilters) return;
|
||||
|
@ -146,23 +149,17 @@ gl.issueBoards.IssueCardInner = Vue.extend({
|
|||
</span>
|
||||
</h4>
|
||||
<div class="card-assignee">
|
||||
<a
|
||||
class="has-tooltip js-no-trigger"
|
||||
:href="assigneeUrl(assignee)"
|
||||
:title="assigneeUrlTitle(assignee)"
|
||||
<user-avatar-link
|
||||
v-for="(assignee, index) in issue.assignees"
|
||||
:key="assignee.id"
|
||||
v-if="shouldRenderAssignee(index)"
|
||||
data-container="body"
|
||||
data-placement="bottom"
|
||||
>
|
||||
<img
|
||||
class="avatar avatar-inline s20"
|
||||
:src="assignee.avatar"
|
||||
width="20"
|
||||
height="20"
|
||||
:alt="avatarUrlTitle(assignee)"
|
||||
/>
|
||||
</a>
|
||||
class="js-no-trigger"
|
||||
:link-href="assigneeUrl(assignee)"
|
||||
:img-alt="avatarUrlTitle(assignee)"
|
||||
:img-src="assignee.avatar"
|
||||
:tooltip-text="assigneeUrlTitle(assignee)"
|
||||
tooltip-placement="bottom"
|
||||
/>
|
||||
<span
|
||||
class="avatar-counter has-tooltip"
|
||||
:title="assigneeCounterTooltip"
|
||||
|
|
|
@ -13,6 +13,7 @@ export default {
|
|||
FilteredSearchContainer.container = this.$el;
|
||||
|
||||
this.filteredSearch = new FilteredSearchBoards(this.store);
|
||||
this.filteredSearch.setup();
|
||||
this.filteredSearch.removeTokens();
|
||||
this.filteredSearch.handleInputPlaceholder();
|
||||
this.filteredSearch.toggleClearSearchButton();
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
/* global Flash */
|
||||
|
||||
import Vue from 'vue';
|
||||
|
||||
require('./lists_dropdown');
|
||||
import './lists_dropdown';
|
||||
|
||||
const ModalStore = gl.issueBoards.ModalStore;
|
||||
|
||||
|
@ -27,7 +26,8 @@ gl.issueBoards.ModalFooter = Vue.extend({
|
|||
},
|
||||
methods: {
|
||||
addIssues() {
|
||||
const list = this.modal.selectedList || this.state.lists[0];
|
||||
const firstListIndex = 1;
|
||||
const list = this.modal.selectedList || this.state.lists[firstListIndex];
|
||||
const selectedIssues = ModalStore.getSelectedIssues();
|
||||
const issueIds = selectedIssues.map(issue => issue.globalId);
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import modalFilters from './filters';
|
||||
|
||||
require('./tabs');
|
||||
import './tabs';
|
||||
|
||||
const ModalStore = gl.issueBoards.ModalStore;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/* global ListIssue */
|
||||
|
||||
import Vue from 'vue';
|
||||
import queryData from '../../utils/query_data';
|
||||
|
||||
require('./header');
|
||||
require('./list');
|
||||
require('./footer');
|
||||
require('./empty_state');
|
||||
import queryData from '~/boards/utils/query_data';
|
||||
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
|
||||
import './header';
|
||||
import './list';
|
||||
import './footer';
|
||||
import './empty_state';
|
||||
|
||||
const ModalStore = gl.issueBoards.ModalStore;
|
||||
|
||||
|
@ -88,9 +88,9 @@ gl.issueBoards.IssuesModal = Vue.extend({
|
|||
return gl.boardService.getBacklog(queryData(this.filter.path, {
|
||||
page: this.page,
|
||||
per: this.perPage,
|
||||
})).then((res) => {
|
||||
const data = res.json();
|
||||
|
||||
}))
|
||||
.then(resp => resp.json())
|
||||
.then((data) => {
|
||||
if (clearIssues) {
|
||||
this.issues = [];
|
||||
}
|
||||
|
@ -137,6 +137,7 @@ gl.issueBoards.IssuesModal = Vue.extend({
|
|||
'modal-list': gl.issueBoards.ModalList,
|
||||
'modal-footer': gl.issueBoards.ModalFooter,
|
||||
'empty-state': gl.issueBoards.ModalEmptyState,
|
||||
loadingIcon,
|
||||
},
|
||||
template: `
|
||||
<div
|
||||
|
@ -161,7 +162,7 @@ gl.issueBoards.IssuesModal = Vue.extend({
|
|||
class="add-issues-list text-center"
|
||||
v-if="loading || filterLoading">
|
||||
<div class="add-issues-list-loading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
<loading-icon />
|
||||
</div>
|
||||
</section>
|
||||
<modal-footer></modal-footer>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* global ListIssue */
|
||||
/* global bp */
|
||||
|
||||
import Vue from 'vue';
|
||||
import bp from '../../../breakpoints';
|
||||
|
||||
const ModalStore = gl.issueBoards.ModalStore;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
|
|||
},
|
||||
computed: {
|
||||
selected() {
|
||||
return this.modal.selectedList || this.state.lists[0];
|
||||
return this.modal.selectedList || this.state.lists[1];
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var,
|
||||
promise/catch-or-return */
|
||||
import _ from 'underscore';
|
||||
|
||||
window.gl = window.gl || {};
|
||||
window.gl.issueBoards = window.gl.issueBoards || {};
|
||||
|
|
|
@ -46,8 +46,7 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({
|
|||
},
|
||||
template: `
|
||||
<div
|
||||
class="block list"
|
||||
v-if="list.type !== 'closed'">
|
||||
class="block list">
|
||||
<button
|
||||
class="btn btn-default btn-block"
|
||||
type="button"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import FilteredSearchContainer from '../filtered_search/container';
|
||||
|
||||
export default class FilteredSearchBoards extends gl.FilteredSearchManager {
|
||||
constructor(store, updateUrl = false) {
|
||||
constructor(store, updateUrl = false, cantEdit = []) {
|
||||
super('boards');
|
||||
|
||||
this.store = store;
|
||||
|
@ -11,6 +11,7 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
|
|||
// Issue boards is slightly different, we handle all the requests async
|
||||
// instead or reloading the page, we just re-fire the list ajax requests
|
||||
this.isHandledAsync = true;
|
||||
this.cantEdit = cantEdit;
|
||||
}
|
||||
|
||||
updateObject(path) {
|
||||
|
@ -40,4 +41,8 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
|
|||
// Get the placeholder back if search is empty
|
||||
this.filteredSearchInput.dispatchEvent(new Event('input'));
|
||||
}
|
||||
|
||||
canEdit(tokenName) {
|
||||
return this.cantEdit.indexOf(tokenName) === -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,9 @@ class List {
|
|||
this.position = obj.position;
|
||||
this.title = obj.title;
|
||||
this.type = obj.list_type;
|
||||
this.preset = ['closed', 'blank'].indexOf(this.type) > -1;
|
||||
this.preset = ['backlog', 'closed', 'blank'].indexOf(this.type) > -1;
|
||||
this.isExpandable = ['backlog', 'closed'].indexOf(this.type) > -1;
|
||||
this.isExpanded = true;
|
||||
this.page = 1;
|
||||
this.loading = true;
|
||||
this.loadingMore = false;
|
||||
|
@ -38,9 +40,8 @@ class List {
|
|||
|
||||
save () {
|
||||
return gl.boardService.createList(this.label.id)
|
||||
.then((resp) => {
|
||||
const data = resp.json();
|
||||
|
||||
.then(resp => resp.json())
|
||||
.then((data) => {
|
||||
this.id = data.id;
|
||||
this.type = data.list_type;
|
||||
this.position = data.position;
|
||||
|
@ -89,8 +90,8 @@ class List {
|
|||
}
|
||||
|
||||
return gl.boardService.getIssuesForList(this.id, data)
|
||||
.then((resp) => {
|
||||
const data = resp.json();
|
||||
.then(resp => resp.json())
|
||||
.then((data) => {
|
||||
this.loading = false;
|
||||
this.issuesSize = data.size;
|
||||
|
||||
|
@ -103,13 +104,18 @@ class List {
|
|||
}
|
||||
|
||||
newIssue (issue) {
|
||||
this.addIssue(issue);
|
||||
this.addIssue(issue, null, 0);
|
||||
this.issuesSize += 1;
|
||||
|
||||
return gl.boardService.newIssue(this.id, issue)
|
||||
.then((resp) => {
|
||||
const data = resp.json();
|
||||
.then(resp => resp.json())
|
||||
.then((data) => {
|
||||
issue.id = data.iid;
|
||||
|
||||
if (this.issuesSize > 1) {
|
||||
const moveBeforeIid = this.issues[1].id;
|
||||
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeIid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -23,11 +23,6 @@ class BoardService {
|
|||
url: bulkUpdatePath,
|
||||
},
|
||||
});
|
||||
|
||||
Vue.http.interceptors.push((request, next) => {
|
||||
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
all () {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable comma-dangle, space-before-function-paren, one-var, no-shadow, dot-notation, max-len */
|
||||
/* global List */
|
||||
|
||||
import _ from 'underscore';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
window.gl = window.gl || {};
|
||||
|
@ -22,6 +22,7 @@ gl.issueBoards.BoardsStore = {
|
|||
create () {
|
||||
this.state.lists = [];
|
||||
this.filter.path = gl.utils.getUrlParamsArray().join('&');
|
||||
this.detail = { issue: {} };
|
||||
},
|
||||
addList (listObj, defaultAvatar) {
|
||||
const list = new List(listObj, defaultAvatar);
|
||||
|
@ -31,10 +32,14 @@ gl.issueBoards.BoardsStore = {
|
|||
},
|
||||
new (listObj) {
|
||||
const list = this.addList(listObj);
|
||||
const backlogList = this.findList('type', 'backlog', 'backlog');
|
||||
|
||||
list
|
||||
.save()
|
||||
.then(() => {
|
||||
// Remove any new issues from the backlog
|
||||
// as they will be visible in the new list
|
||||
list.issues.forEach(backlogList.removeIssue.bind(backlogList));
|
||||
this.state.lists = _.sortBy(this.state.lists, 'position');
|
||||
})
|
||||
.catch(() => {
|
||||
|
@ -47,7 +52,7 @@ gl.issueBoards.BoardsStore = {
|
|||
},
|
||||
shouldAddBlankState () {
|
||||
// Decide whether to add the blank state
|
||||
return !(this.state.lists.filter(list => list.type !== 'closed')[0]);
|
||||
return !(this.state.lists.filter(list => list.type !== 'backlog' && list.type !== 'closed')[0]);
|
||||
},
|
||||
addBlankState () {
|
||||
if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
|
||||
|
@ -100,7 +105,7 @@ gl.issueBoards.BoardsStore = {
|
|||
issueTo.removeLabel(listFrom.label);
|
||||
}
|
||||
|
||||
if (listTo.type === 'closed') {
|
||||
if (listTo.type === 'closed' && listFrom.type !== 'backlog') {
|
||||
issueLists.forEach((list) => {
|
||||
list.removeIssue(issue);
|
||||
});
|
||||
|
|
36
app/assets/javascripts/branches/branches_delete_modal.js
Normal file
36
app/assets/javascripts/branches/branches_delete_modal.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
const MODAL_SELECTOR = '#modal-delete-branch';
|
||||
|
||||
class DeleteModal {
|
||||
constructor() {
|
||||
this.$modal = $(MODAL_SELECTOR);
|
||||
this.$toggleBtns = $(`[data-target="${MODAL_SELECTOR}"]`);
|
||||
this.$branchName = $('.js-branch-name', this.$modal);
|
||||
this.$confirmInput = $('.js-delete-branch-input', this.$modal);
|
||||
this.$deleteBtn = $('.js-delete-branch', this.$modal);
|
||||
this.bindEvents();
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.$toggleBtns.on('click', this.setModalData.bind(this));
|
||||
this.$confirmInput.on('input', this.setDeleteDisabled.bind(this));
|
||||
}
|
||||
|
||||
setModalData(e) {
|
||||
this.branchName = e.currentTarget.dataset.branchName || '';
|
||||
this.deletePath = e.currentTarget.dataset.deletePath || '';
|
||||
this.updateModal();
|
||||
}
|
||||
|
||||
setDeleteDisabled(e) {
|
||||
this.$deleteBtn.attr('disabled', e.currentTarget.value !== this.branchName);
|
||||
}
|
||||
|
||||
updateModal() {
|
||||
this.$branchName.text(this.branchName);
|
||||
this.$confirmInput.val('');
|
||||
this.$deleteBtn.attr('href', this.deletePath);
|
||||
this.$deleteBtn.attr('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
export default DeleteModal;
|
|
@ -1,66 +1,19 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, quotes, no-shadow, prefer-arrow-callback, prefer-template, consistent-return, no-return-assign, new-parens, no-param-reassign, max-len */
|
||||
export const breakpoints = {
|
||||
lg: 1200,
|
||||
md: 992,
|
||||
sm: 768,
|
||||
xs: 0,
|
||||
};
|
||||
|
||||
var Breakpoints = (function() {
|
||||
var BreakpointInstance, instance;
|
||||
const BreakpointInstance = {
|
||||
windowWidth: () => window.innerWidth,
|
||||
getBreakpointSize() {
|
||||
const windowWidth = this.windowWidth();
|
||||
|
||||
function Breakpoints() {}
|
||||
const breakpoint = Object.keys(breakpoints).find(key => windowWidth > breakpoints[key]);
|
||||
|
||||
instance = null;
|
||||
return breakpoint;
|
||||
},
|
||||
};
|
||||
|
||||
BreakpointInstance = (function() {
|
||||
var BREAKPOINTS;
|
||||
|
||||
BREAKPOINTS = ["xs", "sm", "md", "lg"];
|
||||
|
||||
function BreakpointInstance() {
|
||||
this.setup();
|
||||
}
|
||||
|
||||
BreakpointInstance.prototype.setup = function() {
|
||||
var allDeviceSelector, els;
|
||||
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
|
||||
return ".device-" + breakpoint;
|
||||
});
|
||||
if ($(allDeviceSelector.join(",")).length) {
|
||||
return;
|
||||
}
|
||||
// Create all the elements
|
||||
els = $.map(BREAKPOINTS, function(breakpoint) {
|
||||
return "<div class='device-" + breakpoint + " visible-" + breakpoint + "'></div>";
|
||||
});
|
||||
return $("body").append(els.join(''));
|
||||
};
|
||||
|
||||
BreakpointInstance.prototype.visibleDevice = function() {
|
||||
var allDeviceSelector;
|
||||
allDeviceSelector = BREAKPOINTS.map(function(breakpoint) {
|
||||
return ".device-" + breakpoint;
|
||||
});
|
||||
return $(allDeviceSelector.join(",")).filter(":visible");
|
||||
};
|
||||
|
||||
BreakpointInstance.prototype.getBreakpointSize = function() {
|
||||
var $visibleDevice;
|
||||
$visibleDevice = this.visibleDevice;
|
||||
// TODO: Consider refactoring in light of turbolinks removal.
|
||||
// the page refreshed via turbolinks
|
||||
if (!$visibleDevice().length) {
|
||||
this.setup();
|
||||
}
|
||||
$visibleDevice = this.visibleDevice();
|
||||
return $visibleDevice.attr("class").split("visible-")[1];
|
||||
};
|
||||
|
||||
return BreakpointInstance;
|
||||
})();
|
||||
|
||||
Breakpoints.get = function() {
|
||||
return instance != null ? instance : instance = new BreakpointInstance;
|
||||
};
|
||||
|
||||
return Breakpoints;
|
||||
})();
|
||||
|
||||
$(() => { window.bp = Breakpoints.get(); });
|
||||
|
||||
window.Breakpoints = Breakpoints;
|
||||
export default BreakpointInstance;
|
||||
|
|
|
@ -1,47 +1,38 @@
|
|||
/* eslint-disable func-names, wrap-iife, no-use-before-define,
|
||||
consistent-return, prefer-rest-params */
|
||||
/* global Breakpoints */
|
||||
|
||||
import _ from 'underscore';
|
||||
import bp from './breakpoints';
|
||||
import { bytesToKiB } from './lib/utils/number_utils';
|
||||
|
||||
const bind = function (fn, me) { return function () { return fn.apply(me, arguments); }; };
|
||||
const AUTO_SCROLL_OFFSET = 75;
|
||||
const DOWN_BUILD_TRACE = '#down-build-trace';
|
||||
|
||||
window.Build = (function () {
|
||||
Build.timeout = null;
|
||||
|
||||
Build.state = null;
|
||||
|
||||
function Build(options) {
|
||||
this.options = options || $('.js-build-options').data();
|
||||
|
||||
this.pageUrl = this.options.pageUrl;
|
||||
this.buildUrl = this.options.buildUrl;
|
||||
this.buildStatus = this.options.buildStatus;
|
||||
this.state = this.options.logState;
|
||||
this.buildStage = this.options.buildStage;
|
||||
this.$document = $(document);
|
||||
this.logBytes = 0;
|
||||
this.hasBeenScrolled = false;
|
||||
|
||||
this.updateDropdown = bind(this.updateDropdown, this);
|
||||
this.updateDropdown = this.updateDropdown.bind(this);
|
||||
this.getBuildTrace = this.getBuildTrace.bind(this);
|
||||
|
||||
this.$body = $('body');
|
||||
this.$buildTrace = $('#build-trace');
|
||||
this.$autoScrollContainer = $('.autoscroll-container');
|
||||
this.$autoScrollStatus = $('#autoscroll-status');
|
||||
this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text');
|
||||
this.$upBuildTrace = $('#up-build-trace');
|
||||
this.$downBuildTrace = $(DOWN_BUILD_TRACE);
|
||||
this.$scrollTopBtn = $('#scroll-top');
|
||||
this.$scrollBottomBtn = $('#scroll-bottom');
|
||||
this.$buildRefreshAnimation = $('.js-build-refresh');
|
||||
this.$buildScroll = $('#js-build-scroll');
|
||||
this.$truncatedInfo = $('.js-truncated-info');
|
||||
this.$buildTraceOutput = $('.js-build-output');
|
||||
this.$topBar = $('.js-top-bar');
|
||||
|
||||
// Scroll controllers
|
||||
this.$scrollTopBtn = $('.js-scroll-up');
|
||||
this.$scrollBottomBtn = $('.js-scroll-down');
|
||||
|
||||
clearTimeout(Build.timeout);
|
||||
// Init breakpoint checker
|
||||
this.bp = Breakpoints.get();
|
||||
|
||||
this.initSidebar();
|
||||
this.populateJobs(this.buildStage);
|
||||
|
@ -56,54 +47,141 @@ window.Build = (function () {
|
|||
.off('click', '.stage-item')
|
||||
.on('click', '.stage-item', this.updateDropdown);
|
||||
|
||||
this.$document.on('scroll', this.initScrollMonitor.bind(this));
|
||||
// add event listeners to the scroll buttons
|
||||
this.$scrollTopBtn
|
||||
.off('click')
|
||||
.on('click', this.scrollToTop.bind(this));
|
||||
|
||||
this.$scrollBottomBtn
|
||||
.off('click')
|
||||
.on('click', this.scrollToBottom.bind(this));
|
||||
|
||||
this.scrollThrottled = _.throttle(this.toggleScroll.bind(this), 100);
|
||||
|
||||
$(window)
|
||||
.off('scroll')
|
||||
.on('scroll', () => {
|
||||
const contentHeight = this.$buildTraceOutput.height();
|
||||
if (contentHeight > this.windowSize) {
|
||||
// means the user did not scroll, the content was updated.
|
||||
this.windowSize = contentHeight;
|
||||
} else {
|
||||
// User scrolled
|
||||
this.hasBeenScrolled = true;
|
||||
this.toggleScrollAnimation(false);
|
||||
}
|
||||
|
||||
this.scrollThrottled();
|
||||
});
|
||||
|
||||
$(window)
|
||||
.off('resize.build')
|
||||
.on('resize.build', this.sidebarOnResize.bind(this));
|
||||
|
||||
$('a', this.$buildScroll)
|
||||
.off('click.stepTrace')
|
||||
.on('click.stepTrace', this.stepTrace);
|
||||
.on('resize.build', _.throttle(this.sidebarOnResize.bind(this), 100));
|
||||
|
||||
this.updateArtifactRemoveDate();
|
||||
this.initScrollButtonAffix();
|
||||
this.invokeBuildTrace();
|
||||
this.initAffixTopArea();
|
||||
|
||||
this.getBuildTrace();
|
||||
}
|
||||
|
||||
Build.prototype.initAffixTopArea = function () {
|
||||
/**
|
||||
If the browser does not support position sticky, it returns the position as static.
|
||||
If the browser does support sticky, then we allow the browser to handle it, if not
|
||||
then we default back to Bootstraps affix
|
||||
**/
|
||||
if (this.$topBar.css('position') !== 'static') return;
|
||||
|
||||
const offsetTop = this.$buildTrace.offset().top;
|
||||
|
||||
this.$topBar.affix({
|
||||
offset: {
|
||||
top: offsetTop,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Build.prototype.canScroll = function () {
|
||||
return $(document).height() > $(window).height();
|
||||
};
|
||||
|
||||
Build.prototype.toggleScroll = function () {
|
||||
const currentPosition = $(document).scrollTop();
|
||||
const scrollHeight = $(document).height();
|
||||
|
||||
const windowHeight = $(window).height();
|
||||
if (this.canScroll()) {
|
||||
if (currentPosition > 0 &&
|
||||
(scrollHeight - currentPosition !== windowHeight)) {
|
||||
// User is in the middle of the log
|
||||
|
||||
this.toggleDisableButton(this.$scrollTopBtn, false);
|
||||
this.toggleDisableButton(this.$scrollBottomBtn, false);
|
||||
} else if (currentPosition === 0) {
|
||||
// User is at Top of Build Log
|
||||
|
||||
this.toggleDisableButton(this.$scrollTopBtn, true);
|
||||
this.toggleDisableButton(this.$scrollBottomBtn, false);
|
||||
} else if (scrollHeight - currentPosition === windowHeight) {
|
||||
// User is at the bottom of the build log.
|
||||
|
||||
this.toggleDisableButton(this.$scrollTopBtn, false);
|
||||
this.toggleDisableButton(this.$scrollBottomBtn, true);
|
||||
}
|
||||
} else {
|
||||
this.toggleDisableButton(this.$scrollTopBtn, true);
|
||||
this.toggleDisableButton(this.$scrollBottomBtn, true);
|
||||
}
|
||||
};
|
||||
|
||||
Build.prototype.scrollDown = function () {
|
||||
$(document).scrollTop($(document).height());
|
||||
};
|
||||
|
||||
Build.prototype.scrollToBottom = function () {
|
||||
this.scrollDown();
|
||||
this.hasBeenScrolled = true;
|
||||
this.toggleScroll();
|
||||
};
|
||||
|
||||
Build.prototype.scrollToTop = function () {
|
||||
$(document).scrollTop(0);
|
||||
this.hasBeenScrolled = true;
|
||||
this.toggleScroll();
|
||||
};
|
||||
|
||||
Build.prototype.toggleDisableButton = function ($button, disable) {
|
||||
if (disable && $button.prop('disabled')) return;
|
||||
$button.prop('disabled', disable);
|
||||
};
|
||||
|
||||
Build.prototype.toggleScrollAnimation = function (toggle) {
|
||||
this.$scrollBottomBtn.toggleClass('animate', toggle);
|
||||
};
|
||||
|
||||
Build.prototype.initSidebar = function () {
|
||||
this.$sidebar = $('.js-build-sidebar');
|
||||
this.$sidebar.niceScroll();
|
||||
this.$document
|
||||
.off('click', '.js-sidebar-build-toggle')
|
||||
.on('click', '.js-sidebar-build-toggle', this.toggleSidebar);
|
||||
};
|
||||
|
||||
Build.prototype.invokeBuildTrace = function () {
|
||||
return this.getBuildTrace();
|
||||
};
|
||||
|
||||
Build.prototype.getBuildTrace = function () {
|
||||
return $.ajax({
|
||||
url: `${this.pageUrl}/trace.json`,
|
||||
dataType: 'json',
|
||||
data: {
|
||||
state: this.state,
|
||||
},
|
||||
success: ((log) => {
|
||||
const $buildContainer = $('.js-build-output');
|
||||
|
||||
data: this.state,
|
||||
})
|
||||
.done((log) => {
|
||||
gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`);
|
||||
|
||||
if (log.state) {
|
||||
this.state = log.state;
|
||||
}
|
||||
|
||||
this.windowSize = this.$buildTraceOutput.height();
|
||||
|
||||
if (log.append) {
|
||||
$buildContainer.append(log.html);
|
||||
this.$buildTraceOutput.append(log.html);
|
||||
this.logBytes += log.size;
|
||||
} else {
|
||||
$buildContainer.html(log.html);
|
||||
this.$buildTraceOutput.html(log.html);
|
||||
this.logBytes = log.size;
|
||||
}
|
||||
|
||||
|
@ -114,157 +192,62 @@ window.Build = (function () {
|
|||
const size = bytesToKiB(this.logBytes);
|
||||
$('.js-truncated-info-size').html(`${size}`);
|
||||
this.$truncatedInfo.removeClass('hidden');
|
||||
this.initAffixTruncatedInfo();
|
||||
} else {
|
||||
this.$truncatedInfo.addClass('hidden');
|
||||
}
|
||||
|
||||
this.checkAutoscroll();
|
||||
|
||||
if (!log.complete) {
|
||||
if (!this.hasBeenScrolled) {
|
||||
this.toggleScrollAnimation(true);
|
||||
} else {
|
||||
this.toggleScrollAnimation(false);
|
||||
}
|
||||
|
||||
Build.timeout = setTimeout(() => {
|
||||
this.invokeBuildTrace();
|
||||
this.getBuildTrace();
|
||||
}, 4000);
|
||||
} else {
|
||||
this.$buildRefreshAnimation.remove();
|
||||
this.toggleScrollAnimation(false);
|
||||
}
|
||||
|
||||
if (log.status !== this.buildStatus) {
|
||||
let pageUrl = this.pageUrl;
|
||||
|
||||
if (this.$autoScrollStatus.data('state') === 'enabled') {
|
||||
pageUrl += DOWN_BUILD_TRACE;
|
||||
}
|
||||
|
||||
gl.utils.visitUrl(pageUrl);
|
||||
gl.utils.visitUrl(this.pageUrl);
|
||||
}
|
||||
}),
|
||||
error: () => {
|
||||
})
|
||||
.fail(() => {
|
||||
this.$buildRefreshAnimation.remove();
|
||||
return this.initScrollMonitor();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Build.prototype.checkAutoscroll = function () {
|
||||
if (this.$autoScrollStatus.data('state') === 'enabled') {
|
||||
return $('html,body').scrollTop(this.$buildTrace.height());
|
||||
}
|
||||
|
||||
// Handle a situation where user started new build
|
||||
// but never scrolled a page
|
||||
if (!this.$scrollTopBtn.is(':visible') &&
|
||||
!this.$scrollBottomBtn.is(':visible') &&
|
||||
!gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
|
||||
this.$scrollBottomBtn.show();
|
||||
}
|
||||
};
|
||||
|
||||
Build.prototype.initScrollButtonAffix = function () {
|
||||
// Hide everything initially
|
||||
this.$scrollTopBtn.hide();
|
||||
this.$scrollBottomBtn.hide();
|
||||
this.$autoScrollContainer.hide();
|
||||
};
|
||||
|
||||
// Page scroll listener to detect if user has scrolling page
|
||||
// and handle following cases
|
||||
// 1) User is at Top of Build Log;
|
||||
// - Hide Top Arrow button
|
||||
// - Show Bottom Arrow button
|
||||
// - Disable Autoscroll and hide indicator (when build is running)
|
||||
// 2) User is at Bottom of Build Log;
|
||||
// - Show Top Arrow button
|
||||
// - Hide Bottom Arrow button
|
||||
// - Enable Autoscroll and show indicator (when build is running)
|
||||
// 3) User is somewhere in middle of Build Log;
|
||||
// - Show Top Arrow button
|
||||
// - Show Bottom Arrow button
|
||||
// - Disable Autoscroll and hide indicator (when build is running)
|
||||
Build.prototype.initScrollMonitor = function () {
|
||||
if (!gl.utils.isInViewport(this.$upBuildTrace.get(0)) &&
|
||||
!gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
|
||||
// User is somewhere in middle of Build Log
|
||||
|
||||
this.$scrollTopBtn.show();
|
||||
|
||||
if (this.buildStatus === 'success' || this.buildStatus === 'failed') { // Check if Build is completed
|
||||
this.$scrollBottomBtn.show();
|
||||
} else if (this.$buildRefreshAnimation.is(':visible') &&
|
||||
!gl.utils.isInViewport(this.$buildRefreshAnimation.get(0))) {
|
||||
this.$scrollBottomBtn.show();
|
||||
} else {
|
||||
this.$scrollBottomBtn.hide();
|
||||
}
|
||||
|
||||
// Hide Autoscroll Status Indicator
|
||||
if (this.$scrollBottomBtn.is(':visible')) {
|
||||
this.$autoScrollContainer.hide();
|
||||
this.$autoScrollStatusText.removeClass('animate');
|
||||
} else {
|
||||
this.$autoScrollContainer.css({
|
||||
top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET,
|
||||
}).show();
|
||||
this.$autoScrollStatusText.addClass('animate');
|
||||
}
|
||||
} else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) &&
|
||||
!gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
|
||||
// User is at Top of Build Log
|
||||
|
||||
this.$scrollTopBtn.hide();
|
||||
this.$scrollBottomBtn.show();
|
||||
|
||||
this.$autoScrollContainer.hide();
|
||||
this.$autoScrollStatusText.removeClass('animate');
|
||||
} else if ((!gl.utils.isInViewport(this.$upBuildTrace.get(0)) &&
|
||||
gl.utils.isInViewport(this.$downBuildTrace.get(0))) ||
|
||||
(this.$buildRefreshAnimation.is(':visible') &&
|
||||
gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)))) {
|
||||
// User is at Bottom of Build Log
|
||||
|
||||
this.$scrollTopBtn.show();
|
||||
this.$scrollBottomBtn.hide();
|
||||
|
||||
// Show and Reposition Autoscroll Status Indicator
|
||||
this.$autoScrollContainer.css({
|
||||
top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET,
|
||||
}).show();
|
||||
this.$autoScrollStatusText.addClass('animate');
|
||||
} else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) &&
|
||||
gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
|
||||
// Build Log height is small
|
||||
|
||||
this.$scrollTopBtn.hide();
|
||||
this.$scrollBottomBtn.hide();
|
||||
|
||||
// Hide Autoscroll Status Indicator
|
||||
this.$autoScrollContainer.hide();
|
||||
this.$autoScrollStatusText.removeClass('animate');
|
||||
}
|
||||
|
||||
if (this.buildStatus === 'running' || this.buildStatus === 'pending') {
|
||||
// Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise.
|
||||
this.$autoScrollStatus.data(
|
||||
'state',
|
||||
gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)) ? 'enabled' : 'disabled',
|
||||
);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
if (!this.hasBeenScrolled) {
|
||||
this.scrollDown();
|
||||
}
|
||||
})
|
||||
.then(() => this.toggleScroll());
|
||||
};
|
||||
|
||||
Build.prototype.shouldHideSidebarForViewport = function () {
|
||||
const bootstrapBreakpoint = this.bp.getBreakpointSize();
|
||||
const bootstrapBreakpoint = bp.getBreakpointSize();
|
||||
return bootstrapBreakpoint === 'xs' || bootstrapBreakpoint === 'sm';
|
||||
};
|
||||
|
||||
Build.prototype.toggleSidebar = function (shouldHide) {
|
||||
const shouldShow = typeof shouldHide === 'boolean' ? !shouldHide : undefined;
|
||||
const $toggleButton = $('.js-sidebar-build-toggle-header');
|
||||
|
||||
this.$buildScroll.toggleClass('sidebar-expanded', shouldShow)
|
||||
.toggleClass('sidebar-collapsed', shouldHide);
|
||||
this.$truncatedInfo.toggleClass('sidebar-expanded', shouldShow)
|
||||
.toggleClass('sidebar-collapsed', shouldHide);
|
||||
this.$sidebar.toggleClass('right-sidebar-expanded', shouldShow)
|
||||
this.$sidebar
|
||||
.toggleClass('right-sidebar-expanded', shouldShow)
|
||||
.toggleClass('right-sidebar-collapsed', shouldHide);
|
||||
|
||||
this.$topBar
|
||||
.toggleClass('sidebar-expanded', shouldShow)
|
||||
.toggleClass('sidebar-collapsed', shouldHide);
|
||||
|
||||
if (this.$sidebar.hasClass('right-sidebar-expanded')) {
|
||||
$toggleButton.addClass('hidden');
|
||||
} else {
|
||||
$toggleButton.removeClass('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
Build.prototype.sidebarOnResize = function () {
|
||||
|
@ -301,24 +284,5 @@ window.Build = (function () {
|
|||
this.populateJobs(stage);
|
||||
};
|
||||
|
||||
Build.prototype.stepTrace = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const $currentTarget = $(e.currentTarget);
|
||||
$.scrollTo($currentTarget.attr('href'), {
|
||||
offset: 0,
|
||||
});
|
||||
};
|
||||
|
||||
Build.prototype.initAffixTruncatedInfo = function () {
|
||||
const offsetTop = this.$buildTrace.offset().top;
|
||||
|
||||
this.$truncatedInfo.affix({
|
||||
offset: {
|
||||
top: offsetTop,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return Build;
|
||||
})();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
$(function() {
|
||||
$('.reveal-variables').off('click').on('click', function() {
|
||||
$('.js-build').toggle().niceScroll();
|
||||
$('.js-build-variables').toggle();
|
||||
$(this).hide();
|
||||
});
|
||||
});
|
||||
|
|
97
app/assets/javascripts/close_reopen_report_toggle.js
Normal file
97
app/assets/javascripts/close_reopen_report_toggle.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
import DropLab from './droplab/drop_lab';
|
||||
import ISetter from './droplab/plugins/input_setter';
|
||||
|
||||
// Todo: Remove this when fixing issue in input_setter plugin
|
||||
const InputSetter = Object.assign({}, ISetter);
|
||||
|
||||
class CloseReopenReportToggle {
|
||||
constructor(opts = {}) {
|
||||
this.dropdownTrigger = opts.dropdownTrigger;
|
||||
this.dropdownList = opts.dropdownList;
|
||||
this.button = opts.button;
|
||||
}
|
||||
|
||||
initDroplab() {
|
||||
this.reopenItem = this.dropdownList.querySelector('.reopen-item');
|
||||
this.closeItem = this.dropdownList.querySelector('.close-item');
|
||||
|
||||
this.droplab = new DropLab();
|
||||
|
||||
const config = this.setConfig();
|
||||
|
||||
this.droplab.init(this.dropdownTrigger, this.dropdownList, [InputSetter], config);
|
||||
}
|
||||
|
||||
updateButton(isClosed) {
|
||||
this.toggleButtonType(isClosed);
|
||||
|
||||
this.button.blur();
|
||||
}
|
||||
|
||||
toggleButtonType(isClosed) {
|
||||
const [showItem, hideItem] = this.getButtonTypes(isClosed);
|
||||
|
||||
showItem.classList.remove('hidden');
|
||||
showItem.classList.add('droplab-item-selected');
|
||||
|
||||
hideItem.classList.add('hidden');
|
||||
hideItem.classList.remove('droplab-item-selected');
|
||||
|
||||
showItem.click();
|
||||
}
|
||||
|
||||
getButtonTypes(isClosed) {
|
||||
return isClosed ? [this.reopenItem, this.closeItem] : [this.closeItem, this.reopenItem];
|
||||
}
|
||||
|
||||
setDisable(shouldDisable = true) {
|
||||
if (shouldDisable) {
|
||||
this.button.setAttribute('disabled', 'true');
|
||||
this.dropdownTrigger.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
this.button.removeAttribute('disabled');
|
||||
this.dropdownTrigger.removeAttribute('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
setConfig() {
|
||||
const config = {
|
||||
InputSetter: [
|
||||
{
|
||||
input: this.button,
|
||||
valueAttribute: 'data-text',
|
||||
inputAttribute: 'data-value',
|
||||
},
|
||||
{
|
||||
input: this.button,
|
||||
valueAttribute: 'data-text',
|
||||
inputAttribute: 'title',
|
||||
},
|
||||
{
|
||||
input: this.button,
|
||||
valueAttribute: 'data-button-class',
|
||||
inputAttribute: 'class',
|
||||
},
|
||||
{
|
||||
input: this.dropdownTrigger,
|
||||
valueAttribute: 'data-toggle-class',
|
||||
inputAttribute: 'class',
|
||||
},
|
||||
{
|
||||
input: this.button,
|
||||
valueAttribute: 'data-url',
|
||||
inputAttribute: 'href',
|
||||
},
|
||||
{
|
||||
input: this.button,
|
||||
valueAttribute: 'data-method',
|
||||
inputAttribute: 'data-method',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
export default CloseReopenReportToggle;
|
|
@ -1,5 +1,8 @@
|
|||
import DropLab from './droplab/drop_lab';
|
||||
import InputSetter from './droplab/plugins/input_setter';
|
||||
import ISetter from './droplab/plugins/input_setter';
|
||||
|
||||
// Todo: Remove this when fixing issue in input_setter plugin
|
||||
const InputSetter = Object.assign({}, ISetter);
|
||||
|
||||
class CommentTypeToggle {
|
||||
constructor(opts = {}) {
|
||||
|
|
|
@ -1,29 +1,43 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import Vue from 'vue';
|
||||
import VueResource from 'vue-resource';
|
||||
import CommitPipelinesTable from './pipelines_table';
|
||||
|
||||
Vue.use(VueResource);
|
||||
import commitPipelinesTable from './pipelines_table.vue';
|
||||
|
||||
/**
|
||||
* Commits View > Pipelines Tab > Pipelines Table.
|
||||
*
|
||||
* Renders Pipelines table in pipelines tab in the commits show view.
|
||||
* Used in:
|
||||
* - Commit details View > Pipelines Tab > Pipelines Table.
|
||||
* - Merge Request details View > Pipelines Tab > Pipelines Table.
|
||||
* - New Merge Request View > Pipelines Tab > Pipelines Table.
|
||||
*/
|
||||
|
||||
// export for use in merge_request_tabs.js (TODO: remove this hack)
|
||||
const CommitPipelinesTable = Vue.extend(commitPipelinesTable);
|
||||
|
||||
// export for use in merge_request_tabs.js (TODO: remove this hack when we understand how to load
|
||||
// vue.js in merge_request_tabs.js)
|
||||
window.gl = window.gl || {};
|
||||
window.gl.CommitPipelinesTable = CommitPipelinesTable;
|
||||
|
||||
$(() => {
|
||||
gl.commits = gl.commits || {};
|
||||
gl.commits.pipelines = gl.commits.pipelines || {};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
|
||||
|
||||
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
|
||||
gl.commits.pipelines.PipelinesTableBundle = new CommitPipelinesTable().$mount();
|
||||
pipelineTableViewEl.appendChild(gl.commits.pipelines.PipelinesTableBundle.$el);
|
||||
if (pipelineTableViewEl) {
|
||||
// Update MR and Commits tabs
|
||||
pipelineTableViewEl.addEventListener('update-pipelines-count', (event) => {
|
||||
if (event.detail.pipelines &&
|
||||
event.detail.pipelines.count &&
|
||||
event.detail.pipelines.count.all) {
|
||||
const badge = document.querySelector('.js-pipelines-mr-count');
|
||||
|
||||
badge.textContent = event.detail.pipelines.count.all;
|
||||
}
|
||||
});
|
||||
|
||||
if (pipelineTableViewEl.dataset.disableInitialization === undefined) {
|
||||
const table = new CommitPipelinesTable({
|
||||
propsData: {
|
||||
endpoint: pipelineTableViewEl.dataset.endpoint,
|
||||
helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
|
||||
},
|
||||
}).$mount();
|
||||
pipelineTableViewEl.appendChild(table.$el);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,179 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Visibility from 'visibilityjs';
|
||||
import PipelinesTableComponent from '../../vue_shared/components/pipelines_table';
|
||||
import PipelinesService from '../../pipelines/services/pipelines_service';
|
||||
import PipelineStore from '../../pipelines/stores/pipelines_store';
|
||||
import eventHub from '../../pipelines/event_hub';
|
||||
import EmptyState from '../../pipelines/components/empty_state.vue';
|
||||
import ErrorState from '../../pipelines/components/error_state.vue';
|
||||
import '../../lib/utils/common_utils';
|
||||
import '../../vue_shared/vue_resource_interceptor';
|
||||
import Poll from '../../lib/utils/poll';
|
||||
|
||||
/**
|
||||
*
|
||||
* Uses `pipelines-table-component` to render Pipelines table with an API call.
|
||||
* Endpoint is provided in HTML and passed as `endpoint`.
|
||||
* We need a store to store the received environemnts.
|
||||
* We need a service to communicate with the server.
|
||||
*
|
||||
* Necessary SVG in the table are provided as props. This should be refactored
|
||||
* as soon as we have Webpack and can load them directly into JS files.
|
||||
*/
|
||||
|
||||
export default Vue.component('pipelines-table', {
|
||||
|
||||
components: {
|
||||
'pipelines-table-component': PipelinesTableComponent,
|
||||
'error-state': ErrorState,
|
||||
'empty-state': EmptyState,
|
||||
},
|
||||
|
||||
/**
|
||||
* Accesses the DOM to provide the needed data.
|
||||
* Returns the necessary props to render `pipelines-table-component` component.
|
||||
*
|
||||
* @return {Object}
|
||||
*/
|
||||
data() {
|
||||
const store = new PipelineStore();
|
||||
|
||||
return {
|
||||
endpoint: null,
|
||||
helpPagePath: null,
|
||||
store,
|
||||
state: store.state,
|
||||
isLoading: false,
|
||||
hasError: false,
|
||||
isMakingRequest: false,
|
||||
updateGraphDropdown: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
shouldRenderErrorState() {
|
||||
return this.hasError && !this.isLoading;
|
||||
},
|
||||
|
||||
shouldRenderEmptyState() {
|
||||
return !this.state.pipelines.length &&
|
||||
!this.isLoading &&
|
||||
!this.hasError;
|
||||
},
|
||||
|
||||
shouldRenderTable() {
|
||||
return !this.isLoading &&
|
||||
this.state.pipelines.length > 0 &&
|
||||
!this.hasError;
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* When the component is about to be mounted, tell the service to fetch the data
|
||||
*
|
||||
* A request to fetch the pipelines will be made.
|
||||
* In case of a successfull response we will store the data in the provided
|
||||
* store, in case of a failed response we need to warn the user.
|
||||
*
|
||||
*/
|
||||
beforeMount() {
|
||||
const element = document.querySelector('#commit-pipeline-table-view');
|
||||
|
||||
this.endpoint = element.dataset.endpoint;
|
||||
this.helpPagePath = element.dataset.helpPagePath;
|
||||
this.service = new PipelinesService(this.endpoint);
|
||||
|
||||
this.poll = new Poll({
|
||||
resource: this.service,
|
||||
method: 'getPipelines',
|
||||
successCallback: this.successCallback,
|
||||
errorCallback: this.errorCallback,
|
||||
notificationCallback: this.setIsMakingRequest,
|
||||
});
|
||||
|
||||
if (!Visibility.hidden()) {
|
||||
this.isLoading = true;
|
||||
this.poll.makeRequest();
|
||||
}
|
||||
|
||||
Visibility.change(() => {
|
||||
if (!Visibility.hidden()) {
|
||||
this.poll.restart();
|
||||
} else {
|
||||
this.poll.stop();
|
||||
}
|
||||
});
|
||||
|
||||
eventHub.$on('refreshPipelines', this.fetchPipelines);
|
||||
},
|
||||
|
||||
beforeDestroyed() {
|
||||
eventHub.$off('refreshPipelines');
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.poll.stop();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchPipelines() {
|
||||
this.isLoading = true;
|
||||
|
||||
return this.service.getPipelines()
|
||||
.then(response => this.successCallback(response))
|
||||
.catch(() => this.errorCallback());
|
||||
},
|
||||
|
||||
successCallback(resp) {
|
||||
const response = resp.json();
|
||||
|
||||
// depending of the endpoint the response can either bring a `pipelines` key or not.
|
||||
const pipelines = response.pipelines || response;
|
||||
this.store.storePipelines(pipelines);
|
||||
this.isLoading = false;
|
||||
this.updateGraphDropdown = true;
|
||||
},
|
||||
|
||||
errorCallback() {
|
||||
this.hasError = true;
|
||||
this.isLoading = false;
|
||||
this.updateGraphDropdown = false;
|
||||
},
|
||||
|
||||
setIsMakingRequest(isMakingRequest) {
|
||||
this.isMakingRequest = isMakingRequest;
|
||||
|
||||
if (isMakingRequest) {
|
||||
this.updateGraphDropdown = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
template: `
|
||||
<div class="content-list pipelines">
|
||||
<div
|
||||
class="realtime-loading"
|
||||
v-if="isLoading">
|
||||
<i
|
||||
class="fa fa-spinner fa-spin"
|
||||
aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<empty-state
|
||||
v-if="shouldRenderEmptyState"
|
||||
:help-page-path="helpPagePath" />
|
||||
|
||||
<error-state v-if="shouldRenderErrorState" />
|
||||
|
||||
<div
|
||||
class="table-holder"
|
||||
v-if="shouldRenderTable">
|
||||
<pipelines-table-component
|
||||
:pipelines="state.pipelines"
|
||||
:service="service"
|
||||
:update-graph-dropdown="updateGraphDropdown"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
101
app/assets/javascripts/commit/pipelines/pipelines_table.vue
Normal file
101
app/assets/javascripts/commit/pipelines/pipelines_table.vue
Normal file
|
@ -0,0 +1,101 @@
|
|||
<script>
|
||||
import PipelinesService from '../../pipelines/services/pipelines_service';
|
||||
import PipelineStore from '../../pipelines/stores/pipelines_store';
|
||||
import pipelinesMixin from '../../pipelines/mixins/pipelines';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
endpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
helpPagePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
mixins: [
|
||||
pipelinesMixin,
|
||||
],
|
||||
|
||||
data() {
|
||||
const store = new PipelineStore();
|
||||
|
||||
return {
|
||||
store,
|
||||
state: store.state,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Empty state is only rendered if after the first request we receive no pipelines.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
shouldRenderEmptyState() {
|
||||
return !this.state.pipelines.length &&
|
||||
!this.isLoading &&
|
||||
this.hasMadeRequest &&
|
||||
!this.hasError;
|
||||
},
|
||||
|
||||
shouldRenderTable() {
|
||||
return !this.isLoading &&
|
||||
this.state.pipelines.length > 0 &&
|
||||
!this.hasError;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.service = new PipelinesService(this.endpoint);
|
||||
},
|
||||
methods: {
|
||||
successCallback(resp) {
|
||||
return resp.json().then((response) => {
|
||||
// depending of the endpoint the response can either bring a `pipelines` key or not.
|
||||
const pipelines = response.pipelines || response;
|
||||
this.setCommonData(pipelines);
|
||||
|
||||
const updatePipelinesEvent = new CustomEvent('update-pipelines-count', {
|
||||
detail: {
|
||||
pipelines: response,
|
||||
},
|
||||
});
|
||||
|
||||
// notifiy to update the count in tabs
|
||||
if (this.$el.parentElement) {
|
||||
this.$el.parentElement.dispatchEvent(updatePipelinesEvent);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="content-list pipelines">
|
||||
|
||||
<loading-icon
|
||||
label="Loading pipelines"
|
||||
size="3"
|
||||
v-if="isLoading"
|
||||
/>
|
||||
|
||||
<empty-state
|
||||
v-if="shouldRenderEmptyState"
|
||||
:help-page-path="helpPagePath"
|
||||
/>
|
||||
|
||||
<error-state
|
||||
v-if="shouldRenderErrorState"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="table-holder"
|
||||
v-if="shouldRenderTable">
|
||||
<pipelines-table-component
|
||||
:pipelines="state.pipelines"
|
||||
:update-graph-dropdown="updateGraphDropdown"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -7,6 +7,8 @@ window.CommitsList = (function() {
|
|||
CommitsList.timer = null;
|
||||
|
||||
CommitsList.init = function(limit) {
|
||||
this.$contentList = $('.content_list');
|
||||
|
||||
$("body").on("click", ".day-commits-table li.commit", function(e) {
|
||||
if (e.target.nodeName !== "A") {
|
||||
location.href = $(this).attr("url");
|
||||
|
@ -14,9 +16,9 @@ window.CommitsList = (function() {
|
|||
return false;
|
||||
}
|
||||
});
|
||||
Pager.init(limit, false, false, function() {
|
||||
gl.utils.localTimeAgo($('.js-timeago'));
|
||||
});
|
||||
|
||||
Pager.init(parseInt(limit, 10), false, false, this.processCommits);
|
||||
|
||||
this.content = $("#commits-list");
|
||||
this.searchField = $("#commits-search");
|
||||
this.lastSearch = this.searchField.val();
|
||||
|
@ -62,5 +64,34 @@ window.CommitsList = (function() {
|
|||
});
|
||||
};
|
||||
|
||||
// Prepare loaded data.
|
||||
CommitsList.processCommits = (data) => {
|
||||
let processedData = data;
|
||||
const $processedData = $(processedData);
|
||||
const $commitsHeadersLast = CommitsList.$contentList.find('li.js-commit-header').last();
|
||||
const lastShownDay = $commitsHeadersLast.data('day');
|
||||
const $loadedCommitsHeadersFirst = $processedData.filter('li.js-commit-header').first();
|
||||
const loadedShownDayFirst = $loadedCommitsHeadersFirst.data('day');
|
||||
let commitsCount;
|
||||
|
||||
// If commits headers show the same date,
|
||||
// remove the last header and change the previous one.
|
||||
if (lastShownDay === loadedShownDayFirst) {
|
||||
// Last shown commits count under the last commits header.
|
||||
commitsCount = $commitsHeadersLast.nextUntil('li.js-commit-header').find('li.commit').length;
|
||||
|
||||
// Remove duplicate of commits header.
|
||||
processedData = $processedData.not(`li.js-commit-header[data-day="${loadedShownDayFirst}"]`);
|
||||
|
||||
// Update commits count in the previous commits header.
|
||||
commitsCount += Number($(processedData).nextUntil('li.js-commit-header').first().find('li.commit').length);
|
||||
$commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${gl.text.pluralize('commit', commitsCount)}`);
|
||||
}
|
||||
|
||||
gl.utils.localTimeAgo($processedData.find('.js-timeago'));
|
||||
|
||||
return processedData;
|
||||
};
|
||||
|
||||
return CommitsList;
|
||||
})();
|
||||
|
|
2
app/assets/javascripts/commons/bootstrap.js
vendored
2
app/assets/javascripts/commons/bootstrap.js
vendored
|
@ -3,11 +3,13 @@ import $ from 'jquery';
|
|||
// bootstrap jQuery plugins
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/affix';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/alert';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/button';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/dropdown';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/modal';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/tab';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/transition';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip';
|
||||
import 'bootstrap-sass/assets/javascripts/bootstrap/popover';
|
||||
|
||||
// custom jQuery functions
|
||||
$.fn.extend({
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'underscore';
|
||||
import './polyfills';
|
||||
import './jquery';
|
||||
import './bootstrap';
|
||||
|
|
1
app/assets/javascripts/commons/jquery.js
vendored
1
app/assets/javascripts/commons/jquery.js
vendored
|
@ -6,6 +6,5 @@ import 'vendor/jquery.endless-scroll';
|
|||
import 'vendor/jquery.caret';
|
||||
import 'vendor/jquery.atwho';
|
||||
import 'vendor/jquery.scrollTo';
|
||||
import 'vendor/jquery.nicescroll';
|
||||
import 'vendor/jquery.waitforimages';
|
||||
import 'select2/select2';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// ECMAScript polyfills
|
||||
import 'core-js/fn/array/find';
|
||||
import 'core-js/fn/array/find-index';
|
||||
import 'core-js/fn/array/from';
|
||||
import 'core-js/fn/array/includes';
|
||||
import 'core-js/fn/object/assign';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
|
||||
|
||||
require('./lib/utils/common_utils');
|
||||
import _ from 'underscore';
|
||||
import './lib/utils/common_utils';
|
||||
import { placeholderImage } from './lazy_loader';
|
||||
|
||||
const gfmRules = {
|
||||
// The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert
|
||||
|
@ -18,12 +19,12 @@ const gfmRules = {
|
|||
},
|
||||
},
|
||||
TaskListFilter: {
|
||||
'input[type=checkbox].task-list-item-checkbox'(el, text) {
|
||||
'input[type=checkbox].task-list-item-checkbox'(el) {
|
||||
return `[${el.checked ? 'x' : ' '}]`;
|
||||
},
|
||||
},
|
||||
ReferenceFilter: {
|
||||
'.tooltip'(el, text) {
|
||||
'.tooltip'(el) {
|
||||
return '';
|
||||
},
|
||||
'a.gfm:not([data-link=true])'(el, text) {
|
||||
|
@ -39,15 +40,15 @@ const gfmRules = {
|
|||
},
|
||||
},
|
||||
TableOfContentsFilter: {
|
||||
'ul.section-nav'(el, text) {
|
||||
'ul.section-nav'(el) {
|
||||
return '[[_TOC_]]';
|
||||
},
|
||||
},
|
||||
EmojiFilter: {
|
||||
'img.emoji'(el, text) {
|
||||
'img.emoji'(el) {
|
||||
return el.getAttribute('alt');
|
||||
},
|
||||
'gl-emoji'(el, text) {
|
||||
'gl-emoji'(el) {
|
||||
return `:${el.getAttribute('data-name')}:`;
|
||||
},
|
||||
},
|
||||
|
@ -56,14 +57,19 @@ const gfmRules = {
|
|||
return text;
|
||||
},
|
||||
},
|
||||
ImageLazyLoadFilter: {
|
||||
'img'(el, text) {
|
||||
return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
|
||||
},
|
||||
},
|
||||
VideoLinkFilter: {
|
||||
'.video-container'(el, text) {
|
||||
'.video-container'(el) {
|
||||
const videoEl = el.querySelector('video');
|
||||
if (!videoEl) return false;
|
||||
|
||||
return CopyAsGFM.nodeToGFM(videoEl);
|
||||
},
|
||||
'video'(el, text) {
|
||||
'video'(el) {
|
||||
return `![${el.dataset.title}](${el.getAttribute('src')})`;
|
||||
},
|
||||
},
|
||||
|
@ -74,19 +80,19 @@ const gfmRules = {
|
|||
'code.code.math[data-math-style=inline]'(el, text) {
|
||||
return `$\`${text}\`$`;
|
||||
},
|
||||
'span.katex-display span.katex-mathml'(el, text) {
|
||||
'span.katex-display span.katex-mathml'(el) {
|
||||
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
|
||||
if (!mathAnnotation) return false;
|
||||
|
||||
return `\`\`\`math\n${CopyAsGFM.nodeToGFM(mathAnnotation)}\n\`\`\``;
|
||||
},
|
||||
'span.katex-mathml'(el, text) {
|
||||
'span.katex-mathml'(el) {
|
||||
const mathAnnotation = el.querySelector('annotation[encoding="application/x-tex"]');
|
||||
if (!mathAnnotation) return false;
|
||||
|
||||
return `$\`${CopyAsGFM.nodeToGFM(mathAnnotation)}\`$`;
|
||||
},
|
||||
'span.katex-html'(el, text) {
|
||||
'span.katex-html'(el) {
|
||||
// We don't want to include the content of this element in the copied text.
|
||||
return '';
|
||||
},
|
||||
|
@ -95,7 +101,7 @@ const gfmRules = {
|
|||
},
|
||||
},
|
||||
SanitizationFilter: {
|
||||
'a[name]:not([href]):empty'(el, text) {
|
||||
'a[name]:not([href]):empty'(el) {
|
||||
return el.outerHTML;
|
||||
},
|
||||
'dl'(el, text) {
|
||||
|
@ -143,7 +149,7 @@ const gfmRules = {
|
|||
},
|
||||
},
|
||||
MarkdownFilter: {
|
||||
'br'(el, text) {
|
||||
'br'(el) {
|
||||
// Two spaces at the end of a line are turned into a BR
|
||||
return ' ';
|
||||
},
|
||||
|
@ -162,8 +168,10 @@ const gfmRules = {
|
|||
'blockquote'(el, text) {
|
||||
return text.trim().split('\n').map(s => `> ${s}`.trim()).join('\n');
|
||||
},
|
||||
'img'(el, text) {
|
||||
return `![${el.getAttribute('alt')}](${el.getAttribute('src')})`;
|
||||
'img'(el) {
|
||||
const imageSrc = el.src;
|
||||
const imageUrl = imageSrc && imageSrc !== placeholderImage ? imageSrc : (el.dataset.src || '');
|
||||
return `![${el.getAttribute('alt')}](${imageUrl})`;
|
||||
},
|
||||
'a.anchor'(el, text) {
|
||||
// Don't render a Markdown link for the anchor link inside a heading
|
||||
|
@ -222,10 +230,10 @@ const gfmRules = {
|
|||
'sup'(el, text) {
|
||||
return `^${text}`;
|
||||
},
|
||||
'hr'(el, text) {
|
||||
'hr'(el) {
|
||||
return '-----';
|
||||
},
|
||||
'table'(el, text) {
|
||||
'table'(el) {
|
||||
const theadEl = el.querySelector('thead');
|
||||
const tbodyEl = el.querySelector('tbody');
|
||||
if (!theadEl || !tbodyEl) return false;
|
||||
|
@ -233,11 +241,11 @@ const gfmRules = {
|
|||
const theadText = CopyAsGFM.nodeToGFM(theadEl);
|
||||
const tbodyText = CopyAsGFM.nodeToGFM(tbodyEl);
|
||||
|
||||
return theadText + tbodyText;
|
||||
return [theadText, tbodyText].join('\n');
|
||||
},
|
||||
'thead'(el, text) {
|
||||
const cells = _.map(el.querySelectorAll('th'), (cell) => {
|
||||
let chars = CopyAsGFM.nodeToGFM(cell).trim().length + 2;
|
||||
let chars = CopyAsGFM.nodeToGFM(cell).length + 2;
|
||||
|
||||
let before = '';
|
||||
let after = '';
|
||||
|
@ -262,10 +270,15 @@ const gfmRules = {
|
|||
return before + middle + after;
|
||||
});
|
||||
|
||||
return `${text}|${cells.join('|')}|`;
|
||||
const separatorRow = `|${cells.join('|')}|`;
|
||||
|
||||
return [text, separatorRow].join('\n');
|
||||
},
|
||||
'tr'(el, text) {
|
||||
const cells = _.map(el.querySelectorAll('td, th'), cell => CopyAsGFM.nodeToGFM(cell).trim());
|
||||
'tr'(el) {
|
||||
const cellEls = el.querySelectorAll('td, th');
|
||||
if (cellEls.length === 0) return false;
|
||||
|
||||
const cells = _.map(cellEls, cell => CopyAsGFM.nodeToGFM(cell));
|
||||
return `| ${cells.join(' | ')} |`;
|
||||
},
|
||||
},
|
||||
|
@ -273,12 +286,12 @@ const gfmRules = {
|
|||
|
||||
class CopyAsGFM {
|
||||
constructor() {
|
||||
$(document).on('copy', '.md, .wiki', (e) => { this.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
|
||||
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { this.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
|
||||
$(document).on('paste', '.js-gfm-input', this.pasteGFM.bind(this));
|
||||
$(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
|
||||
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
|
||||
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
|
||||
}
|
||||
|
||||
copyAsGFM(e, transformer) {
|
||||
static copyAsGFM(e, transformer) {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
if (!clipboardData) return;
|
||||
|
||||
|
@ -292,26 +305,59 @@ class CopyAsGFM {
|
|||
e.stopPropagation();
|
||||
|
||||
clipboardData.setData('text/plain', el.textContent);
|
||||
clipboardData.setData('text/x-gfm', CopyAsGFM.nodeToGFM(el));
|
||||
clipboardData.setData('text/x-gfm', this.nodeToGFM(el));
|
||||
}
|
||||
|
||||
pasteGFM(e) {
|
||||
static pasteGFM(e) {
|
||||
const clipboardData = e.originalEvent.clipboardData;
|
||||
if (!clipboardData) return;
|
||||
|
||||
const text = clipboardData.getData('text/plain');
|
||||
const gfm = clipboardData.getData('text/x-gfm');
|
||||
if (!gfm) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
window.gl.utils.insertText(e.target, gfm);
|
||||
window.gl.utils.insertText(e.target, (textBefore, textAfter) => {
|
||||
// If the text before the cursor contains an odd number of backticks,
|
||||
// we are either inside an inline code span that starts with 1 backtick
|
||||
// or a code block that starts with 3 backticks.
|
||||
// This logic still holds when there are one or more _closed_ code spans
|
||||
// or blocks that will have 2 or 6 backticks.
|
||||
// This will break down when the actual code block contains an uneven
|
||||
// number of backticks, but this is a rare edge case.
|
||||
const backtickMatch = textBefore.match(/`/g);
|
||||
const insideCodeBlock = backtickMatch && (backtickMatch.length % 2) === 1;
|
||||
|
||||
if (insideCodeBlock) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return gfm;
|
||||
});
|
||||
}
|
||||
|
||||
static transformGFMSelection(documentFragment) {
|
||||
// If the documentFragment contains more than just Markdown, don't copy as GFM.
|
||||
if (documentFragment.querySelector('.md, .wiki')) return null;
|
||||
const gfmEls = documentFragment.querySelectorAll('.md, .wiki');
|
||||
switch (gfmEls.length) {
|
||||
case 0: {
|
||||
return documentFragment;
|
||||
}
|
||||
case 1: {
|
||||
return gfmEls[0];
|
||||
}
|
||||
default: {
|
||||
const allGfmEl = document.createElement('div');
|
||||
|
||||
return documentFragment;
|
||||
for (let i = 0; i < gfmEls.length; i += 1) {
|
||||
const lineEl = gfmEls[i];
|
||||
allGfmEl.appendChild(lineEl);
|
||||
allGfmEl.appendChild(document.createTextNode('\n\n'));
|
||||
}
|
||||
|
||||
return allGfmEl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static transformCodeSelection(documentFragment) {
|
||||
|
@ -343,7 +389,7 @@ class CopyAsGFM {
|
|||
return codeEl;
|
||||
}
|
||||
|
||||
static nodeToGFM(node) {
|
||||
static nodeToGFM(node, respectWhitespaceParam = false) {
|
||||
if (node.nodeType === Node.COMMENT_NODE) {
|
||||
return '';
|
||||
}
|
||||
|
@ -352,7 +398,9 @@ class CopyAsGFM {
|
|||
return node.textContent;
|
||||
}
|
||||
|
||||
const text = this.innerGFM(node);
|
||||
const respectWhitespace = respectWhitespaceParam || (node.nodeName === 'PRE' || node.nodeName === 'CODE');
|
||||
|
||||
const text = this.innerGFM(node, respectWhitespace);
|
||||
|
||||
if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
||||
return text;
|
||||
|
@ -366,7 +414,17 @@ class CopyAsGFM {
|
|||
|
||||
if (!window.gl.utils.nodeMatchesSelector(node, selector)) continue;
|
||||
|
||||
const result = func(node, text);
|
||||
let result;
|
||||
if (func.length === 2) {
|
||||
// if `func` takes 2 arguments, it depends on text.
|
||||
// if there is no text, we don't need to generate GFM for this node.
|
||||
if (text.length === 0) continue;
|
||||
|
||||
result = func(node, text);
|
||||
} else {
|
||||
result = func(node);
|
||||
}
|
||||
|
||||
if (result === false) continue;
|
||||
|
||||
return result;
|
||||
|
@ -376,7 +434,7 @@ class CopyAsGFM {
|
|||
return text;
|
||||
}
|
||||
|
||||
static innerGFM(parentNode) {
|
||||
static innerGFM(parentNode, respectWhitespace = false) {
|
||||
const nodes = parentNode.childNodes;
|
||||
|
||||
const clonedParentNode = parentNode.cloneNode(true);
|
||||
|
@ -386,13 +444,19 @@ class CopyAsGFM {
|
|||
const node = nodes[i];
|
||||
const clonedNode = clonedNodes[i];
|
||||
|
||||
const text = this.nodeToGFM(node);
|
||||
const text = this.nodeToGFM(node, respectWhitespace);
|
||||
|
||||
// `clonedNode.replaceWith(text)` is not yet widely supported
|
||||
clonedNode.parentNode.replaceChild(document.createTextNode(text), clonedNode);
|
||||
}
|
||||
|
||||
return clonedParentNode.innerText || clonedParentNode.textContent;
|
||||
let nodeText = clonedParentNode.innerText || clonedParentNode.textContent;
|
||||
|
||||
if (!respectWhitespace) {
|
||||
nodeText = nodeText.trim();
|
||||
}
|
||||
|
||||
return nodeText;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-param-reassign, wrap-iife, max-len */
|
||||
/* global Api */
|
||||
import Api from './api';
|
||||
|
||||
class CreateLabelDropdown {
|
||||
constructor ($el, namespacePath, projectPath) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import Vue from 'vue';
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
|
||||
const global = window.gl || (window.gl = {});
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
@ -10,6 +11,9 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
components: {
|
||||
userAvatarImage,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -19,7 +23,8 @@ global.cycleAnalytics.StageCodeComponent = Vue.extend({
|
|||
<ul class="stage-event-list">
|
||||
<li v-for="mergeRequest in items" class="stage-event-item">
|
||||
<div class="item-details">
|
||||
<img class="avatar" :src="mergeRequest.author.avatarUrl">
|
||||
<!-- FIXME: Pass an alt attribute here for accessibility -->
|
||||
<user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
|
||||
<h5 class="item-title merge-merquest-title">
|
||||
<a :href="mergeRequest.url">
|
||||
{{ mergeRequest.title }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import Vue from 'vue';
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
|
||||
const global = window.gl || (window.gl = {});
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
@ -10,6 +10,9 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
components: {
|
||||
userAvatarImage,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -19,7 +22,8 @@ global.cycleAnalytics.StageIssueComponent = Vue.extend({
|
|||
<ul class="stage-event-list">
|
||||
<li v-for="issue in items" class="stage-event-item">
|
||||
<div class="item-details">
|
||||
<img class="avatar" :src="issue.author.avatarUrl">
|
||||
<!-- FIXME: Pass an alt attribute here for accessibility -->
|
||||
<user-avatar-image :img-src="issue.author.avatarUrl"/>
|
||||
<h5 class="item-title issue-title">
|
||||
<a class="issue-title" :href="issue.url">
|
||||
{{ issue.title }}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
import Vue from 'vue';
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
import iconCommit from '../svg/icon_commit.svg';
|
||||
|
||||
const global = window.gl || (window.gl = {});
|
||||
|
@ -10,11 +11,12 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
|
||||
components: {
|
||||
userAvatarImage,
|
||||
},
|
||||
data() {
|
||||
return { iconCommit };
|
||||
},
|
||||
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -24,7 +26,8 @@ global.cycleAnalytics.StagePlanComponent = Vue.extend({
|
|||
<ul class="stage-event-list">
|
||||
<li v-for="commit in items" class="stage-event-item">
|
||||
<div class="item-details item-conmmit-component">
|
||||
<img class="avatar" :src="commit.author.avatarUrl">
|
||||
<!-- FIXME: Pass an alt attribute here for accessibility -->
|
||||
<user-avatar-image :img-src="commit.author.avatarUrl"/>
|
||||
<h5 class="item-title commit-title">
|
||||
<a :href="commit.commitUrl">
|
||||
{{ commit.title }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import Vue from 'vue';
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
|
||||
const global = window.gl || (window.gl = {});
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
@ -10,6 +10,9 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
components: {
|
||||
userAvatarImage,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -19,7 +22,8 @@ global.cycleAnalytics.StageProductionComponent = Vue.extend({
|
|||
<ul class="stage-event-list">
|
||||
<li v-for="issue in items" class="stage-event-item">
|
||||
<div class="item-details">
|
||||
<img class="avatar" :src="issue.author.avatarUrl">
|
||||
<!-- FIXME: Pass an alt attribute here for accessibility -->
|
||||
<user-avatar-image :img-src="issue.author.avatarUrl"/>
|
||||
<h5 class="item-title issue-title">
|
||||
<a class="issue-title" :href="issue.url">
|
||||
{{ issue.title }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
|
||||
import Vue from 'vue';
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
|
||||
const global = window.gl || (window.gl = {});
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
@ -10,6 +10,9 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
|
|||
items: Array,
|
||||
stage: Object,
|
||||
},
|
||||
components: {
|
||||
userAvatarImage,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -19,7 +22,8 @@ global.cycleAnalytics.StageReviewComponent = Vue.extend({
|
|||
<ul class="stage-event-list">
|
||||
<li v-for="mergeRequest in items" class="stage-event-item">
|
||||
<div class="item-details">
|
||||
<img class="avatar" :src="mergeRequest.author.avatarUrl">
|
||||
<!-- FIXME: Pass an alt attribute here for accessibility -->
|
||||
<user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
|
||||
<h5 class="item-title merge-merquest-title">
|
||||
<a :href="mergeRequest.url">
|
||||
{{ mergeRequest.title }}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
import Vue from 'vue';
|
||||
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||
import iconBranch from '../svg/icon_branch.svg';
|
||||
|
||||
const global = window.gl || (window.gl = {});
|
||||
|
@ -13,6 +14,9 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
|
|||
data() {
|
||||
return { iconBranch };
|
||||
},
|
||||
components: {
|
||||
userAvatarImage,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<div class="events-description">
|
||||
|
@ -22,13 +26,14 @@ global.cycleAnalytics.StageStagingComponent = Vue.extend({
|
|||
<ul class="stage-event-list">
|
||||
<li v-for="build in items" class="stage-event-item item-build-component">
|
||||
<div class="item-details">
|
||||
<img class="avatar" :src="build.author.avatarUrl">
|
||||
<!-- FIXME: Pass an alt attribute here for accessibility -->
|
||||
<user-avatar-image :img-src="build.author.avatarUrl"/>
|
||||
<h5 class="item-title">
|
||||
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
|
||||
<i class="fa fa-code-fork"></i>
|
||||
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
|
||||
<a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a>
|
||||
<span class="icon-branch">${iconBranch}</span>
|
||||
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
|
||||
<a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a>
|
||||
</h5>
|
||||
<span>
|
||||
<a :href="build.url" class="build-date">{{ build.date }}</a>
|
||||
|
|
|
@ -29,9 +29,9 @@ global.cycleAnalytics.StageTestComponent = Vue.extend({
|
|||
·
|
||||
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
|
||||
<i class="fa fa-code-fork"></i>
|
||||
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
|
||||
<a :href="build.branch.url" class="ref-name">{{ build.branch.name }}</a>
|
||||
<span class="icon-branch">${iconBranch}</span>
|
||||
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
|
||||
<a :href="build.commitUrl" class="commit-sha">{{ build.shortSha }}</a>
|
||||
</h5>
|
||||
<span>
|
||||
<a :href="build.url" class="issue-date">
|
||||
|
|
|
@ -4,18 +4,16 @@ import Vue from 'vue';
|
|||
import Cookies from 'js-cookie';
|
||||
import Translate from '../vue_shared/translate';
|
||||
import LimitWarningComponent from './components/limit_warning_component';
|
||||
|
||||
require('./components/stage_code_component');
|
||||
require('./components/stage_issue_component');
|
||||
require('./components/stage_plan_component');
|
||||
require('./components/stage_production_component');
|
||||
require('./components/stage_review_component');
|
||||
require('./components/stage_staging_component');
|
||||
require('./components/stage_test_component');
|
||||
require('./components/total_time_component');
|
||||
require('./cycle_analytics_service');
|
||||
require('./cycle_analytics_store');
|
||||
require('./default_event_objects');
|
||||
import './components/stage_code_component';
|
||||
import './components/stage_issue_component';
|
||||
import './components/stage_plan_component';
|
||||
import './components/stage_production_component';
|
||||
import './components/stage_review_component';
|
||||
import './components/stage_staging_component';
|
||||
import './components/stage_test_component';
|
||||
import './components/total_time_component';
|
||||
import './cycle_analytics_service';
|
||||
import './cycle_analytics_store';
|
||||
|
||||
Vue.use(Translate);
|
||||
|
||||
|
@ -94,7 +92,7 @@ $(() => {
|
|||
});
|
||||
},
|
||||
selectDefaultStage() {
|
||||
const stage = this.state.stages.first();
|
||||
const stage = this.state.stages[0];
|
||||
this.selectStage(stage);
|
||||
},
|
||||
selectStage(stage) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
import { __ } from '../locale';
|
||||
|
||||
require('../lib/utils/text_utility');
|
||||
const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
|
||||
import { __ } from '../locale';
|
||||
import '../lib/utils/text_utility';
|
||||
import DEFAULT_EVENT_OBJECTS from './default_event_objects';
|
||||
|
||||
const global = window.gl || (window.gl = {});
|
||||
global.cycleAnalytics = global.cycleAnalytics || {};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
issue: {
|
||||
created_at: '',
|
||||
url: '',
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import eventHub from '../eventhub';
|
||||
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
@ -22,6 +23,11 @@
|
|||
default: 'btn-default',
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
loadingIcon,
|
||||
},
|
||||
|
||||
methods: {
|
||||
doAction() {
|
||||
this.isLoading = true;
|
||||
|
@ -44,11 +50,6 @@
|
|||
:disabled="isLoading"
|
||||
@click="doAction">
|
||||
{{ text }}
|
||||
<i
|
||||
v-if="isLoading"
|
||||
class="fa fa-spinner fa-spin"
|
||||
aria-hidden="true"
|
||||
aria-label="Loading">
|
||||
</i>
|
||||
<loading-icon v-if="isLoading" />
|
||||
</button>
|
||||
</template>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue