Update upstream source from tag 'upstream/12.1.11'
Update to upstream version '12.1.11'
with Debian dir 6183fb855f
This commit is contained in:
commit
da6367271f
3552 changed files with 62667 additions and 52118 deletions
|
@ -5,6 +5,7 @@ globals:
|
||||||
gl: false
|
gl: false
|
||||||
gon: false
|
gon: false
|
||||||
localStorage: false
|
localStorage: false
|
||||||
|
IS_EE: false
|
||||||
plugins:
|
plugins:
|
||||||
- import
|
- import
|
||||||
- html
|
- html
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.29"
|
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.29"
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
|
|
||||||
RAILS_ENV: "test"
|
RAILS_ENV: "test"
|
||||||
NODE_ENV: "test"
|
NODE_ENV: "test"
|
||||||
SIMPLECOV: "true"
|
SIMPLECOV: "true"
|
||||||
|
@ -37,6 +36,7 @@ include:
|
||||||
- local: .gitlab/ci/cng.gitlab-ci.yml
|
- local: .gitlab/ci/cng.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/docs.gitlab-ci.yml
|
- local: .gitlab/ci/docs.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/frontend.gitlab-ci.yml
|
- local: .gitlab/ci/frontend.gitlab-ci.yml
|
||||||
|
- local: .gitlab/ci/memory.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/pages.gitlab-ci.yml
|
- local: .gitlab/ci/pages.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/qa.gitlab-ci.yml
|
- local: .gitlab/ci/qa.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/reports.gitlab-ci.yml
|
- local: .gitlab/ci/reports.gitlab-ci.yml
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
/doc/ @axil @marcia @eread @mikelewis
|
/doc/ @axil @marcia @eread @mikelewis
|
||||||
|
|
||||||
# Frontend maintainers should see everything in `app/assets/`
|
# Frontend maintainers should see everything in `app/assets/`
|
||||||
app/assets/ @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann @kushalpandya
|
app/assets/ @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann @kushalpandya @pslaughter
|
||||||
*.scss @annabeldunstone @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann @kushalpandya
|
*.scss @annabeldunstone @ClemMakesApps @fatihacet @filipa @iamphill @mikegreiling @timzallmann @kushalpandya @pslaughter
|
||||||
|
|
||||||
# Someone from the database team should review changes in `db/`
|
# Someone from the database team should review changes in `db/`
|
||||||
db/ @abrandl @NikolayS
|
db/ @abrandl @NikolayS
|
||||||
|
@ -19,3 +19,5 @@ db/ @abrandl @NikolayS
|
||||||
/lib/gitlab/ci/templates/ @nolith @zj
|
/lib/gitlab/ci/templates/ @nolith @zj
|
||||||
/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
|
/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
|
||||||
/lib/gitlab/ci/templates/Security/ @plafoucriere @gonzoyumo @twoodham
|
/lib/gitlab/ci/templates/Security/ @plafoucriere @gonzoyumo @twoodham
|
||||||
|
/ee/app/models/project_alias.rb @patrickbajao
|
||||||
|
/ee/lib/api/project_aliases.rb @patrickbajao
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
# Trigger a manual docs build in gitlab-docs only on non docs-only branches.
|
# Trigger a manual docs build in gitlab-docs only on non docs-only branches.
|
||||||
# Useful to preview the docs changes live.
|
# Useful to preview the docs changes live.
|
||||||
review-docs-deploy-manual:
|
review-docs-deploy-manual:
|
||||||
<<: *review-docs
|
extends:
|
||||||
|
- .review-docs
|
||||||
|
- .no-docs-and-no-qa
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- gem install gitlab --no-document
|
- gem install gitlab --no-document
|
||||||
|
@ -21,9 +23,6 @@ review-docs-deploy-manual:
|
||||||
only:
|
only:
|
||||||
- branches@gitlab-org/gitlab-ce
|
- branches@gitlab-org/gitlab-ce
|
||||||
- branches@gitlab-org/gitlab-ee
|
- branches@gitlab-org/gitlab-ee
|
||||||
except:
|
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
|
||||||
|
|
||||||
# Always trigger a docs build in gitlab-docs only on docs-only branches.
|
# Always trigger a docs build in gitlab-docs only on docs-only branches.
|
||||||
# Useful to preview the docs changes live.
|
# Useful to preview the docs changes live.
|
||||||
|
@ -66,6 +65,8 @@ docs lint:
|
||||||
- scripts/lint-changelog-yaml
|
- scripts/lint-changelog-yaml
|
||||||
- mv doc/ /tmp/gitlab-docs/content/$DOCS_GITLAB_REPO_SUFFIX
|
- mv doc/ /tmp/gitlab-docs/content/$DOCS_GITLAB_REPO_SUFFIX
|
||||||
- cd /tmp/gitlab-docs
|
- cd /tmp/gitlab-docs
|
||||||
|
# Lint Markdown
|
||||||
|
- bundle exec mdl content/$DOCS_GITLAB_REPO_SUFFIX -c $CI_PROJECT_DIR/.mdlrc
|
||||||
# Build HTML from Markdown
|
# Build HTML from Markdown
|
||||||
- bundle exec nanoc
|
- bundle exec nanoc
|
||||||
# Check the internal links
|
# Check the internal links
|
||||||
|
|
|
@ -1,26 +1,25 @@
|
||||||
.assets-compile-cache: &assets-compile-cache
|
.assets-compile-cache: &assets-compile-cache
|
||||||
cache:
|
cache:
|
||||||
key: "assets-compile:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:v5"
|
key: "assets-compile:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:v6"
|
||||||
paths:
|
paths:
|
||||||
- vendor/ruby/
|
- vendor/ruby/
|
||||||
- .yarn-cache/
|
- .yarn-cache/
|
||||||
- tmp/cache/assets/sprockets
|
- tmp/cache/assets/sprockets
|
||||||
policy: pull-push
|
|
||||||
|
|
||||||
.use-pg: &use-pg
|
.use-pg: &use-pg
|
||||||
services:
|
services:
|
||||||
- name: postgres:9.6
|
- name: postgres:9.6.11
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:alpine
|
- name: redis:alpine
|
||||||
|
|
||||||
gitlab:assets:compile:
|
.gitlab:assets:compile-metadata:
|
||||||
<<: *assets-compile-cache
|
<<: *assets-compile-cache
|
||||||
extends: .dedicated-no-docs-pull-cache-job
|
extends: .dedicated-no-docs-pull-cache-job
|
||||||
image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-git-2.21-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.29-docker-18.06.1
|
image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-git-2.21-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.29-docker-18.06.1
|
||||||
dependencies:
|
dependencies:
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
services:
|
services:
|
||||||
- docker:stable-dind
|
- docker:19.03.0-dind
|
||||||
variables:
|
variables:
|
||||||
NODE_ENV: "production"
|
NODE_ENV: "production"
|
||||||
RAILS_ENV: "production"
|
RAILS_ENV: "production"
|
||||||
|
@ -33,7 +32,7 @@ gitlab:assets:compile:
|
||||||
DOCKER_HOST: tcp://docker:2375
|
DOCKER_HOST: tcp://docker:2375
|
||||||
script:
|
script:
|
||||||
- node --version
|
- node --version
|
||||||
- retry yarn install --frozen-lockfile --production --cache-folder .yarn-cache
|
- retry yarn install --frozen-lockfile --production --cache-folder .yarn-cache --prefer-offline
|
||||||
- free -m
|
- free -m
|
||||||
- retry bundle exec rake gitlab:assets:compile
|
- retry bundle exec rake gitlab:assets:compile
|
||||||
- time scripts/build_assets_image
|
- time scripts/build_assets_image
|
||||||
|
@ -58,14 +57,32 @@ gitlab:assets:compile:
|
||||||
- docker
|
- docker
|
||||||
- gitlab-org
|
- gitlab-org
|
||||||
|
|
||||||
compile-assets:
|
gitlab:assets:compile:
|
||||||
|
extends: .gitlab:assets:compile-metadata
|
||||||
|
cache:
|
||||||
|
policy: pull-push
|
||||||
|
only:
|
||||||
|
- master@gitlab-org/gitlab-ce
|
||||||
|
- master@gitlab-org/gitlab-ee
|
||||||
|
|
||||||
|
gitlab:assets:compile pull-cache:
|
||||||
|
extends: .gitlab:assets:compile-metadata
|
||||||
|
cache:
|
||||||
|
policy: pull
|
||||||
|
except:
|
||||||
|
refs:
|
||||||
|
- master@gitlab-org/gitlab-ce
|
||||||
|
- master@gitlab-org/gitlab-ee
|
||||||
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
|
||||||
|
.compile-assets-metadata:
|
||||||
extends: .dedicated-runner
|
extends: .dedicated-runner
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
<<: *assets-compile-cache
|
<<: *assets-compile-cache
|
||||||
stage: prepare
|
stage: prepare
|
||||||
script:
|
script:
|
||||||
- node --version
|
- node --version
|
||||||
- retry yarn install --frozen-lockfile --cache-folder .yarn-cache
|
- retry yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline
|
||||||
- free -m
|
- free -m
|
||||||
- retry bundle exec rake gitlab:assets:compile
|
- retry bundle exec rake gitlab:assets:compile
|
||||||
- scripts/clean-old-cached-assets
|
- scripts/clean-old-cached-assets
|
||||||
|
@ -77,8 +94,23 @@ compile-assets:
|
||||||
paths:
|
paths:
|
||||||
- node_modules
|
- node_modules
|
||||||
- public/assets
|
- public/assets
|
||||||
|
|
||||||
|
compile-assets:
|
||||||
|
extends: .compile-assets-metadata
|
||||||
|
cache:
|
||||||
|
policy: pull-push
|
||||||
|
only:
|
||||||
|
- master@gitlab-org/gitlab-ce
|
||||||
|
- master@gitlab-org/gitlab-ee
|
||||||
|
|
||||||
|
compile-assets pull-cache:
|
||||||
|
extends: .compile-assets-metadata
|
||||||
|
cache:
|
||||||
|
policy: pull
|
||||||
except:
|
except:
|
||||||
refs:
|
refs:
|
||||||
|
- master@gitlab-org/gitlab-ce
|
||||||
|
- master@gitlab-org/gitlab-ee
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
|
||||||
gitlab:ui:visual:
|
gitlab:ui:visual:
|
||||||
|
@ -87,6 +119,7 @@ gitlab:ui:visual:
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
dependencies:
|
dependencies:
|
||||||
- compile-assets
|
- compile-assets
|
||||||
|
- compile-assets pull-cache
|
||||||
script:
|
script:
|
||||||
# Remove node modules from GitLab that may conflict with gitlab-ui
|
# Remove node modules from GitLab that may conflict with gitlab-ui
|
||||||
- rm -r node_modules
|
- rm -r node_modules
|
||||||
|
@ -116,6 +149,7 @@ karma:
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
dependencies:
|
dependencies:
|
||||||
- compile-assets
|
- compile-assets
|
||||||
|
- compile-assets pull-cache
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
variables:
|
variables:
|
||||||
# we override the max_old_space_size to prevent OOM errors
|
# we override the max_old_space_size to prevent OOM errors
|
||||||
|
@ -134,14 +168,15 @@ karma:
|
||||||
paths:
|
paths:
|
||||||
- chrome_debug.log
|
- chrome_debug.log
|
||||||
- coverage-javascript/
|
- coverage-javascript/
|
||||||
reports:
|
# reports:
|
||||||
junit: junit_karma.xml
|
# junit: junit_karma.xml
|
||||||
|
|
||||||
jest:
|
jest:
|
||||||
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
|
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
dependencies:
|
dependencies:
|
||||||
- compile-assets
|
- compile-assets
|
||||||
|
- compile-assets pull-cache
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
script:
|
script:
|
||||||
- scripts/gitaly-test-spawn
|
- scripts/gitaly-test-spawn
|
||||||
|
@ -156,8 +191,8 @@ jest:
|
||||||
paths:
|
paths:
|
||||||
- coverage-frontend/
|
- coverage-frontend/
|
||||||
- junit_jest.xml
|
- junit_jest.xml
|
||||||
reports:
|
# reports:
|
||||||
junit: junit_jest.xml
|
# junit: junit_jest.xml
|
||||||
cache:
|
cache:
|
||||||
key: jest
|
key: jest
|
||||||
paths:
|
paths:
|
||||||
|
@ -196,7 +231,7 @@ qa:selectors:
|
||||||
before_script: []
|
before_script: []
|
||||||
script:
|
script:
|
||||||
- date
|
- date
|
||||||
- yarn install --frozen-lockfile --cache-folder .yarn-cache
|
- yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline
|
||||||
- date
|
- date
|
||||||
- yarn run webpack-prod
|
- yarn run webpack-prod
|
||||||
|
|
||||||
|
@ -232,6 +267,7 @@ jsdoc:
|
||||||
stage: post-test
|
stage: post-test
|
||||||
dependencies:
|
dependencies:
|
||||||
- compile-assets
|
- compile-assets
|
||||||
|
- compile-assets pull-cache
|
||||||
before_script: []
|
before_script: []
|
||||||
script:
|
script:
|
||||||
- date
|
- date
|
||||||
|
|
|
@ -28,16 +28,26 @@
|
||||||
policy: pull
|
policy: pull
|
||||||
stage: test
|
stage: test
|
||||||
|
|
||||||
.dedicated-no-docs-pull-cache-job:
|
.no-docs:
|
||||||
extends: .dedicated-pull-cache-job
|
|
||||||
except:
|
except:
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
refs:
|
||||||
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
|
||||||
|
.no-docs-and-no-qa:
|
||||||
|
except:
|
||||||
|
refs:
|
||||||
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
- /(^qa[\/-].*|.*-qa$)/
|
||||||
|
|
||||||
|
.dedicated-no-docs-pull-cache-job:
|
||||||
|
extends:
|
||||||
|
- .dedicated-pull-cache-job
|
||||||
|
- .no-docs
|
||||||
|
|
||||||
.dedicated-no-docs-and-no-qa-pull-cache-job:
|
.dedicated-no-docs-and-no-qa-pull-cache-job:
|
||||||
extends: .dedicated-pull-cache-job
|
extends:
|
||||||
except:
|
- .dedicated-pull-cache-job
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
- .no-docs-and-no-qa
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
|
||||||
|
|
||||||
# Jobs that do not need a DB
|
# Jobs that do not need a DB
|
||||||
.dedicated-no-docs-no-db-pull-cache-job:
|
.dedicated-no-docs-no-db-pull-cache-job:
|
||||||
|
@ -45,6 +55,12 @@
|
||||||
variables:
|
variables:
|
||||||
SETUP_DB: "false"
|
SETUP_DB: "false"
|
||||||
|
|
||||||
|
# Jobs that need a dedicated runner, with no cache
|
||||||
|
.dedicated-no-docs:
|
||||||
|
extends:
|
||||||
|
- .dedicated-runner
|
||||||
|
- .no-docs
|
||||||
|
|
||||||
.single-script-job-dedicated-runner:
|
.single-script-job-dedicated-runner:
|
||||||
extends: .dedicated-runner
|
extends: .dedicated-runner
|
||||||
image: ruby:2.6-alpine
|
image: ruby:2.6-alpine
|
||||||
|
|
42
.gitlab/ci/memory.gitlab-ci.yml
Normal file
42
.gitlab/ci/memory.gitlab-ci.yml
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
memory-static:
|
||||||
|
extends: .dedicated-no-docs-no-db-pull-cache-job
|
||||||
|
script:
|
||||||
|
# Uses two different reports from the 'derailed_benchmars' gem.
|
||||||
|
|
||||||
|
# Loads each of gems in the Gemfile and checks how much memory they consume when they are required.
|
||||||
|
# 'derailed_benchmarks' internally uses 'get_process_mem'
|
||||||
|
- bundle exec derailed bundle:mem > tmp/memory_bundle_mem.txt
|
||||||
|
- scripts/generate-gems-size-metrics-static tmp/memory_bundle_mem.txt >> 'tmp/memory_metrics.txt'
|
||||||
|
|
||||||
|
# Outputs detailed information about objects created while gems are loaded.
|
||||||
|
# 'derailed_benchmarks' internally uses 'memory_profiler'
|
||||||
|
- bundle exec derailed bundle:objects > tmp/memory_bundle_objects.txt
|
||||||
|
- scripts/generate-gems-memory-metrics-static tmp/memory_bundle_objects.txt >> 'tmp/memory_metrics.txt'
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- tmp/memory_*.txt
|
||||||
|
reports:
|
||||||
|
metrics: tmp/memory_metrics.txt
|
||||||
|
|
||||||
|
# Show memory usage caused by invoking require per gem.
|
||||||
|
# Unlike `memory-static`, it hits the app with one request to ensure that any last minute require-s have been called.
|
||||||
|
# The application is booted in `production` environment.
|
||||||
|
# All tests are run without a webserver (directly using Rack::Mock by default).
|
||||||
|
memory-on-boot:
|
||||||
|
extends: .rspec-metadata-pg-10
|
||||||
|
variables:
|
||||||
|
NODE_ENV: "production"
|
||||||
|
RAILS_ENV: "production"
|
||||||
|
SETUP_DB: "true"
|
||||||
|
SKIP_STORAGE_VALIDATION: "true"
|
||||||
|
# we override the max_old_space_size to prevent OOM errors
|
||||||
|
NODE_OPTIONS: --max_old_space_size=3584
|
||||||
|
script:
|
||||||
|
# Both bootsnap and derailed monkey-patch Kernel#require, which leads to circular dependency
|
||||||
|
- DISABLE_BOOTSNAP=true PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> 'tmp/memory_on_boot.txt'
|
||||||
|
- scripts/generate-memory-metrics-on-boot tmp/memory_on_boot.txt >> 'tmp/memory_on_boot_metrics.txt'
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- tmp/memory_*.txt
|
||||||
|
reports:
|
||||||
|
metrics: tmp/memory_on_boot_metrics.txt
|
|
@ -1,6 +1,6 @@
|
||||||
.use-pg: &use-pg
|
.use-pg: &use-pg
|
||||||
services:
|
services:
|
||||||
- name: postgres:9.6
|
- name: postgres:9.6.11
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:alpine
|
- name: redis:alpine
|
||||||
|
|
||||||
|
@ -10,11 +10,6 @@
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
- name: redis:alpine
|
- name: redis:alpine
|
||||||
|
|
||||||
.use-mysql: &use-mysql
|
|
||||||
services:
|
|
||||||
- mysql:5.7
|
|
||||||
- redis:alpine
|
|
||||||
|
|
||||||
.only-schedules-master: &only-schedules-master
|
.only-schedules-master: &only-schedules-master
|
||||||
only:
|
only:
|
||||||
- schedules@gitlab-org/gitlab-ce
|
- schedules@gitlab-org/gitlab-ce
|
||||||
|
@ -25,8 +20,9 @@
|
||||||
- master@gitlab/gitlab-ee
|
- master@gitlab/gitlab-ee
|
||||||
|
|
||||||
.gitlab-setup: &gitlab-setup
|
.gitlab-setup: &gitlab-setup
|
||||||
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
|
extends:
|
||||||
<<: *use-pg
|
- .dedicated-no-docs-and-no-qa-pull-cache-job
|
||||||
|
- .use-pg
|
||||||
variables:
|
variables:
|
||||||
SETUP_DB: "false"
|
SETUP_DB: "false"
|
||||||
script:
|
script:
|
||||||
|
@ -48,7 +44,9 @@
|
||||||
- bundle exec rake $CI_JOB_NAME
|
- bundle exec rake $CI_JOB_NAME
|
||||||
|
|
||||||
.rspec-metadata: &rspec-metadata
|
.rspec-metadata: &rspec-metadata
|
||||||
extends: .dedicated-pull-cache-job
|
extends:
|
||||||
|
- .dedicated-pull-cache-job
|
||||||
|
- .no-docs-and-no-qa
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- JOB_NAME=( $CI_JOB_NAME )
|
- JOB_NAME=( $CI_JOB_NAME )
|
||||||
|
@ -68,6 +66,8 @@
|
||||||
- scripts/gitaly-test-spawn
|
- scripts/gitaly-test-spawn
|
||||||
- date
|
- date
|
||||||
- 'export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new.pattern(:${TEST_LEVEL})")'
|
- 'export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new.pattern(:${TEST_LEVEL})")'
|
||||||
|
- mkdir -p tmp/memory_test
|
||||||
|
- export MEMORY_TEST_PATH="tmp/memory_test/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_memory.csv"
|
||||||
- knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag level:${TEST_LEVEL} --tag ~geo"
|
- knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag level:${TEST_LEVEL} --tag ~geo"
|
||||||
- date
|
- date
|
||||||
artifacts:
|
artifacts:
|
||||||
|
@ -79,11 +79,9 @@
|
||||||
- rspec_flaky/
|
- rspec_flaky/
|
||||||
- rspec_profiling/
|
- rspec_profiling/
|
||||||
- tmp/capybara/
|
- tmp/capybara/
|
||||||
reports:
|
- tmp/memory_test/
|
||||||
junit: junit_rspec.xml
|
# reports:
|
||||||
except:
|
# junit: junit_rspec.xml
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
|
||||||
|
|
||||||
.rspec-metadata-pg: &rspec-metadata-pg
|
.rspec-metadata-pg: &rspec-metadata-pg
|
||||||
<<: *rspec-metadata
|
<<: *rspec-metadata
|
||||||
|
@ -94,10 +92,6 @@
|
||||||
<<: *use-pg-10
|
<<: *use-pg-10
|
||||||
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.29"
|
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.29"
|
||||||
|
|
||||||
.rspec-metadata-mysql: &rspec-metadata-mysql
|
|
||||||
<<: *rspec-metadata
|
|
||||||
<<: *use-mysql
|
|
||||||
|
|
||||||
# DB migration, rollback, and seed jobs
|
# DB migration, rollback, and seed jobs
|
||||||
.db-migrate-reset: &db-migrate-reset
|
.db-migrate-reset: &db-migrate-reset
|
||||||
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
|
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
|
||||||
|
@ -131,8 +125,10 @@
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
|
|
||||||
setup-test-env:
|
setup-test-env:
|
||||||
extends: .dedicated-runner-default-cache
|
extends:
|
||||||
<<: *use-pg
|
- .dedicated-runner-default-cache
|
||||||
|
- .no-docs
|
||||||
|
- .use-pg
|
||||||
stage: prepare
|
stage: prepare
|
||||||
script:
|
script:
|
||||||
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
|
||||||
|
@ -143,8 +139,6 @@ setup-test-env:
|
||||||
- tmp/tests
|
- tmp/tests
|
||||||
- config/secrets.yml
|
- config/secrets.yml
|
||||||
- vendor/gitaly-ruby
|
- vendor/gitaly-ruby
|
||||||
except:
|
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
|
||||||
|
|
||||||
rspec unit pg:
|
rspec unit pg:
|
||||||
<<: *rspec-metadata-pg
|
<<: *rspec-metadata-pg
|
||||||
|
@ -173,42 +167,6 @@ rspec system pg-10:
|
||||||
<<: *only-schedules-master
|
<<: *only-schedules-master
|
||||||
parallel: 24
|
parallel: 24
|
||||||
|
|
||||||
rspec unit mysql:
|
|
||||||
<<: *rspec-metadata-mysql
|
|
||||||
<<: *only-schedules-master
|
|
||||||
parallel: 20
|
|
||||||
|
|
||||||
rspec integration mysql:
|
|
||||||
<<: *rspec-metadata-mysql
|
|
||||||
<<: *only-schedules-master
|
|
||||||
parallel: 6
|
|
||||||
|
|
||||||
rspec system mysql:
|
|
||||||
<<: *rspec-metadata-mysql
|
|
||||||
<<: *only-schedules-master
|
|
||||||
parallel: 24
|
|
||||||
|
|
||||||
.rspec-mysql-on-demand: &rspec-mysql-on-demand
|
|
||||||
only:
|
|
||||||
variables:
|
|
||||||
- $CI_COMMIT_MESSAGE =~ /\[run mysql\]/i
|
|
||||||
- $CI_COMMIT_REF_NAME =~ /mysql/
|
|
||||||
|
|
||||||
rspec unit mysql on-demand:
|
|
||||||
<<: *rspec-metadata-mysql
|
|
||||||
<<: *rspec-mysql-on-demand
|
|
||||||
parallel: 20
|
|
||||||
|
|
||||||
rspec integration mysql on-demand:
|
|
||||||
<<: *rspec-metadata-mysql
|
|
||||||
<<: *rspec-mysql-on-demand
|
|
||||||
parallel: 6
|
|
||||||
|
|
||||||
rspec system mysql on-demand:
|
|
||||||
<<: *rspec-metadata-mysql
|
|
||||||
<<: *rspec-mysql-on-demand
|
|
||||||
parallel: 24
|
|
||||||
|
|
||||||
rspec-fast-spec-helper:
|
rspec-fast-spec-helper:
|
||||||
<<: *rspec-metadata-pg
|
<<: *rspec-metadata-pg
|
||||||
script:
|
script:
|
||||||
|
@ -226,16 +184,11 @@ rspec quarantine pg:
|
||||||
<<: *rspec-quarantine
|
<<: *rspec-quarantine
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
|
||||||
rspec quarantine mysql:
|
|
||||||
<<: *rspec-metadata-mysql
|
|
||||||
<<: *rspec-quarantine
|
|
||||||
<<: *only-schedules-master
|
|
||||||
allow_failure: true
|
|
||||||
|
|
||||||
static-analysis:
|
static-analysis:
|
||||||
extends: .dedicated-no-docs-no-db-pull-cache-job
|
extends: .dedicated-no-docs-no-db-pull-cache-job
|
||||||
dependencies:
|
dependencies:
|
||||||
- compile-assets
|
- compile-assets
|
||||||
|
- compile-assets pull-cache
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
script:
|
script:
|
||||||
- scripts/static-analysis
|
- scripts/static-analysis
|
||||||
|
@ -250,11 +203,12 @@ static-analysis:
|
||||||
downtime_check:
|
downtime_check:
|
||||||
<<: *rake-exec
|
<<: *rake-exec
|
||||||
except:
|
except:
|
||||||
- master
|
refs:
|
||||||
- tags
|
- master
|
||||||
- /^[\d-]+-stable(-ee)?$/
|
- tags
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
- /^[\d-]+-stable(-ee)?$/
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
- /(^qa[\/-].*|.*-qa$)/
|
||||||
dependencies:
|
dependencies:
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
|
|
||||||
|
@ -262,12 +216,13 @@ ee_compat_check:
|
||||||
<<: *rake-exec
|
<<: *rake-exec
|
||||||
dependencies: []
|
dependencies: []
|
||||||
except:
|
except:
|
||||||
- master
|
refs:
|
||||||
- tags
|
- master
|
||||||
- /[\d-]+-stable(-ee)?/
|
- tags
|
||||||
- /^security-/
|
- /[\d-]+-stable(-ee)?/
|
||||||
- branches@gitlab-org/gitlab-ee
|
- /^security-/
|
||||||
- branches@gitlab/gitlab-ee
|
- branches@gitlab-org/gitlab-ee
|
||||||
|
- branches@gitlab/gitlab-ee
|
||||||
retry: 0
|
retry: 0
|
||||||
artifacts:
|
artifacts:
|
||||||
name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}"
|
name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}"
|
||||||
|
@ -280,10 +235,6 @@ db:migrate:reset-pg:
|
||||||
<<: *db-migrate-reset
|
<<: *db-migrate-reset
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
|
|
||||||
db:migrate:reset-mysql:
|
|
||||||
<<: *db-migrate-reset
|
|
||||||
<<: *use-mysql
|
|
||||||
|
|
||||||
db:check-schema-pg:
|
db:check-schema-pg:
|
||||||
<<: *db-migrate-reset
|
<<: *db-migrate-reset
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
|
@ -294,15 +245,11 @@ migration:path-pg:
|
||||||
<<: *migration-paths
|
<<: *migration-paths
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
|
|
||||||
migration:path-mysql:
|
|
||||||
<<: *migration-paths
|
|
||||||
<<: *use-mysql
|
|
||||||
|
|
||||||
.db-rollback: &db-rollback
|
.db-rollback: &db-rollback
|
||||||
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
|
extends: .dedicated-no-docs-and-no-qa-pull-cache-job
|
||||||
script:
|
script:
|
||||||
- bundle exec rake db:migrate VERSION=20170523121229
|
- bundle exec rake db:migrate VERSION=20180101160629
|
||||||
- bundle exec rake db:migrate
|
- bundle exec rake db:migrate SKIP_SCHEMA_VERSION_CHECK=true
|
||||||
dependencies:
|
dependencies:
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
|
|
||||||
|
@ -310,26 +257,18 @@ db:rollback-pg:
|
||||||
<<: *db-rollback
|
<<: *db-rollback
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
|
|
||||||
db:rollback-mysql:
|
|
||||||
<<: *db-rollback
|
|
||||||
<<: *use-mysql
|
|
||||||
|
|
||||||
gitlab:setup-pg:
|
gitlab:setup-pg:
|
||||||
<<: *gitlab-setup
|
<<: *gitlab-setup
|
||||||
<<: *use-pg
|
<<: *use-pg
|
||||||
dependencies:
|
dependencies:
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
|
|
||||||
gitlab:setup-mysql:
|
|
||||||
<<: *gitlab-setup
|
|
||||||
<<: *use-mysql
|
|
||||||
dependencies:
|
|
||||||
- setup-test-env
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
# Don't include dedicated-no-docs-no-db-pull-cache-job here since we need to
|
# Don't include dedicated-no-docs-no-db-pull-cache-job here since we need to
|
||||||
# download artifacts from all the rspec jobs instead of from setup-test-env only
|
# download artifacts from all the rspec jobs instead of from setup-test-env only
|
||||||
extends: .dedicated-runner-default-cache
|
extends:
|
||||||
|
- .dedicated-runner-default-cache
|
||||||
|
- .no-docs-and-no-qa
|
||||||
cache:
|
cache:
|
||||||
policy: pull
|
policy: pull
|
||||||
variables:
|
variables:
|
||||||
|
@ -337,6 +276,7 @@ coverage:
|
||||||
stage: post-test
|
stage: post-test
|
||||||
script:
|
script:
|
||||||
- bundle exec scripts/merge-simplecov
|
- bundle exec scripts/merge-simplecov
|
||||||
|
- bundle exec scripts/gather-test-memory-data
|
||||||
coverage: '/LOC \((\d+\.\d+%)\) covered.$/'
|
coverage: '/LOC \((\d+\.\d+%)\) covered.$/'
|
||||||
artifacts:
|
artifacts:
|
||||||
name: coverage
|
name: coverage
|
||||||
|
@ -344,6 +284,4 @@ coverage:
|
||||||
paths:
|
paths:
|
||||||
- coverage/index.html
|
- coverage/index.html
|
||||||
- coverage/assets/
|
- coverage/assets/
|
||||||
except:
|
- tmp/memory_test/
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
|
||||||
|
|
|
@ -1,98 +1,26 @@
|
||||||
include:
|
include:
|
||||||
- template: Code-Quality.gitlab-ci.yml
|
- template: Code-Quality.gitlab-ci.yml
|
||||||
|
- template: Security/SAST.gitlab-ci.yml
|
||||||
|
- template: Security/Dependency-Scanning.gitlab-ci.yml
|
||||||
|
|
||||||
code_quality:
|
code_quality:
|
||||||
extends: .dedicated-no-docs-no-db-pull-cache-job
|
extends: .dedicated-no-docs
|
||||||
# gitlab-org runners set `privileged: false` but we need to have it set to true
|
# gitlab-org runners set `privileged: false` but we need to have it set to true
|
||||||
# since we're using Docker in Docker
|
# since we're using Docker in Docker
|
||||||
tags: []
|
tags: []
|
||||||
before_script: []
|
before_script: []
|
||||||
cache: {}
|
cache: {}
|
||||||
dependencies: []
|
|
||||||
variables:
|
|
||||||
SETUP_DB: "false"
|
|
||||||
|
|
||||||
sast:
|
sast:
|
||||||
extends: .dedicated-no-docs-no-db-pull-cache-job
|
extends: .dedicated-no-docs
|
||||||
image: docker:stable
|
|
||||||
variables:
|
|
||||||
SAST_CONFIDENCE_LEVEL: 2
|
|
||||||
DOCKER_DRIVER: overlay2
|
|
||||||
allow_failure: true
|
|
||||||
tags: []
|
tags: []
|
||||||
before_script: []
|
before_script: []
|
||||||
cache: {}
|
cache: {}
|
||||||
dependencies: []
|
variables:
|
||||||
services:
|
SAST_BRAKEMAN_LEVEL: 2
|
||||||
- docker:stable-dind
|
|
||||||
script:
|
|
||||||
- | # this is required to avoid undesirable reset of Docker image ENV variables being set on build stage
|
|
||||||
function propagate_env_vars() {
|
|
||||||
CURRENT_ENV=$(printenv)
|
|
||||||
|
|
||||||
for VAR_NAME; do
|
|
||||||
echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
|
|
||||||
done
|
|
||||||
}
|
|
||||||
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
|
|
||||||
- |
|
|
||||||
docker run \
|
|
||||||
$(propagate_env_vars \
|
|
||||||
SAST_ANALYZER_IMAGES \
|
|
||||||
SAST_ANALYZER_IMAGE_PREFIX \
|
|
||||||
SAST_ANALYZER_IMAGE_TAG \
|
|
||||||
SAST_DEFAULT_ANALYZERS \
|
|
||||||
SAST_BRAKEMAN_LEVEL \
|
|
||||||
SAST_GOSEC_LEVEL \
|
|
||||||
SAST_FLAWFINDER_LEVEL \
|
|
||||||
SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
|
|
||||||
SAST_PULL_ANALYZER_IMAGE_TIMEOUT \
|
|
||||||
SAST_RUN_ANALYZER_TIMEOUT \
|
|
||||||
) \
|
|
||||||
--volume "$PWD:/code" \
|
|
||||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
|
||||||
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
|
|
||||||
artifacts:
|
|
||||||
reports:
|
|
||||||
sast: gl-sast-report.json
|
|
||||||
|
|
||||||
dependency_scanning:
|
dependency_scanning:
|
||||||
extends: .dedicated-no-docs-no-db-pull-cache-job
|
extends: .dedicated-no-docs
|
||||||
image: docker:stable
|
|
||||||
variables:
|
|
||||||
DOCKER_DRIVER: overlay2
|
|
||||||
allow_failure: true
|
|
||||||
tags: []
|
tags: []
|
||||||
before_script: []
|
before_script: []
|
||||||
cache: {}
|
cache: {}
|
||||||
dependencies: []
|
|
||||||
services:
|
|
||||||
- docker:stable-dind
|
|
||||||
script:
|
|
||||||
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
|
|
||||||
- | # this is required to avoid undesirable reset of Docker image ENV variables being set on build stage
|
|
||||||
function propagate_env_vars() {
|
|
||||||
CURRENT_ENV=$(printenv)
|
|
||||||
|
|
||||||
for VAR_NAME; do
|
|
||||||
echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
|
|
||||||
done
|
|
||||||
}
|
|
||||||
- |
|
|
||||||
docker run \
|
|
||||||
$(propagate_env_vars \
|
|
||||||
DS_ANALYZER_IMAGES \
|
|
||||||
DS_ANALYZER_IMAGE_PREFIX \
|
|
||||||
DS_ANALYZER_IMAGE_TAG \
|
|
||||||
DS_DEFAULT_ANALYZERS \
|
|
||||||
DEP_SCAN_DISABLE_REMOTE_CHECKS \
|
|
||||||
DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
|
|
||||||
DS_PULL_ANALYZER_IMAGE_TIMEOUT \
|
|
||||||
DS_RUN_ANALYZER_TIMEOUT \
|
|
||||||
) \
|
|
||||||
--volume "$PWD:/code" \
|
|
||||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
|
||||||
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
|
|
||||||
artifacts:
|
|
||||||
reports:
|
|
||||||
dependency_scanning: gl-dependency-scanning-report.json
|
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
<<: *review-base
|
<<: *review-base
|
||||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine
|
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine
|
||||||
services:
|
services:
|
||||||
- docker:stable-dind
|
- docker:19.03.0-dind
|
||||||
tags:
|
tags:
|
||||||
- gitlab-org
|
- gitlab-org
|
||||||
- docker
|
- docker
|
||||||
|
@ -77,6 +77,7 @@ schedule:review-build-cng:
|
||||||
.review-deploy-base: &review-deploy-base
|
.review-deploy-base: &review-deploy-base
|
||||||
<<: *review-base
|
<<: *review-base
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
retry: 1
|
||||||
stage: review
|
stage: review
|
||||||
variables:
|
variables:
|
||||||
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
|
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
|
||||||
|
@ -95,10 +96,16 @@ schedule:review-build-cng:
|
||||||
- install_api_client_dependencies_with_apk
|
- install_api_client_dependencies_with_apk
|
||||||
- source scripts/review_apps/review-apps.sh
|
- source scripts/review_apps/review-apps.sh
|
||||||
script:
|
script:
|
||||||
- perform_review_app_deployment
|
- check_kube_domain
|
||||||
|
- ensure_namespace
|
||||||
|
- install_tiller
|
||||||
|
- install_external_dns
|
||||||
|
- download_chart
|
||||||
|
- deploy || display_deployment_debug
|
||||||
|
- wait_for_review_app_to_be_accessible
|
||||||
|
- add_license
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths: [review_app_url.txt]
|
||||||
- review_app_url.txt
|
|
||||||
expire_in: 2 days
|
expire_in: 2 days
|
||||||
when: always
|
when: always
|
||||||
|
|
||||||
|
@ -108,8 +115,6 @@ review-deploy:
|
||||||
schedule:review-deploy:
|
schedule:review-deploy:
|
||||||
<<: *review-deploy-base
|
<<: *review-deploy-base
|
||||||
<<: *review-schedules-only
|
<<: *review-schedules-only
|
||||||
script:
|
|
||||||
- perform_review_app_deployment
|
|
||||||
|
|
||||||
review-stop:
|
review-stop:
|
||||||
<<: *review-base
|
<<: *review-base
|
||||||
|
@ -124,11 +129,11 @@ review-stop:
|
||||||
script:
|
script:
|
||||||
- source scripts/review_apps/review-apps.sh
|
- source scripts/review_apps/review-apps.sh
|
||||||
- delete
|
- delete
|
||||||
- cleanup
|
|
||||||
|
|
||||||
.review-qa-base: &review-qa-base
|
.review-qa-base: &review-qa-base
|
||||||
<<: *review-docker
|
<<: *review-docker
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
|
retry: 2
|
||||||
stage: qa
|
stage: qa
|
||||||
variables:
|
variables:
|
||||||
<<: *review-docker-variables
|
<<: *review-docker-variables
|
||||||
|
@ -169,7 +174,38 @@ review-qa-all:
|
||||||
script:
|
script:
|
||||||
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/review-qa-all_master_report.json
|
- export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/review-qa-all_master_report.json
|
||||||
- export KNAPSACK_TEST_FILE_PATTERN=qa/specs/features/**/*_spec.rb
|
- export KNAPSACK_TEST_FILE_PATTERN=qa/specs/features/**/*_spec.rb
|
||||||
- gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
|
- gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}" -- --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml --format html --out tmp/rspec.htm --color --format documentation
|
||||||
|
|
||||||
|
parallel-spec-reports:
|
||||||
|
extends: .dedicated-runner
|
||||||
|
dependencies:
|
||||||
|
- review-qa-all
|
||||||
|
image: ruby:2.6-alpine
|
||||||
|
services: []
|
||||||
|
before_script: []
|
||||||
|
variables:
|
||||||
|
SETUP_DB: "false"
|
||||||
|
NEW_PARALLEL_SPECS_REPORT: qa/report-new.html
|
||||||
|
BASE_ARTIFACT_URL: "${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/file/qa/"
|
||||||
|
stage: post-test
|
||||||
|
allow_failure: true
|
||||||
|
when: manual
|
||||||
|
retry: 0
|
||||||
|
artifacts:
|
||||||
|
when: always
|
||||||
|
paths:
|
||||||
|
- qa/report-new.html
|
||||||
|
- qa/gitlab-qa-run-*
|
||||||
|
reports:
|
||||||
|
junit: qa/gitlab-qa-run-*/**/rspec-*.xml
|
||||||
|
script:
|
||||||
|
- apk add --update build-base libxml2-dev libxslt-dev && rm -rf /var/cache/apk/*
|
||||||
|
- gem install nokogiri
|
||||||
|
- cd qa/gitlab-qa-run-*/gitlab-*
|
||||||
|
- ARTIFACT_DIRS=$(pwd |rev| awk -F / '{print $1,$2}' | rev | sed s_\ _/_)
|
||||||
|
- cd ../../..
|
||||||
|
- '[[ -f $NEW_PARALLEL_SPECS_REPORT ]] || echo "{}" > ${NEW_PARALLEL_SPECS_REPORT}'
|
||||||
|
- scripts/merge-html-reports ${NEW_PARALLEL_SPECS_REPORT} ${BASE_ARTIFACT_URL}${ARTIFACT_DIRS} qa/gitlab-qa-run-*/**/rspec.htm
|
||||||
|
|
||||||
.review-performance-base: &review-performance-base
|
.review-performance-base: &review-performance-base
|
||||||
<<: *review-qa-base
|
<<: *review-qa-base
|
||||||
|
@ -225,11 +261,12 @@ danger-review:
|
||||||
except:
|
except:
|
||||||
refs:
|
refs:
|
||||||
- master
|
- master
|
||||||
|
- /^[\d-]+-stable(-ee)?$/
|
||||||
variables:
|
variables:
|
||||||
- $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
|
- $CI_COMMIT_REF_NAME =~ /^ce-to-ee-.*/
|
||||||
- $CI_COMMIT_REF_NAME =~ /.*-stable(-ee)?-prepare-.*/
|
- $CI_COMMIT_REF_NAME =~ /.*-stable(-ee)?-prepare-.*/
|
||||||
script:
|
script:
|
||||||
- git version
|
- git version
|
||||||
- node --version
|
- node --version
|
||||||
- yarn install --frozen-lockfile --cache-folder .yarn-cache
|
- yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline
|
||||||
- danger --fail-on-errors=true
|
- danger --fail-on-errors=true
|
||||||
|
|
|
@ -15,7 +15,9 @@ cache gems:
|
||||||
- setup-test-env
|
- setup-test-env
|
||||||
|
|
||||||
gitlab_git_test:
|
gitlab_git_test:
|
||||||
extends: .dedicated-runner
|
extends:
|
||||||
|
- .dedicated-runner
|
||||||
|
- .no-docs-and-no-qa
|
||||||
variables:
|
variables:
|
||||||
SETUP_DB: "false"
|
SETUP_DB: "false"
|
||||||
before_script: []
|
before_script: []
|
||||||
|
@ -23,12 +25,11 @@ gitlab_git_test:
|
||||||
cache: {}
|
cache: {}
|
||||||
script:
|
script:
|
||||||
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
|
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
|
||||||
except:
|
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
|
||||||
|
|
||||||
no_ee_check:
|
no_ee_check:
|
||||||
extends: .dedicated-runner
|
extends:
|
||||||
|
- .dedicated-runner
|
||||||
|
- .no-docs-and-no-qa
|
||||||
variables:
|
variables:
|
||||||
SETUP_DB: "false"
|
SETUP_DB: "false"
|
||||||
before_script: []
|
before_script: []
|
||||||
|
@ -38,6 +39,3 @@ no_ee_check:
|
||||||
- scripts/no-ee-check
|
- scripts/no-ee-check
|
||||||
only:
|
only:
|
||||||
- /.+/@gitlab-org/gitlab-ce
|
- /.+/@gitlab-org/gitlab-ce
|
||||||
except:
|
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
- rspec_profiling/
|
- rspec_profiling/
|
||||||
|
|
||||||
retrieve-tests-metadata:
|
retrieve-tests-metadata:
|
||||||
<<: *tests-metadata-state
|
extends:
|
||||||
|
- .tests-metadata-state
|
||||||
|
- .no-docs-and-no-qa
|
||||||
stage: prepare
|
stage: prepare
|
||||||
cache:
|
cache:
|
||||||
key: tests_metadata
|
key: tests_metadata
|
||||||
|
@ -25,9 +27,6 @@ retrieve-tests-metadata:
|
||||||
- mkdir -p rspec_profiling/
|
- mkdir -p rspec_profiling/
|
||||||
- wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH
|
- wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH
|
||||||
- '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}'
|
- '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}'
|
||||||
except:
|
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
|
||||||
|
|
||||||
update-tests-metadata:
|
update-tests-metadata:
|
||||||
<<: *tests-metadata-state
|
<<: *tests-metadata-state
|
||||||
|
@ -69,9 +68,10 @@ flaky-examples-check:
|
||||||
only:
|
only:
|
||||||
- branches
|
- branches
|
||||||
except:
|
except:
|
||||||
- master
|
refs:
|
||||||
- /(^docs[\/-].*|.*-docs$)/
|
- master
|
||||||
- /(^qa[\/-].*|.*-qa$)/
|
- /(^docs[\/-].*|.*-docs$)/
|
||||||
|
- /(^qa[\/-].*|.*-qa$)/
|
||||||
artifacts:
|
artifacts:
|
||||||
expire_in: 30d
|
expire_in: 30d
|
||||||
paths:
|
paths:
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
#### Database Reviewer Checklist
|
|
||||||
|
|
||||||
Thank you for becoming a ~database reviewer! Please work on the list
|
|
||||||
below to complete your setup. For any question, reach out to #database
|
|
||||||
an mention `@abrandl`.
|
|
||||||
|
|
||||||
- [ ] Change issue title to include your name: `Database Reviewer Checklist: Your Name`
|
|
||||||
- [ ] Review general [code review guide](https://docs.gitlab.com/ee/development/code_review.html)
|
|
||||||
- [ ] Review [database review documentation](https://about.gitlab.com/handbook/engineering/workflow/code-review/database.html)
|
|
||||||
- [ ] Familiarize with [migration helpers](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/database/migration_helpers.rb) and review usage in existing migrations
|
|
||||||
- [ ] Read [database migration style guide](https://docs.gitlab.com/ee/development/migration_style_guide.html)
|
|
||||||
- [ ] Familiarize with best practices in [database guides](https://docs.gitlab.com/ee/development/#database-guides)
|
|
||||||
- [ ] Watch [Optimising Rails Database Queries: Episode 1](https://www.youtube.com/watch?v=79GurlaxhsI)
|
|
||||||
- [ ] Read [Understanding EXPLAIN plans](https://docs.gitlab.com/ee/development/understanding_explain_plans.html)
|
|
||||||
- [ ] Review [database best practices](https://docs.gitlab.com/ee/development/#best-practices)
|
|
||||||
- [ ] Review how we use [database instances restored from a backup](https://ops.gitlab.net/gitlab-com/gl-infra/gitlab-restore/postgres-gprd) for testing and make sure you're set up to execute pipelines (check [README.md](https://ops.gitlab.net/gitlab-com/gl-infra/gitlab-restore/postgres-gprd/blob/master/README.md) and reach out to @abrandl since this is currently subject to being changed)
|
|
||||||
- [ ] Get yourself added to [`@gl-database`](https://gitlab.com/groups/gl-database/-/group_members) group and respond to @-mentions to the group (reach out to any maintainer on the group to get added). You will get TODOs on gitlab.com for group mentions.
|
|
||||||
- [ ] Make sure you have proper access to at least a read-only replica in staging and production
|
|
||||||
- [ ] Indicate in `data/team.yml` your role as a database reviewer ([example MR](https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/19600/diffs)). Assign MR to your manager for merge.
|
|
||||||
- [ ] Send one MR to improve the [review documentation](https://about.gitlab.com/handbook/engineering/workflow/code-review/database.html) or the [issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Database%20Reviewer.md)
|
|
||||||
|
|
||||||
Note that *approving and accepting* merge requests is *restricted* to
|
|
||||||
Database Maintainers only. As a reviewer, pass the MR to a maintainer
|
|
||||||
for approval.
|
|
||||||
|
|
||||||
You're all set! Watch out for TODOs on GitLab.com.
|
|
||||||
|
|
||||||
###### Where to go for questions?
|
|
||||||
|
|
||||||
Reach out to `#database` on Slack and mention `@abrandl` for any questions.
|
|
||||||
|
|
||||||
cc @abrandl
|
|
||||||
|
|
||||||
/label ~meta ~database
|
|
|
@ -4,7 +4,7 @@
|
||||||
Note: Doc work as part of feature development is covered in the Feature Request template.
|
Note: Doc work as part of feature development is covered in the Feature Request template.
|
||||||
|
|
||||||
* For issues related to features of the docs.gitlab.com site, see
|
* For issues related to features of the docs.gitlab.com site, see
|
||||||
https://gitlab.com/gitlab-com/gitlab-docs/issues/
|
https://gitlab.com/gitlab-org/gitlab-docs/issues/
|
||||||
|
|
||||||
* For information about documentation content and process, see
|
* For information about documentation content and process, see
|
||||||
https://docs.gitlab.com/ee/development/documentation/ -->
|
https://docs.gitlab.com/ee/development/documentation/ -->
|
||||||
|
|
|
@ -22,7 +22,7 @@ https://docs.gitlab.com/ce/development/documentation/index.html#changing-documen
|
||||||
- [ ] Make sure internal links pointing to the document in question are not broken.
|
- [ ] Make sure internal links pointing to the document in question are not broken.
|
||||||
- [ ] Search and replace any links referring to old docs in GitLab Rails app,
|
- [ ] Search and replace any links referring to old docs in GitLab Rails app,
|
||||||
specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
|
specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
|
||||||
- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/writing_documentation.html#redirections-for-pages-with-disqus-comments)
|
- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
|
||||||
to the new document if there are any Disqus comments on the old document thread.
|
to the new document if there are any Disqus comments on the old document thread.
|
||||||
- [ ] Update the link in `features.yml` (if applicable)
|
- [ ] Update the link in `features.yml` (if applicable)
|
||||||
- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
|
- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
|
||||||
|
|
7
.mdlrc
Normal file
7
.mdlrc
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# This is the options file for mdl, configured in .gitlab/ci/docs.gitlab-ci.yml,
|
||||||
|
# and related to the style file ./mdlrc.style
|
||||||
|
|
||||||
|
# See https://github.com/markdownlint/markdownlint/blob/master/docs/configuration.md
|
||||||
|
|
||||||
|
ignore_front_matter true
|
||||||
|
style File.expand_path('.mdlrc.style', __dir__)
|
21
.mdlrc.style
Normal file
21
.mdlrc.style
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# This is the style file for mdl, configured in .gitlab/ci/docs.gitlab-ci.yml,
|
||||||
|
# and related to the options file ./mdlrc
|
||||||
|
|
||||||
|
# See https://github.com/markdownlint/markdownlint/blob/master/docs/RULES.md
|
||||||
|
# for more detailed information on the rules and styles.
|
||||||
|
|
||||||
|
rule "MD001"
|
||||||
|
rule "MD003", :style => :atx
|
||||||
|
rule "MD011"
|
||||||
|
rule "MD023"
|
||||||
|
rule "MD032"
|
||||||
|
rule "MD034"
|
||||||
|
rule "MD037"
|
||||||
|
|
||||||
|
# Should not be used currently:
|
||||||
|
|
||||||
|
# rule "MD004", :style => :dash # unordered list style - dash
|
||||||
|
# False positives, see https://github.com/markdownlint/markdownlint/issues/261
|
||||||
|
|
||||||
|
# rule "MD039" # Spaces inside link text
|
||||||
|
# Crashes when link text has certain punctuation
|
|
@ -466,12 +466,10 @@ Rails/LinkToBlank:
|
||||||
Rails/Presence:
|
Rails/Presence:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'app/models/ci/pipeline.rb'
|
- 'app/models/ci/pipeline.rb'
|
||||||
- 'app/models/clusters/platforms/kubernetes.rb'
|
|
||||||
- 'app/models/concerns/mentionable.rb'
|
- 'app/models/concerns/mentionable.rb'
|
||||||
- 'app/models/project_services/hipchat_service.rb'
|
- 'app/models/project_services/hipchat_service.rb'
|
||||||
- 'app/models/project_services/irker_service.rb'
|
- 'app/models/project_services/irker_service.rb'
|
||||||
- 'app/models/project_services/jira_service.rb'
|
- 'app/models/project_services/jira_service.rb'
|
||||||
- 'app/models/project_services/kubernetes_service.rb'
|
|
||||||
- 'app/models/project_services/packagist_service.rb'
|
- 'app/models/project_services/packagist_service.rb'
|
||||||
- 'app/models/wiki_page.rb'
|
- 'app/models/wiki_page.rb'
|
||||||
- 'lib/gitlab/github_import/importer/releases_importer.rb'
|
- 'lib/gitlab/github_import/importer/releases_importer.rb'
|
||||||
|
@ -514,7 +512,6 @@ Security/YAMLLoad:
|
||||||
- 'spec/config/mail_room_spec.rb'
|
- 'spec/config/mail_room_spec.rb'
|
||||||
- 'spec/initializers/secret_token_spec.rb'
|
- 'spec/initializers/secret_token_spec.rb'
|
||||||
- 'spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb'
|
- 'spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb'
|
||||||
- 'spec/models/project_services/kubernetes_service_spec.rb'
|
|
||||||
|
|
||||||
# Offense count: 34
|
# Offense count: 34
|
||||||
# Configuration parameters: EnforcedStyle.
|
# Configuration parameters: EnforcedStyle.
|
||||||
|
|
374
CHANGELOG.md
374
CHANGELOG.md
|
@ -2,20 +2,27 @@
|
||||||
documentation](doc/development/changelog.md) for instructions on adding your own
|
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||||
entry.
|
entry.
|
||||||
|
|
||||||
## 12.0.9
|
## 12.1.11
|
||||||
|
|
||||||
|
- No changes.
|
||||||
|
|
||||||
|
## 12.1.10
|
||||||
|
|
||||||
|
- No changes.
|
||||||
|
|
||||||
|
## 12.1.9
|
||||||
|
|
||||||
### Security (1 change)
|
### Security (1 change)
|
||||||
|
|
||||||
- Upgrade pages to 1.6.3.
|
- Upgrade pages to 1.7.2.
|
||||||
|
|
||||||
|
|
||||||
## 12.0.8
|
## 12.1.8
|
||||||
|
|
||||||
### Security (22 changes)
|
### Security (21 changes)
|
||||||
|
|
||||||
- Ensure only authorised users can create notes on Merge Requests and Issues.
|
- Ensure only authorised users can create notes on Merge Requests and Issues.
|
||||||
- Add :login_recaptcha_protection_enabled setting to prevent bots from brute-force attacks.
|
- Add :login_recaptcha_protection_enabled setting to prevent bots from brute-force attacks.
|
||||||
- Queries for Upload should be scoped by model.
|
|
||||||
- Speed up regexp in namespace format by failing fast after reaching maximum namespace depth.
|
- Speed up regexp in namespace format by failing fast after reaching maximum namespace depth.
|
||||||
- Limit the size of issuable description and comments.
|
- Limit the size of issuable description and comments.
|
||||||
- Send TODOs for comments on commits correctly.
|
- Send TODOs for comments on commits correctly.
|
||||||
|
@ -37,36 +44,349 @@ entry.
|
||||||
- Fix SSRF via DNS rebinding in Kubernetes Integration.
|
- Fix SSRF via DNS rebinding in Kubernetes Integration.
|
||||||
|
|
||||||
|
|
||||||
## 12.0.7
|
## 12.1.7
|
||||||
|
|
||||||
- Unreleased due to QA failure.
|
- Unreleased due to QA failure.
|
||||||
|
|
||||||
## 12.0.6
|
## 12.1.6
|
||||||
|
|
||||||
### Security (2 changes)
|
### Security (2 changes)
|
||||||
|
|
||||||
- Upgrade Gitaly to 1.47.2 to prevent revision flag injection exploits.
|
- Upgrade Gitaly to 1.53.2 to prevent revision flag injection exploits.
|
||||||
- Upgrade pages to 1.6.2 to prevent gitlab api token recovery from cookie.
|
- Upgrade pages to 1.7.1 to prevent gitlab api token recovery from cookie.
|
||||||
|
|
||||||
## 12.0.5
|
## 12.1.5
|
||||||
|
|
||||||
- No changes.
|
- No changes.
|
||||||
|
|
||||||
## 12.0.4
|
## 12.1.4
|
||||||
|
|
||||||
|
### Fixed (3 changes, 1 of them is from the community)
|
||||||
|
|
||||||
|
- Properly translate term in projects list. !30958
|
||||||
|
- Add exclusive lease to mergeability check process. !31082
|
||||||
|
- Fix Docker in Docker (DIND) listen port behavior change by adding DOCKER_TLS_CERTDIR in CI job templates. !31201 (Cameron Boulton)
|
||||||
|
|
||||||
|
### Performance (1 change)
|
||||||
|
|
||||||
|
- Improve job log rendering performance. !31262
|
||||||
|
|
||||||
|
|
||||||
|
## 12.1.3
|
||||||
|
|
||||||
|
### Fixed (11 changes)
|
||||||
|
|
||||||
|
- Prevent multiple confirmation modals from opening when deleting a repository. !30532
|
||||||
|
- Fix the project auto devops API. !30946
|
||||||
|
- Fix "Certificate misses intermediates" UI error when enabling Let's Encrypt integration for pages domain. !30995
|
||||||
|
- Fix xterm css not loading for environment terminal. !31023
|
||||||
|
- Set DOCKER_TLS_CERTDIR in Auto Dev-Ops CI template to fix jobs using Docker-in-Docker. !31078
|
||||||
|
- Set DOCKER_TLS_CERTDIR in CI job templates to fix Docker-in-Docker service. !31080
|
||||||
|
- Support Docker OCI images. !31127
|
||||||
|
- Fix error rendering submodules in MR diffs when there is no .gitmodules. !31162
|
||||||
|
- Fix pdf.js rendering pages in the wrong order. !31222
|
||||||
|
- Fix exception handling in Gitaly autodetection. !31285
|
||||||
|
- Fix bug that caused diffs not to show on MRs with changes to submodules.
|
||||||
|
|
||||||
|
### Performance (1 change)
|
||||||
|
|
||||||
|
- Optimise import performance. !31045
|
||||||
|
|
||||||
|
|
||||||
|
## 12.1.2
|
||||||
|
|
||||||
|
### Security (1 change)
|
||||||
|
|
||||||
|
- Use source project as permissions reference for MergeRequestsController#pipelines.
|
||||||
|
|
||||||
### Security (9 changes)
|
### Security (9 changes)
|
||||||
|
|
||||||
- Restrict slash commands to users who can log in.
|
- Restrict slash commands to users who can log in.
|
||||||
- Patch XSS issue in wiki links.
|
- Patch XSS issue in wiki links.
|
||||||
|
- Queries for Upload should be scoped by model.
|
||||||
- Filter merge request params on the new merge request page.
|
- Filter merge request params on the new merge request page.
|
||||||
- Fix Server Side Request Forgery mitigation bypass.
|
- Fix Server Side Request Forgery mitigation bypass.
|
||||||
- Show badges if pipelines are public otherwise default to project permissions.
|
- Show badges if pipelines are public otherwise default to project permissions.
|
||||||
- Do not allow localhost url redirection in GitHub Integration.
|
- Do not allow localhost url redirection in GitHub Integration.
|
||||||
- Do not show moved issue id for users that cannot read issue.
|
- Do not show moved issue id for users that cannot read issue.
|
||||||
- Use source project as permissions reference for MergeRequestsController#pipelines.
|
|
||||||
- Drop feature to take ownership of trigger token.
|
- Drop feature to take ownership of trigger token.
|
||||||
|
|
||||||
|
|
||||||
|
## 12.1.1
|
||||||
|
|
||||||
|
- No changes.
|
||||||
|
|
||||||
|
## 12.1.0
|
||||||
|
|
||||||
|
### Security (11 changes, 2 of them are from the community)
|
||||||
|
|
||||||
|
- Update tar to 2.2.2. !29949 (Takuya Noguchi)
|
||||||
|
- Update lodash to 4.7.14 and lodash.mergewith to 4.6.2. !30602 (Takuya Noguchi)
|
||||||
|
- Correctly check permissions when creating snippet notes.
|
||||||
|
- Gate MR head_pipeline behind read_pipeline ability.
|
||||||
|
- Prevent Billion Laughs attack.
|
||||||
|
- Add missing authorizations in GraphQL.
|
||||||
|
- Fix Denial of Service for comments when rendering issues/MR comments.
|
||||||
|
- Expose merge requests count based on user access.
|
||||||
|
- Fix DoS vulnerability in color validation regex.
|
||||||
|
- Prevent the detection of merge request templates by unauthorized users.
|
||||||
|
- Persist tmp snippet uploads at users.
|
||||||
|
|
||||||
|
### Removed (7 changes)
|
||||||
|
|
||||||
|
- Disable Kubernetes credential passthrough for managed project-level clusters. !29262
|
||||||
|
- Remove deprecated group routes. !29351
|
||||||
|
- Remove support for creating non-RBAC kubernetes clusters. !29614
|
||||||
|
- Remove Kubernetes service integration and Kubernetes service template from available deployment platforms. !29786
|
||||||
|
- Remove MySQL support. !29790
|
||||||
|
- Remove depreated /u/:username routing. !30044
|
||||||
|
- Remove support for legacy pipeline triggers. !30133
|
||||||
|
|
||||||
|
### Fixed (84 changes, 14 of them are from the community)
|
||||||
|
|
||||||
|
- Update a user's routes after updating their name. !23272
|
||||||
|
- Show poper panel when validation error occurs in admin settings panels. !25434
|
||||||
|
- Expect bytes from Gitaly RPC GetRawChanges. !28164
|
||||||
|
- Sanitize LDAP output in Rake tasks. !28427
|
||||||
|
- Left align mr widget icons and text. !28561
|
||||||
|
- Keep the empty folders in the tree. !29196
|
||||||
|
- Fix incorrect emoji placement in commit diff discussion. !29445
|
||||||
|
- Fix favicon path with uploads of object store. !29482 (Roger Meier)
|
||||||
|
- Remove duplicate trailing +/- char in merge request discussions. !29518
|
||||||
|
- Fix the signup form's username validation messages not displaying. !29678 (Jiaan Louw)
|
||||||
|
- Fix broken environment selector and always display it on monitoring dashboard. !29705
|
||||||
|
- Fix Container Scanning job timeout when using the kubernetes executor. !29706
|
||||||
|
- Look for new branches more carefully. !29761
|
||||||
|
- Fix nested lists unnecessary margin. !29775 (Kuba Kopeć)
|
||||||
|
- Fix reports jobs timing out because of cache. !29780
|
||||||
|
- Fix Double Border in Profile Page. !29784 (Yoginth <@yo>)
|
||||||
|
- Remove minimum character limits for fuzzy searches when using a CTE. !29810
|
||||||
|
- Set default sort method for dashboard projects list. !29830 (David Palubin)
|
||||||
|
- Protect TeamCity builds from triggering when a branch has been deleted. And a MR-option. !29836 (Nikolay Novikov, Raphael Tweitmann)
|
||||||
|
- Fix pipeline schedule does not run correctly when it's scheduled at the same time with the cron worker. !29848
|
||||||
|
- Always shows author of created issue/started discussion/comment in HTML body and text of email. !29886 (Frank van Rest)
|
||||||
|
- Build correct basenames for title search results. !29898
|
||||||
|
- Resolve "500 error when forking via the web IDE button". !29909
|
||||||
|
- Turn commit sha in monitor charts popover to link. !29914
|
||||||
|
- Fix broken URLs for uploads with a plus in the filename. !29915
|
||||||
|
- Retry fetching Kubernetes Secret#token (#63507). !29922
|
||||||
|
- Enforce presence of pipeline when "Pipeline must succeed" project setting is enabled. !29926
|
||||||
|
- Fix unresponsive reply button in discussions. !29936
|
||||||
|
- Allow asynchronous rebase operations to be monitored. !29940
|
||||||
|
- Resolve Avatar in Please sign in pattern too large. !29944
|
||||||
|
- Persist the cluster a deployment was deployed to. !29960
|
||||||
|
- Fix runner tags search dropdown being empty when there are tags. !29985
|
||||||
|
- Display the correct amount of projects being migrated/rolled-back to Hashed Storage when specifying ranges. !29996
|
||||||
|
- Resolve Environment details header border misaligned. !30011
|
||||||
|
- Correct link to docs for External Dashboard. !30019
|
||||||
|
- Fix Jupyter-Git integration. !30020 (Amit Rathi)
|
||||||
|
- Update Mermaid to 8.1.0. !30036
|
||||||
|
- Fix background migrations failing with unused replication slot. !30042
|
||||||
|
- Disable Rails SQL query cache when applying service templates. !30060
|
||||||
|
- Set higher TTL for write lock of trace to prevent concurrent archiving. !30064
|
||||||
|
- Fix charts on Cluster health page. !30073
|
||||||
|
- Display boards filter bar on mobile. !30120
|
||||||
|
- Fix IDE editor not showing when switching back from preview. !30135
|
||||||
|
- Support note position tracing on an image. !30158
|
||||||
|
- Replace slugifyWithHyphens with improved slugify function. !30172 (Luke Ward)
|
||||||
|
- 'Open' and 'Closed' issue board lists no longer display a redundant tooltip. !30187
|
||||||
|
- Fix pipelines table to update without refreshing after action. !30190
|
||||||
|
- Change ruby_process_start_time_seconds metric to unix timestamp instead of seconds from boot. !30195
|
||||||
|
- Fix attachments using the wrong URLs in e-mails. !30197
|
||||||
|
- Make sure UnicornSampler is started only in master process. !30215
|
||||||
|
- Don't show image diff note on text file. !30221
|
||||||
|
- Fix median counting for cycle analytics. !30229
|
||||||
|
- In WebIDE allow adding new entries of the same name as deleted entry. !30239
|
||||||
|
- Don't let logged out user do manual order. !30264
|
||||||
|
- Skip spam check for task list updates. !30279
|
||||||
|
- Make Housekeeping button do a full garbage collection. !30289
|
||||||
|
- Removing an image should not output binary data. !30314
|
||||||
|
- Fix spacing issues for toasts. !30345
|
||||||
|
- Fix race in forbid_sidekiq_in_transactions.rb. !30359
|
||||||
|
- Fixed back navigation for projects filter. !30373
|
||||||
|
- Fix environments broken terminal. !30401
|
||||||
|
- Fix invalid SSL certificate errors on Drone CI service. !30422
|
||||||
|
- Fix subgroup url in search drop down. !30457
|
||||||
|
- Make unicorn_workers to return meaningful results. !30506
|
||||||
|
- Fix wrong URL when creating milestones from instance milestones dashboard. !30512
|
||||||
|
- Fixed incorrect line wrap for assignee label in issues. !30523 (Marc Schwede)
|
||||||
|
- Improves section header whitespace on the CI/CD Charts page. !30531
|
||||||
|
- Prevent multiple confirmation modals from opening when deleting a repository. !30532
|
||||||
|
- Aligns CI icon in Merge Request dashboard. !30558
|
||||||
|
- Add text-secondary to controls in project list. !30567
|
||||||
|
- Review Tools: Add large z-index to toolbar. !30583
|
||||||
|
- Hide restricted and disallowed visibility radios. !30590
|
||||||
|
- Resolve Label picker: Line break on long label titles. !30610
|
||||||
|
- Fix a bug that prevented projects containing merge request diff comments from being imported. !30630
|
||||||
|
- I fixed z index bug in diff page. !30657 (Faruk Can)
|
||||||
|
- Allow client authentication method to be configured for OpenID Connect. !30683 (Vincent Fazio)
|
||||||
|
- Fix commenting before discussions are loaded. !30724
|
||||||
|
- Fix linebreak rendering in Mermaid flowcharts. !30730
|
||||||
|
- Make httpclient respect system SSL configuration. !30749
|
||||||
|
- Bump fog-aws to v3.5.2. !30803
|
||||||
|
- API: Allow changing only ci_default_git_depth. !30888 (Mathieu Parent)
|
||||||
|
- Search issuables by iids. (Riccardo Padovani)
|
||||||
|
- Fix broken warnings while Editing Issues and Edit File on MR.
|
||||||
|
- Make sure we are receiving the proper information on the MR Popover by updating the IID in the graphql query.
|
||||||
|
|
||||||
|
### Changed (39 changes, 8 of them are from the community)
|
||||||
|
|
||||||
|
- Improve group list UI. !26542
|
||||||
|
- Backport and Docs for Paginate license management and add license search. !27602
|
||||||
|
- Update merge requests section description text on project settings page. !27838
|
||||||
|
- Knative version bump 0.5 -> 0.6. !28798 (Chris Baumbauer)
|
||||||
|
- Add salesforce logo for salesforce SSO. !28857
|
||||||
|
- Enforced requirements for UltraAuth users. !28941 (Kartikey Tanna)
|
||||||
|
- Return 400 when deleting tags more often than once per hour. !29448
|
||||||
|
- Add identity information to external authorization requests. !29461
|
||||||
|
- Enable just-in-time Kubernetes resource creation for project-level clusters. !29515
|
||||||
|
- renamed discussion to thread in merge-request and issue timeline. !29553 (Michel Engelen)
|
||||||
|
- Changed HTTP Status Code for disabled repository on /branches and /commits to 404. !29585 (Sam Battalio)
|
||||||
|
- Enable Git object pools. !29595 (jramsay)
|
||||||
|
- Updated container registry to display error message when special characters in path. Documentation has also been updated. !29616
|
||||||
|
- Allow developers to delete tags. !29668
|
||||||
|
- Will not update issue timestamps when changing positions in a list. !29677
|
||||||
|
- Include a link back to the MR for Visual Review feedback form. !29719
|
||||||
|
- Improve discussion reply buttons layout and how jump to next discussion button appears. !29779
|
||||||
|
- Renders a pre-release tag for releases. !29797
|
||||||
|
- Migrate NULL values for users.private_profile column and update users API to reject null value for private_profile. !29888
|
||||||
|
- Re-name files in Web IDE in a more natural way. !29948
|
||||||
|
- Include events from subgroups in group's activity. !29953 (Fabian Schneider @fabsrc)
|
||||||
|
- Upgrade to Gitaly v1.49.0. !29990
|
||||||
|
- Remove group and instance clusters feature flag. !30124
|
||||||
|
- Add support for creating random passwords in user creation API. !30138
|
||||||
|
- Support CIDR notation in IP rate limiter. !30146
|
||||||
|
- Add Redis call details in Peek performance bar. !30191
|
||||||
|
- Create Knative role and binding with service account. !30235
|
||||||
|
- Add cleanup migration for MR's multiple assignees. !30261
|
||||||
|
- Updates PHP template to php:latest to ensure always targeting latest stable. !30319 (Paul Giberson)
|
||||||
|
- Format `from` and `to` fields in JSON audit log. !30333
|
||||||
|
- Upgrade to Gitaly v1.51.0. !30353
|
||||||
|
- Modify cycle analytics on project level. !30356
|
||||||
|
- Extract clair version as CLAIR_EXECUTABLE_VERSION variable and update clair executable from v8 to v11. !30396
|
||||||
|
- Upgrade Rouge to 3.5.1. !30431
|
||||||
|
- Move multiple issue boards to core. !30503
|
||||||
|
- Upgrade to Gitaly v1.52.0. !30568
|
||||||
|
- Upgrade to Gitaly v1.53.0. !30614
|
||||||
|
- Open WebIDE in fork when user doesn't have access. !30642
|
||||||
|
- Propagate python version variable. (Can Eldem)
|
||||||
|
|
||||||
|
### Performance (25 changes, 1 of them is from the community)
|
||||||
|
|
||||||
|
- Remove tooltip directive on project avatar image component. !29631 (George Tsiolis)
|
||||||
|
- Use Rugged if we detect storage is NFS and we can access the disk. !29725
|
||||||
|
- Add endpoint for fetching diverging commit counts. !29802
|
||||||
|
- Cache feature flag names in Redis for a minute. !29816
|
||||||
|
- Avoid storing backtraces from Bitbucket Cloud imports in the database. !29862
|
||||||
|
- Remove import columns from projects table. !29863
|
||||||
|
- Enable Gitaly ref name caching for discussions.json. !29951
|
||||||
|
- Allow caching of negative FindCommit matches. !29952
|
||||||
|
- Eliminate N+1 queries in Dashboard::TodosController. !29954
|
||||||
|
- Memoize non-existent custom appearances. !29957
|
||||||
|
- Add a separate endpoint for fetching MRs serialized as widgets. !29979
|
||||||
|
- Use CTE to fetch clusters hierarchy in single query. !30063
|
||||||
|
- Enable Gitaly ref caching for SearchController. !30105
|
||||||
|
- Avoid loading pipeline status in search results. !30111
|
||||||
|
- Improve performance of MergeRequestsController#ci_environment_status endpoint. !30224
|
||||||
|
- Add a memory cache local to the thread to reduce Redis load. !30233
|
||||||
|
- Cache Flipper persisted names directly to local memory storage. !30265
|
||||||
|
- Limit amount of JUnit tests returned. !30274
|
||||||
|
- Cache Flipper feature flags in L1 and L2 caches. !30276
|
||||||
|
- Prevent amplification of ReactiveCachingWorker jobs upon failures. !30432
|
||||||
|
- Allow ReactiveCaching to support nil value. !30456
|
||||||
|
- Improve performance of fetching environments statuses. !30560
|
||||||
|
- Do Redis lookup in batches in ActiveSession.sessions_from_ids. !30561
|
||||||
|
- Remove catfile cache feature flag. !30750
|
||||||
|
- Fix Gitaly auto-detection caching. !30954
|
||||||
|
|
||||||
|
### Added (46 changes, 12 of them are from the community)
|
||||||
|
|
||||||
|
- Document the negative commit message push rule for the API. !14004 (Maikel Vlasman)
|
||||||
|
- Expose saml_provider_id in the users API. !14045
|
||||||
|
- Improve Project API. !28327 (Mathieu Parent)
|
||||||
|
- Remove Sentry from application settings. !28447 (Roger Meier)
|
||||||
|
- Implement borderless discussion design with new reply field. !28580
|
||||||
|
- Enable terminals for instance and group clusters. !28613
|
||||||
|
- Resolve Multiple discussions per line in merge request diffs. !28748
|
||||||
|
- Adds link to Grafana in Admin > Monitoring settings when grafana is enabled in config. !28937 (Romain Maneschi)
|
||||||
|
- Bring Manual Ordering on Issue List. !29410
|
||||||
|
- Added commit type to tree GraphQL response. !29412
|
||||||
|
- New API for User Counts, updates on success of an MR the count on top and in other tabs. !29441
|
||||||
|
- Add option to limit time tracking units to hours. !29469 (Jon Kolb)
|
||||||
|
- Add confirmation for registry image deletion. !29505
|
||||||
|
- Sync merge ref upon mergeability check. !29569
|
||||||
|
- Show an Upcoming Status for Releases. !29577
|
||||||
|
- Add order_by and sort params to list runner jobs api. !29629 (Sujay Patel)
|
||||||
|
- Allow custom username for deploy tokens. !29639
|
||||||
|
- Add a verified pill next to email addresses under the admin users section. !29669
|
||||||
|
- Add rake task to clean orphan artifact files. !29681
|
||||||
|
- Render GFM in GraphQL. !29700
|
||||||
|
- Upgrade asciidoctor version to 2.0.10. !29741 (Rajendra Kadam)
|
||||||
|
- Allow auto-completing scoped labels. !29749
|
||||||
|
- Enable syntax highlighting for AsciiDoc. !29835 (Guillaume Grossetie)
|
||||||
|
- Expose placeholder element for metrics charts in GFM. !29861
|
||||||
|
- Added a min schema version check to db:migrate. !29882
|
||||||
|
- Extract zoom link from issue and pass to frontend. !29910 (raju249)
|
||||||
|
- GraphQL mutations for add, remove and toggle emoji. !29919
|
||||||
|
- Labeled issue boards can now collapse. !29955
|
||||||
|
- Allow Ingress to be uninstalled from the UI. !29977
|
||||||
|
- Add permission check to metrics dashboards endpoint. !30017
|
||||||
|
- Allow JupyterHub to be uninstalled from the UI. !30097
|
||||||
|
- Allow GitLab Runner to be uninstalled from the UI. !30176
|
||||||
|
- GraphQL mutations for managing Notes. !30210
|
||||||
|
- Add API for CRUD group clusters. !30213
|
||||||
|
- Add endpoint to move multiple issues in boards. !30216
|
||||||
|
- Enable terminals button for group clusters. !30255
|
||||||
|
- Prevent excessive sanitization of AsciiDoc ouptut. !30290 (Guillaume Grossetie)
|
||||||
|
- Extend `MergeToRefService` to create merge ref from an arbitrary ref. !30361
|
||||||
|
- Add CI variable to provide GitLab HOST. !30417
|
||||||
|
- Add migration for adding rule_type to approval_project_rules. !30575
|
||||||
|
- Enable section anchors in Asciidoctor. !30666 (Guillaume Grossetie)
|
||||||
|
- Preserve footnote link ids in Asciidoctor. !30790 (Guillaume Grossetie)
|
||||||
|
- Add support for generating SSL certificates for custon pages domains through Let's Encrypt.
|
||||||
|
- Introduce default: for gitlab-ci.yml.
|
||||||
|
- Move Multiple Issue Boards for Projects to Core.
|
||||||
|
- Add Gitaly data to the usage ping.
|
||||||
|
|
||||||
|
### Other (35 changes, 15 of them are from the community)
|
||||||
|
|
||||||
|
- Remove unresolved class and fixed height in discussion header. !28440 (David Palubin)
|
||||||
|
- Moved EE/CE code differences for file `app/views/search/_category.html.haml` into CE. !28755 (Michel Engelen)
|
||||||
|
- Changes "Todo" to "To Do" in the UI for clarity. !28844
|
||||||
|
- Migrate GitLab managed project-level clusters to unmanaged if a Kubernetes namespace was unable to be created. !29251
|
||||||
|
- Migrate GitLab managed project-level clusters to unmanaged if they are missing a Kubernetes service account token. !29648
|
||||||
|
- Add strategies column to operations_feature_flag_scopes table. !29808
|
||||||
|
- Disallow `NULL` values for `geo_nodes.primary` column. !29818 (Arun Kumar Mohan)
|
||||||
|
- Replace 'JIRA' with 'Jira'. !29849 (Takuya Noguchi)
|
||||||
|
- Support jsonb default in add_column_with_default migration helper. !29871
|
||||||
|
- Update pagination prev and next texts. !29911
|
||||||
|
- Adds metrics to measure cost of expensive operations. !29928
|
||||||
|
- Always allow access to health endpoints from localhost in dev. !29930
|
||||||
|
- Update GitLab Runner Helm Chart to 0.6.0. !29982
|
||||||
|
- Use darker gray color for system note metadata and edited text. !30054
|
||||||
|
- Fix typo in docs about Elasticsearch. !30162 (Takuya Noguchi)
|
||||||
|
- Fix typo in code comments about Elasticsearch. !30163 (Takuya Noguchi)
|
||||||
|
- Update mixin-deep to 1.3.2. !30223 (Takuya Noguchi)
|
||||||
|
- Migrate markdown header_spec.js to Jest. !30228 (Martin Hobert)
|
||||||
|
- Remove istanbul JavaScript package. !30232 (Takuya Noguchi)
|
||||||
|
- Centralize markdownlint configuration. !30263
|
||||||
|
- Use PostgreSQL 9.6.11 in CI tests. !30270 (Takuya Noguchi)
|
||||||
|
- Fix typo in updateResolvableDiscussionsCounts action. !30278 (Frank van Rest)
|
||||||
|
- Change color for namespace in commit search. !30312
|
||||||
|
- Remove applySuggestion from notes service. !30399 (Frank van Rest)
|
||||||
|
- Improved readability of storage statistics in group / project admin area. !30406
|
||||||
|
- Alignign empty container registry message with design guidelines. !30502
|
||||||
|
- Remove toggleAward from notes service. !30536 (Frank van Rest)
|
||||||
|
- Remove deleteNote from notes service. !30537 (Frank van Rest)
|
||||||
|
- change the use of boardService in favor of boardsStore on footer for the board component. !30616 (eduarmreyes)
|
||||||
|
- Update example Prometheus scrape config. !30739
|
||||||
|
- Update GitLab Pages to v1.7.0.
|
||||||
|
- Add token_encrypted column to operations_feature_flags_clients table.
|
||||||
|
- Removes EE diff for app/views/profiles/preferences/show.html.haml.
|
||||||
|
- Removes EE differences for app/views/layouts/fullscreen.html.haml.
|
||||||
|
- Removes EE differences for app/views/admin/users/show.html.haml.
|
||||||
|
|
||||||
|
|
||||||
## 12.0.3 (2019-06-27)
|
## 12.0.3 (2019-06-27)
|
||||||
|
|
||||||
- No changes.
|
- No changes.
|
||||||
|
@ -415,6 +735,15 @@ entry.
|
||||||
- Moves snowplow to CE repo.
|
- Moves snowplow to CE repo.
|
||||||
|
|
||||||
|
|
||||||
|
## 11.11.4 (2019-06-26)
|
||||||
|
|
||||||
|
### Fixed (3 changes)
|
||||||
|
|
||||||
|
- Fix Fogbugz Importer not working. !29383
|
||||||
|
- Fix scrolling to top on assignee change. !29500
|
||||||
|
- Fix IDE commit using latest ref in branch and overriding contents. !29769
|
||||||
|
|
||||||
|
|
||||||
## 11.11.3 (2019-06-10)
|
## 11.11.3 (2019-06-10)
|
||||||
|
|
||||||
### Fixed (5 changes)
|
### Fixed (5 changes)
|
||||||
|
@ -628,6 +957,27 @@ entry.
|
||||||
- Add some frozen string to spec/**/*.rb. (gfyoung)
|
- Add some frozen string to spec/**/*.rb. (gfyoung)
|
||||||
|
|
||||||
|
|
||||||
|
## 11.10.8 (2019-06-27)
|
||||||
|
|
||||||
|
- No changes.
|
||||||
|
### Security (10 changes)
|
||||||
|
|
||||||
|
- Fix Denial of Service for comments when rendering issues/MR comments.
|
||||||
|
- Gate MR head_pipeline behind read_pipeline ability.
|
||||||
|
- Fix DoS vulnerability in color validation regex.
|
||||||
|
- Expose merge requests count based on user access.
|
||||||
|
- Persist tmp snippet uploads at users.
|
||||||
|
- Add missing authorizations in GraphQL.
|
||||||
|
- Disable Rails SQL query cache when applying service templates.
|
||||||
|
- Prevent Billion Laughs attack.
|
||||||
|
- Correctly check permissions when creating snippet notes.
|
||||||
|
- Prevent the detection of merge request templates by unauthorized users.
|
||||||
|
|
||||||
|
### Performance (1 change)
|
||||||
|
|
||||||
|
- Add improvements to global search of issues and merge requests. !27817
|
||||||
|
|
||||||
|
|
||||||
## 11.10.6 (2019-06-04)
|
## 11.10.6 (2019-06-04)
|
||||||
|
|
||||||
### Fixed (7 changes, 1 of them is from the community)
|
### Fixed (7 changes, 1 of them is from the community)
|
||||||
|
|
|
@ -19,4 +19,5 @@ unless helper.release_automation?
|
||||||
danger.import_dangerfile(path: 'danger/single_codebase')
|
danger.import_dangerfile(path: 'danger/single_codebase')
|
||||||
danger.import_dangerfile(path: 'danger/gitlab_ui_wg')
|
danger.import_dangerfile(path: 'danger/gitlab_ui_wg')
|
||||||
danger.import_dangerfile(path: 'danger/ce_ee_vue_templates')
|
danger.import_dangerfile(path: 'danger/ce_ee_vue_templates')
|
||||||
|
danger.import_dangerfile(path: 'danger/only_documentation')
|
||||||
end
|
end
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.47.3
|
1.53.3
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.6.3
|
1.7.2
|
||||||
|
|
28
Gemfile
28
Gemfile
|
@ -1,6 +1,6 @@
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
gem 'rails', '5.1.7'
|
gem 'rails', '5.2.3'
|
||||||
|
|
||||||
# Improves copy-on-write performance for MRI
|
# Improves copy-on-write performance for MRI
|
||||||
gem 'nakayoshi_fork', '~> 0.0.4'
|
gem 'nakayoshi_fork', '~> 0.0.4'
|
||||||
|
@ -11,7 +11,7 @@ gem 'responders', '~> 2.0'
|
||||||
gem 'sprockets', '~> 3.7.0'
|
gem 'sprockets', '~> 3.7.0'
|
||||||
|
|
||||||
# Default values for AR models
|
# Default values for AR models
|
||||||
gem 'gitlab-default_value_for', '~> 3.1.1', require: 'default_value_for'
|
gem 'default_value_for', '~> 3.2.0'
|
||||||
|
|
||||||
# Supported DBs
|
# Supported DBs
|
||||||
gem 'mysql2', '~> 0.4.10', group: :mysql
|
gem 'mysql2', '~> 0.4.10', group: :mysql
|
||||||
|
@ -84,6 +84,7 @@ gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
|
||||||
gem 'graphql', '~> 1.8.0'
|
gem 'graphql', '~> 1.8.0'
|
||||||
gem 'graphiql-rails', '~> 1.4.10'
|
gem 'graphiql-rails', '~> 1.4.10'
|
||||||
gem 'apollo_upload_server', '~> 2.0.0.beta3'
|
gem 'apollo_upload_server', '~> 2.0.0.beta3'
|
||||||
|
gem 'graphql-docs', '~> 1.6.0', group: [:development, :test]
|
||||||
|
|
||||||
# Disable strong_params so that Mash does not respond to :permitted?
|
# Disable strong_params so that Mash does not respond to :permitted?
|
||||||
gem 'hashie-forbidden_attributes'
|
gem 'hashie-forbidden_attributes'
|
||||||
|
@ -99,7 +100,7 @@ gem 'carrierwave', '~> 1.3'
|
||||||
gem 'mini_magick'
|
gem 'mini_magick'
|
||||||
|
|
||||||
# for backups
|
# for backups
|
||||||
gem 'fog-aws', '~> 3.3'
|
gem 'fog-aws', '~> 3.5'
|
||||||
# Locked until fog-google resolves https://github.com/fog/fog-google/issues/421.
|
# Locked until fog-google resolves https://github.com/fog/fog-google/issues/421.
|
||||||
# Also see config/initializers/fog_core_patch.rb.
|
# Also see config/initializers/fog_core_patch.rb.
|
||||||
gem 'fog-core', '= 2.1.0'
|
gem 'fog-core', '= 2.1.0'
|
||||||
|
@ -129,10 +130,10 @@ gem 'rdoc', '~> 6.0'
|
||||||
gem 'org-ruby', '~> 0.9.12'
|
gem 'org-ruby', '~> 0.9.12'
|
||||||
gem 'creole', '~> 0.5.0'
|
gem 'creole', '~> 0.5.0'
|
||||||
gem 'wikicloth', '0.8.1'
|
gem 'wikicloth', '0.8.1'
|
||||||
gem 'asciidoctor', '~> 1.5.8'
|
gem 'asciidoctor', '~> 2.0.10'
|
||||||
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
|
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
|
||||||
gem 'asciidoctor-plantuml', '0.0.8'
|
gem 'asciidoctor-plantuml', '0.0.9'
|
||||||
gem 'rouge', '~> 3.1'
|
gem 'rouge', '~> 3.5'
|
||||||
gem 'truncato', '~> 0.7.11'
|
gem 'truncato', '~> 0.7.11'
|
||||||
gem 'bootstrap_form', '~> 4.2.0'
|
gem 'bootstrap_form', '~> 4.2.0'
|
||||||
gem 'nokogiri', '~> 1.10.3'
|
gem 'nokogiri', '~> 1.10.3'
|
||||||
|
@ -211,7 +212,7 @@ gem 'discordrb-webhooks-blackst0ne', '~> 3.3', require: false
|
||||||
# HipChat integration
|
# HipChat integration
|
||||||
gem 'hipchat', '~> 1.5.0'
|
gem 'hipchat', '~> 1.5.0'
|
||||||
|
|
||||||
# JIRA integration
|
# Jira integration
|
||||||
gem 'jira-ruby', '~> 1.4'
|
gem 'jira-ruby', '~> 1.4'
|
||||||
|
|
||||||
# Flowdock integration
|
# Flowdock integration
|
||||||
|
@ -300,13 +301,16 @@ gem 'peek-pg', '~> 1.3.0', group: :postgres
|
||||||
gem 'peek-rblineprof', '~> 0.2.0'
|
gem 'peek-rblineprof', '~> 0.2.0'
|
||||||
gem 'peek-redis', '~> 1.2.0'
|
gem 'peek-redis', '~> 1.2.0'
|
||||||
|
|
||||||
|
# Memory benchmarks
|
||||||
|
gem 'derailed_benchmarks', require: false
|
||||||
|
|
||||||
# Metrics
|
# Metrics
|
||||||
group :metrics do
|
group :metrics do
|
||||||
gem 'method_source', '~> 0.8', require: false
|
gem 'method_source', '~> 0.8', require: false
|
||||||
gem 'influxdb', '~> 0.2', require: false
|
gem 'influxdb', '~> 0.2', require: false
|
||||||
|
|
||||||
# Prometheus
|
# Prometheus
|
||||||
gem 'prometheus-client-mmap', '~> 0.9.4'
|
gem 'prometheus-client-mmap', '~> 0.9.8'
|
||||||
gem 'raindrops', '~> 0.18'
|
gem 'raindrops', '~> 0.18'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -336,7 +340,7 @@ group :development, :test do
|
||||||
|
|
||||||
gem 'database_cleaner', '~> 1.7.0'
|
gem 'database_cleaner', '~> 1.7.0'
|
||||||
gem 'factory_bot_rails', '~> 4.8.2'
|
gem 'factory_bot_rails', '~> 4.8.2'
|
||||||
gem 'rspec-rails', '~> 3.7.0'
|
gem 'rspec-rails', '~> 3.8.0'
|
||||||
gem 'rspec-retry', '~> 0.6.1'
|
gem 'rspec-retry', '~> 0.6.1'
|
||||||
gem 'rspec_profiling', '~> 0.0.5'
|
gem 'rspec_profiling', '~> 0.0.5'
|
||||||
gem 'rspec-set', '~> 0.1.3'
|
gem 'rspec-set', '~> 0.1.3'
|
||||||
|
@ -365,6 +369,7 @@ group :development, :test do
|
||||||
gem 'haml_lint', '~> 0.31.0', require: false
|
gem 'haml_lint', '~> 0.31.0', require: false
|
||||||
gem 'simplecov', '~> 0.16.1', require: false
|
gem 'simplecov', '~> 0.16.1', require: false
|
||||||
gem 'bundler-audit', '~> 0.5.0', require: false
|
gem 'bundler-audit', '~> 0.5.0', require: false
|
||||||
|
gem 'mdl', '~> 0.5.0', require: false
|
||||||
|
|
||||||
gem 'benchmark-ips', '~> 2.3.0', require: false
|
gem 'benchmark-ips', '~> 2.3.0', require: false
|
||||||
|
|
||||||
|
@ -374,7 +379,6 @@ group :development, :test do
|
||||||
gem 'activerecord_sane_schema_dumper', '1.0'
|
gem 'activerecord_sane_schema_dumper', '1.0'
|
||||||
|
|
||||||
gem 'stackprof', '~> 0.2.10', require: false
|
gem 'stackprof', '~> 0.2.10', require: false
|
||||||
gem 'derailed_benchmarks', require: false
|
|
||||||
|
|
||||||
gem 'simple_po_parser', '~> 1.1.2', require: false
|
gem 'simple_po_parser', '~> 1.1.2', require: false
|
||||||
|
|
||||||
|
@ -417,7 +421,7 @@ gem 'vmstat', '~> 2.3.0'
|
||||||
gem 'sys-filesystem', '~> 1.1.6'
|
gem 'sys-filesystem', '~> 1.1.6'
|
||||||
|
|
||||||
# SSH host key support
|
# SSH host key support
|
||||||
gem 'net-ssh', '~> 5.0'
|
gem 'net-ssh', '~> 5.2'
|
||||||
gem 'sshkey', '~> 2.0'
|
gem 'sshkey', '~> 2.0'
|
||||||
|
|
||||||
# Required for ED25519 SSH host key support
|
# Required for ED25519 SSH host key support
|
||||||
|
@ -427,7 +431,7 @@ group :ed25519 do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Gitaly GRPC client
|
# Gitaly GRPC client
|
||||||
gem 'gitaly-proto', '~> 1.32.0', require: 'gitaly'
|
gem 'gitaly-proto', '~> 1.37.0', require: 'gitaly'
|
||||||
|
|
||||||
gem 'grpc', '~> 1.19.0'
|
gem 'grpc', '~> 1.19.0'
|
||||||
|
|
||||||
|
|
175
Gemfile.lock
175
Gemfile.lock
|
@ -6,44 +6,48 @@ GEM
|
||||||
ace-rails-ap (4.1.2)
|
ace-rails-ap (4.1.2)
|
||||||
acme-client (2.0.2)
|
acme-client (2.0.2)
|
||||||
faraday (~> 0.9, >= 0.9.1)
|
faraday (~> 0.9, >= 0.9.1)
|
||||||
actioncable (5.1.7)
|
actioncable (5.2.3)
|
||||||
actionpack (= 5.1.7)
|
actionpack (= 5.2.3)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (~> 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
actionmailer (5.1.7)
|
actionmailer (5.2.3)
|
||||||
actionpack (= 5.1.7)
|
actionpack (= 5.2.3)
|
||||||
actionview (= 5.1.7)
|
actionview (= 5.2.3)
|
||||||
activejob (= 5.1.7)
|
activejob (= 5.2.3)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.1.7)
|
actionpack (5.2.3)
|
||||||
actionview (= 5.1.7)
|
actionview (= 5.2.3)
|
||||||
activesupport (= 5.1.7)
|
activesupport (= 5.2.3)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-test (>= 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.1.7)
|
actionview (5.2.3)
|
||||||
activesupport (= 5.1.7)
|
activesupport (= 5.2.3)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.4)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||||
activejob (5.1.7)
|
activejob (5.2.3)
|
||||||
activesupport (= 5.1.7)
|
activesupport (= 5.2.3)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.1.7)
|
activemodel (5.2.3)
|
||||||
activesupport (= 5.1.7)
|
activesupport (= 5.2.3)
|
||||||
activerecord (5.1.7)
|
activerecord (5.2.3)
|
||||||
activemodel (= 5.1.7)
|
activemodel (= 5.2.3)
|
||||||
activesupport (= 5.1.7)
|
activesupport (= 5.2.3)
|
||||||
arel (~> 8.0)
|
arel (>= 9.0)
|
||||||
activerecord-explain-analyze (0.1.0)
|
activerecord-explain-analyze (0.1.0)
|
||||||
activerecord (>= 4)
|
activerecord (>= 4)
|
||||||
pg
|
pg
|
||||||
activerecord_sane_schema_dumper (1.0)
|
activerecord_sane_schema_dumper (1.0)
|
||||||
rails (>= 5, < 6)
|
rails (>= 5, < 6)
|
||||||
activesupport (5.1.7)
|
activestorage (5.2.3)
|
||||||
|
actionpack (= 5.2.3)
|
||||||
|
activerecord (= 5.2.3)
|
||||||
|
marcel (~> 0.3.1)
|
||||||
|
activesupport (5.2.3)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
|
@ -60,17 +64,17 @@ GEM
|
||||||
apollo_upload_server (2.0.0.beta.3)
|
apollo_upload_server (2.0.0.beta.3)
|
||||||
graphql (>= 1.8)
|
graphql (>= 1.8)
|
||||||
rails (>= 4.2)
|
rails (>= 4.2)
|
||||||
arel (8.0.0)
|
arel (9.0.0)
|
||||||
asana (0.8.1)
|
asana (0.8.1)
|
||||||
faraday (~> 0.9)
|
faraday (~> 0.9)
|
||||||
faraday_middleware (~> 0.9)
|
faraday_middleware (~> 0.9)
|
||||||
faraday_middleware-multi_json (~> 0.0)
|
faraday_middleware-multi_json (~> 0.0)
|
||||||
oauth2 (~> 1.0)
|
oauth2 (~> 1.0)
|
||||||
asciidoctor (1.5.8)
|
asciidoctor (2.0.10)
|
||||||
asciidoctor-include-ext (0.3.1)
|
asciidoctor-include-ext (0.3.1)
|
||||||
asciidoctor (>= 1.5.6, < 3.0.0)
|
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||||
asciidoctor-plantuml (0.0.8)
|
asciidoctor-plantuml (0.0.9)
|
||||||
asciidoctor (~> 1.5)
|
asciidoctor (>= 1.5.6, < 3.0.0)
|
||||||
ast (2.4.0)
|
ast (2.4.0)
|
||||||
atomic (1.1.99)
|
atomic (1.1.99)
|
||||||
attr_encrypted (3.1.0)
|
attr_encrypted (3.1.0)
|
||||||
|
@ -163,6 +167,8 @@ GEM
|
||||||
html-pipeline
|
html-pipeline
|
||||||
declarative (0.0.10)
|
declarative (0.0.10)
|
||||||
declarative-option (0.1.0)
|
declarative-option (0.1.0)
|
||||||
|
default_value_for (3.2.0)
|
||||||
|
activerecord (>= 3.2.0, < 6.0)
|
||||||
derailed_benchmarks (1.3.5)
|
derailed_benchmarks (1.3.5)
|
||||||
benchmark-ips (~> 2)
|
benchmark-ips (~> 2)
|
||||||
get_process_mem (~> 0)
|
get_process_mem (~> 0)
|
||||||
|
@ -214,6 +220,8 @@ GEM
|
||||||
excon (0.62.0)
|
excon (0.62.0)
|
||||||
execjs (2.6.0)
|
execjs (2.6.0)
|
||||||
expression_parser (0.9.0)
|
expression_parser (0.9.0)
|
||||||
|
extended-markdown-filter (0.6.0)
|
||||||
|
html-pipeline (~> 2.0)
|
||||||
factory_bot (4.8.2)
|
factory_bot (4.8.2)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
factory_bot_rails (4.8.2)
|
factory_bot_rails (4.8.2)
|
||||||
|
@ -245,7 +253,7 @@ GEM
|
||||||
fog-json
|
fog-json
|
||||||
ipaddress (~> 0.8)
|
ipaddress (~> 0.8)
|
||||||
xml-simple (~> 1.1)
|
xml-simple (~> 1.1)
|
||||||
fog-aws (3.3.0)
|
fog-aws (3.5.2)
|
||||||
fog-core (~> 2.1)
|
fog-core (~> 2.1)
|
||||||
fog-json (~> 1.1)
|
fog-json (~> 1.1)
|
||||||
fog-xml (~> 0.1)
|
fog-xml (~> 0.1)
|
||||||
|
@ -288,6 +296,7 @@ GEM
|
||||||
fuubar (2.2.0)
|
fuubar (2.2.0)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
ruby-progressbar (~> 1.4)
|
ruby-progressbar (~> 1.4)
|
||||||
|
gemoji (3.0.1)
|
||||||
gemojione (3.3.0)
|
gemojione (3.3.0)
|
||||||
json
|
json
|
||||||
get_process_mem (0.2.3)
|
get_process_mem (0.2.3)
|
||||||
|
@ -301,11 +310,9 @@ GEM
|
||||||
gettext_i18n_rails (>= 0.7.1)
|
gettext_i18n_rails (>= 0.7.1)
|
||||||
po_to_json (>= 1.0.0)
|
po_to_json (>= 1.0.0)
|
||||||
rails (>= 3.2.0)
|
rails (>= 3.2.0)
|
||||||
gitaly-proto (1.32.0)
|
gitaly-proto (1.37.0)
|
||||||
grpc (~> 1.0)
|
grpc (~> 1.0)
|
||||||
github-markup (1.7.0)
|
github-markup (1.7.0)
|
||||||
gitlab-default_value_for (3.1.1)
|
|
||||||
activerecord (>= 3.2.0, < 6.0)
|
|
||||||
gitlab-labkit (0.3.0)
|
gitlab-labkit (0.3.0)
|
||||||
actionpack (~> 5)
|
actionpack (~> 5)
|
||||||
activesupport (~> 5)
|
activesupport (~> 5)
|
||||||
|
@ -370,6 +377,14 @@ GEM
|
||||||
railties
|
railties
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
graphql (1.8.1)
|
graphql (1.8.1)
|
||||||
|
graphql-docs (1.6.0)
|
||||||
|
commonmarker (~> 0.16)
|
||||||
|
escape_utils (~> 1.2)
|
||||||
|
extended-markdown-filter (~> 0.4)
|
||||||
|
gemoji (~> 3.0)
|
||||||
|
graphql (~> 1.6)
|
||||||
|
html-pipeline (~> 2.8)
|
||||||
|
sass (~> 3.4)
|
||||||
grpc (1.19.0)
|
grpc (1.19.0)
|
||||||
google-protobuf (~> 3.1)
|
google-protobuf (~> 3.1)
|
||||||
googleapis-common-protos-types (~> 1.0.0)
|
googleapis-common-protos-types (~> 1.0.0)
|
||||||
|
@ -459,6 +474,7 @@ GEM
|
||||||
kgio (2.11.2)
|
kgio (2.11.2)
|
||||||
knapsack (1.17.0)
|
knapsack (1.17.0)
|
||||||
rake
|
rake
|
||||||
|
kramdown (1.17.0)
|
||||||
kubeclient (4.2.2)
|
kubeclient (4.2.2)
|
||||||
http (~> 3.0)
|
http (~> 3.0)
|
||||||
recursive-open-struct (~> 1.0, >= 1.0.4)
|
recursive-open-struct (~> 1.0, >= 1.0.4)
|
||||||
|
@ -492,6 +508,12 @@ GEM
|
||||||
mail (2.7.1)
|
mail (2.7.1)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
mail_room (0.9.1)
|
mail_room (0.9.1)
|
||||||
|
marcel (0.3.3)
|
||||||
|
mimemagic (~> 0.3.2)
|
||||||
|
mdl (0.5.0)
|
||||||
|
kramdown (~> 1.12, >= 1.12.0)
|
||||||
|
mixlib-cli (~> 1.7, >= 1.7.0)
|
||||||
|
mixlib-config (~> 2.2, >= 2.2.1)
|
||||||
memoist (0.16.0)
|
memoist (0.16.0)
|
||||||
memoizable (0.4.2)
|
memoizable (0.4.2)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
|
@ -505,6 +527,9 @@ GEM
|
||||||
mini_mime (1.0.1)
|
mini_mime (1.0.1)
|
||||||
mini_portile2 (2.4.0)
|
mini_portile2 (2.4.0)
|
||||||
minitest (5.11.3)
|
minitest (5.11.3)
|
||||||
|
mixlib-cli (1.7.0)
|
||||||
|
mixlib-config (2.2.18)
|
||||||
|
tomlrb
|
||||||
msgpack (1.2.10)
|
msgpack (1.2.10)
|
||||||
multi_json (1.13.1)
|
multi_json (1.13.1)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
|
@ -515,7 +540,7 @@ GEM
|
||||||
mysql2 (0.4.10)
|
mysql2 (0.4.10)
|
||||||
nakayoshi_fork (0.0.4)
|
nakayoshi_fork (0.0.4)
|
||||||
net-ldap (0.16.0)
|
net-ldap (0.16.0)
|
||||||
net-ssh (5.0.1)
|
net-ssh (5.2.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
nio4r (2.3.1)
|
nio4r (2.3.1)
|
||||||
nokogiri (1.10.3)
|
nokogiri (1.10.3)
|
||||||
|
@ -652,7 +677,7 @@ GEM
|
||||||
parser
|
parser
|
||||||
unparser
|
unparser
|
||||||
procto (0.0.3)
|
procto (0.0.3)
|
||||||
prometheus-client-mmap (0.9.4)
|
prometheus-client-mmap (0.9.8)
|
||||||
pry (0.11.3)
|
pry (0.11.3)
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
method_source (~> 0.9.0)
|
method_source (~> 0.9.0)
|
||||||
|
@ -687,17 +712,18 @@ GEM
|
||||||
rack-test (1.1.0)
|
rack-test (1.1.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
rack-timeout (0.5.1)
|
rack-timeout (0.5.1)
|
||||||
rails (5.1.7)
|
rails (5.2.3)
|
||||||
actioncable (= 5.1.7)
|
actioncable (= 5.2.3)
|
||||||
actionmailer (= 5.1.7)
|
actionmailer (= 5.2.3)
|
||||||
actionpack (= 5.1.7)
|
actionpack (= 5.2.3)
|
||||||
actionview (= 5.1.7)
|
actionview (= 5.2.3)
|
||||||
activejob (= 5.1.7)
|
activejob (= 5.2.3)
|
||||||
activemodel (= 5.1.7)
|
activemodel (= 5.2.3)
|
||||||
activerecord (= 5.1.7)
|
activerecord (= 5.2.3)
|
||||||
activesupport (= 5.1.7)
|
activestorage (= 5.2.3)
|
||||||
|
activesupport (= 5.2.3)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 5.1.7)
|
railties (= 5.2.3)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-controller-testing (1.0.2)
|
rails-controller-testing (1.0.2)
|
||||||
actionpack (~> 5.x, >= 5.0.1)
|
actionpack (~> 5.x, >= 5.0.1)
|
||||||
|
@ -711,12 +737,12 @@ GEM
|
||||||
rails-i18n (5.1.1)
|
rails-i18n (5.1.1)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 5.0, < 6)
|
railties (>= 5.0, < 6)
|
||||||
railties (5.1.7)
|
railties (5.2.3)
|
||||||
actionpack (= 5.1.7)
|
actionpack (= 5.2.3)
|
||||||
activesupport (= 5.1.7)
|
activesupport (= 5.2.3)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.19.0, < 2.0)
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
raindrops (0.19.0)
|
raindrops (0.19.0)
|
||||||
rake (12.3.2)
|
rake (12.3.2)
|
||||||
|
@ -770,41 +796,41 @@ GEM
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rinku (2.0.0)
|
rinku (2.0.0)
|
||||||
rotp (2.1.2)
|
rotp (2.1.2)
|
||||||
rouge (3.3.0)
|
rouge (3.5.1)
|
||||||
rqrcode (0.7.0)
|
rqrcode (0.7.0)
|
||||||
chunky_png
|
chunky_png
|
||||||
rqrcode-rails3 (0.1.7)
|
rqrcode-rails3 (0.1.7)
|
||||||
rqrcode (>= 0.4.2)
|
rqrcode (>= 0.4.2)
|
||||||
rspec (3.7.0)
|
rspec (3.8.0)
|
||||||
rspec-core (~> 3.7.0)
|
rspec-core (~> 3.8.0)
|
||||||
rspec-expectations (~> 3.7.0)
|
rspec-expectations (~> 3.8.0)
|
||||||
rspec-mocks (~> 3.7.0)
|
rspec-mocks (~> 3.8.0)
|
||||||
rspec-core (3.7.1)
|
rspec-core (3.8.2)
|
||||||
rspec-support (~> 3.7.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-expectations (3.7.0)
|
rspec-expectations (3.8.4)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.7.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-mocks (3.7.0)
|
rspec-mocks (3.8.1)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.7.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-parameterized (0.4.2)
|
rspec-parameterized (0.4.2)
|
||||||
binding_ninja (>= 0.2.3)
|
binding_ninja (>= 0.2.3)
|
||||||
parser
|
parser
|
||||||
proc_to_ast
|
proc_to_ast
|
||||||
rspec (>= 2.13, < 4)
|
rspec (>= 2.13, < 4)
|
||||||
unparser
|
unparser
|
||||||
rspec-rails (3.7.2)
|
rspec-rails (3.8.2)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
railties (>= 3.0)
|
railties (>= 3.0)
|
||||||
rspec-core (~> 3.7.0)
|
rspec-core (~> 3.8.0)
|
||||||
rspec-expectations (~> 3.7.0)
|
rspec-expectations (~> 3.8.0)
|
||||||
rspec-mocks (~> 3.7.0)
|
rspec-mocks (~> 3.8.0)
|
||||||
rspec-support (~> 3.7.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-retry (0.6.1)
|
rspec-retry (0.6.1)
|
||||||
rspec-core (> 3.3)
|
rspec-core (> 3.3)
|
||||||
rspec-set (0.1.3)
|
rspec-set (0.1.3)
|
||||||
rspec-support (3.7.1)
|
rspec-support (3.8.2)
|
||||||
rspec_junit_formatter (0.4.1)
|
rspec_junit_formatter (0.4.1)
|
||||||
rspec-core (>= 2, < 4, != 2.12.0)
|
rspec-core (>= 2, < 4, != 2.12.0)
|
||||||
rspec_profiling (0.0.5)
|
rspec_profiling (0.0.5)
|
||||||
|
@ -943,6 +969,7 @@ GEM
|
||||||
parslet (~> 1.8.0)
|
parslet (~> 1.8.0)
|
||||||
toml-rb (1.0.0)
|
toml-rb (1.0.0)
|
||||||
citrus (~> 3.0, > 3.0)
|
citrus (~> 3.0, > 3.0)
|
||||||
|
tomlrb (1.2.8)
|
||||||
truncato (0.7.11)
|
truncato (0.7.11)
|
||||||
htmlentities (~> 4.3.1)
|
htmlentities (~> 4.3.1)
|
||||||
nokogiri (>= 1.7.0, <= 2.0)
|
nokogiri (>= 1.7.0, <= 2.0)
|
||||||
|
@ -999,7 +1026,7 @@ GEM
|
||||||
hashdiff
|
hashdiff
|
||||||
webpack-rails (0.9.11)
|
webpack-rails (0.9.11)
|
||||||
railties (>= 3.2.0)
|
railties (>= 3.2.0)
|
||||||
websocket-driver (0.6.5)
|
websocket-driver (0.7.0)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.3)
|
websocket-extensions (0.1.3)
|
||||||
wikicloth (0.8.1)
|
wikicloth (0.8.1)
|
||||||
|
@ -1025,9 +1052,9 @@ DEPENDENCIES
|
||||||
akismet (~> 2.0)
|
akismet (~> 2.0)
|
||||||
apollo_upload_server (~> 2.0.0.beta3)
|
apollo_upload_server (~> 2.0.0.beta3)
|
||||||
asana (~> 0.8.1)
|
asana (~> 0.8.1)
|
||||||
asciidoctor (~> 1.5.8)
|
asciidoctor (~> 2.0.10)
|
||||||
asciidoctor-include-ext (~> 0.3.1)
|
asciidoctor-include-ext (~> 0.3.1)
|
||||||
asciidoctor-plantuml (= 0.0.8)
|
asciidoctor-plantuml (= 0.0.9)
|
||||||
attr_encrypted (~> 3.1.0)
|
attr_encrypted (~> 3.1.0)
|
||||||
awesome_print
|
awesome_print
|
||||||
babosa (~> 1.0.2)
|
babosa (~> 1.0.2)
|
||||||
|
@ -1056,6 +1083,7 @@ DEPENDENCIES
|
||||||
creole (~> 0.5.0)
|
creole (~> 0.5.0)
|
||||||
database_cleaner (~> 1.7.0)
|
database_cleaner (~> 1.7.0)
|
||||||
deckar01-task_list (= 2.2.0)
|
deckar01-task_list (= 2.2.0)
|
||||||
|
default_value_for (~> 3.2.0)
|
||||||
derailed_benchmarks
|
derailed_benchmarks
|
||||||
device_detector
|
device_detector
|
||||||
devise (~> 4.6)
|
devise (~> 4.6)
|
||||||
|
@ -1077,7 +1105,7 @@ DEPENDENCIES
|
||||||
flipper-active_support_cache_store (~> 0.13.0)
|
flipper-active_support_cache_store (~> 0.13.0)
|
||||||
flowdock (~> 0.7)
|
flowdock (~> 0.7)
|
||||||
fog-aliyun (~> 0.3)
|
fog-aliyun (~> 0.3)
|
||||||
fog-aws (~> 3.3)
|
fog-aws (~> 3.5)
|
||||||
fog-core (= 2.1.0)
|
fog-core (= 2.1.0)
|
||||||
fog-google (~> 1.8)
|
fog-google (~> 1.8)
|
||||||
fog-local (~> 0.6)
|
fog-local (~> 0.6)
|
||||||
|
@ -1091,9 +1119,8 @@ DEPENDENCIES
|
||||||
gettext (~> 3.2.2)
|
gettext (~> 3.2.2)
|
||||||
gettext_i18n_rails (~> 1.8.0)
|
gettext_i18n_rails (~> 1.8.0)
|
||||||
gettext_i18n_rails_js (~> 1.3)
|
gettext_i18n_rails_js (~> 1.3)
|
||||||
gitaly-proto (~> 1.32.0)
|
gitaly-proto (~> 1.37.0)
|
||||||
github-markup (~> 1.7.0)
|
github-markup (~> 1.7.0)
|
||||||
gitlab-default_value_for (~> 3.1.1)
|
|
||||||
gitlab-labkit (~> 0.3.0)
|
gitlab-labkit (~> 0.3.0)
|
||||||
gitlab-markup (~> 1.7.0)
|
gitlab-markup (~> 1.7.0)
|
||||||
gitlab-sidekiq-fetcher (~> 0.4.0)
|
gitlab-sidekiq-fetcher (~> 0.4.0)
|
||||||
|
@ -1109,6 +1136,7 @@ DEPENDENCIES
|
||||||
grape_logging (~> 1.7)
|
grape_logging (~> 1.7)
|
||||||
graphiql-rails (~> 1.4.10)
|
graphiql-rails (~> 1.4.10)
|
||||||
graphql (~> 1.8.0)
|
graphql (~> 1.8.0)
|
||||||
|
graphql-docs (~> 1.6.0)
|
||||||
grpc (~> 1.19.0)
|
grpc (~> 1.19.0)
|
||||||
haml_lint (~> 0.31.0)
|
haml_lint (~> 0.31.0)
|
||||||
hamlit (~> 2.8.8)
|
hamlit (~> 2.8.8)
|
||||||
|
@ -1134,6 +1162,7 @@ DEPENDENCIES
|
||||||
lograge (~> 0.5)
|
lograge (~> 0.5)
|
||||||
loofah (~> 2.2)
|
loofah (~> 2.2)
|
||||||
mail_room (~> 0.9.1)
|
mail_room (~> 0.9.1)
|
||||||
|
mdl (~> 0.5.0)
|
||||||
memory_profiler (~> 0.9)
|
memory_profiler (~> 0.9)
|
||||||
method_source (~> 0.8)
|
method_source (~> 0.8)
|
||||||
mimemagic (~> 0.3.2)
|
mimemagic (~> 0.3.2)
|
||||||
|
@ -1142,7 +1171,7 @@ DEPENDENCIES
|
||||||
mysql2 (~> 0.4.10)
|
mysql2 (~> 0.4.10)
|
||||||
nakayoshi_fork (~> 0.0.4)
|
nakayoshi_fork (~> 0.0.4)
|
||||||
net-ldap
|
net-ldap
|
||||||
net-ssh (~> 5.0)
|
net-ssh (~> 5.2)
|
||||||
nokogiri (~> 1.10.3)
|
nokogiri (~> 1.10.3)
|
||||||
oauth2 (~> 1.4)
|
oauth2 (~> 1.4)
|
||||||
octokit (~> 4.9)
|
octokit (~> 4.9)
|
||||||
|
@ -1173,7 +1202,7 @@ DEPENDENCIES
|
||||||
peek-redis (~> 1.2.0)
|
peek-redis (~> 1.2.0)
|
||||||
pg (~> 1.1)
|
pg (~> 1.1)
|
||||||
premailer-rails (~> 1.9.7)
|
premailer-rails (~> 1.9.7)
|
||||||
prometheus-client-mmap (~> 0.9.4)
|
prometheus-client-mmap (~> 0.9.8)
|
||||||
pry-byebug (~> 3.5.1)
|
pry-byebug (~> 3.5.1)
|
||||||
pry-rails (~> 0.3.4)
|
pry-rails (~> 0.3.4)
|
||||||
puma (~> 3.12)
|
puma (~> 3.12)
|
||||||
|
@ -1184,7 +1213,7 @@ DEPENDENCIES
|
||||||
rack-oauth2 (~> 1.9.3)
|
rack-oauth2 (~> 1.9.3)
|
||||||
rack-proxy (~> 0.6.0)
|
rack-proxy (~> 0.6.0)
|
||||||
rack-timeout
|
rack-timeout
|
||||||
rails (= 5.1.7)
|
rails (= 5.2.3)
|
||||||
rails-controller-testing
|
rails-controller-testing
|
||||||
rails-i18n (~> 5.1)
|
rails-i18n (~> 5.1)
|
||||||
rainbow (~> 3.0)
|
rainbow (~> 3.0)
|
||||||
|
@ -1199,10 +1228,10 @@ DEPENDENCIES
|
||||||
redis-rails (~> 5.0.2)
|
redis-rails (~> 5.0.2)
|
||||||
request_store (~> 1.3)
|
request_store (~> 1.3)
|
||||||
responders (~> 2.0)
|
responders (~> 2.0)
|
||||||
rouge (~> 3.1)
|
rouge (~> 3.5)
|
||||||
rqrcode-rails3 (~> 0.1.7)
|
rqrcode-rails3 (~> 0.1.7)
|
||||||
rspec-parameterized
|
rspec-parameterized
|
||||||
rspec-rails (~> 3.7.0)
|
rspec-rails (~> 3.8.0)
|
||||||
rspec-retry (~> 0.6.1)
|
rspec-retry (~> 0.6.1)
|
||||||
rspec-set (~> 0.1.3)
|
rspec-set (~> 0.1.3)
|
||||||
rspec_junit_formatter
|
rspec_junit_formatter
|
||||||
|
|
49
PROCESS.md
49
PROCESS.md
|
@ -84,44 +84,29 @@ star, smile, etc.). Some good tips about code reviews can be found in our
|
||||||
|
|
||||||
[Code Review Guidelines]: https://docs.gitlab.com/ce/development/code_review.html
|
[Code Review Guidelines]: https://docs.gitlab.com/ce/development/code_review.html
|
||||||
|
|
||||||
|
## Feature flags
|
||||||
|
|
||||||
|
Overview and details of feature flag processes in development of GitLab itself is described in [feature flags process documentation](https://docs.gitlab.com/ee/development/feature_flags/process.html).
|
||||||
|
|
||||||
|
Guides on how to include feature flags in your backend/frontend code while developing GitLab are described in [developing with feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/developing.html).
|
||||||
|
|
||||||
|
Getting access and how to expose the feature to users is detailed in [controlling feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/controls.html).
|
||||||
|
|
||||||
|
## Feature proposals from the 22nd to the 1st
|
||||||
|
|
||||||
|
To allow the Product and Engineering teams time to discuss issues that will be placed into an upcoming milestone,
|
||||||
|
Product Managers must have their proposal for that milestone ready by the 22nd of each month.
|
||||||
|
|
||||||
|
This proposal will be shared with Engineering for discussion, feedback, and planning.
|
||||||
|
The plan for the upcoming milestone must be finalized by the 1st of the month, one week before kickoff on the 8th.
|
||||||
|
|
||||||
## Feature freeze on the 7th for the release on the 22nd
|
## Feature freeze on the 7th for the release on the 22nd
|
||||||
|
|
||||||
The feature freeze on the 7th has been discontinued. The [transition period overview](https://gitlab.com/gitlab-org/release/docs/blob/21cbd409dd5f157fe252f254f3e897f01908abe2/general/deploy/auto-deploy-transition.md#transition)
|
The feature freeze on the 7th has been discontinued. [Transition period overview](https://gitlab.com/gitlab-org/release/docs/blob/21cbd409dd5f157fe252f254f3e897f01908abe2/general/deploy/auto-deploy-transition.md#transition)
|
||||||
describes the change to this process. During the transition period, the only guarantee that
|
describes the change to this process. During the transition period, the only guarantee that
|
||||||
a change will be included in the release on the 22nd is if the change has been
|
a change will be included in the release on the 22nd is if the change has been
|
||||||
deployed to GitLab.com prior to this date.
|
deployed to GitLab.com prior to this date.
|
||||||
|
|
||||||
### Feature flags
|
|
||||||
|
|
||||||
Merge requests that make changes hidden behind a feature flag, or remove an
|
|
||||||
existing feature flag because a feature is deemed stable, may be merged (and
|
|
||||||
picked into the stable branches) up to the 19th of the month. Such merge
|
|
||||||
requests should have the ~"feature flag" label assigned, and don't require a
|
|
||||||
corresponding exception request to be created.
|
|
||||||
|
|
||||||
A level of common sense should be applied when deciding whether to have a feature
|
|
||||||
behind a feature flag off or on by default.
|
|
||||||
|
|
||||||
The following guidelines can be applied to help make this decision:
|
|
||||||
|
|
||||||
* If the feature is not fully ready or functioning, the feature flag should be disabled by default.
|
|
||||||
* If the feature is ready but there are concerns about performance or impact, the feature flag should be enabled by default, but
|
|
||||||
disabled via chatops before deployment on GitLab.com environments. If the performance concern is confirmed, the final release should have the feature flag disabled by default.
|
|
||||||
* In most other cases, the feature flag can be enabled by default.
|
|
||||||
|
|
||||||
For more information on rolling out changes using feature flags, read [through the documentation](https://docs.gitlab.com/ee/development/rolling_out_changes_using_feature_flags.html).
|
|
||||||
|
|
||||||
In order to build the final package and present the feature for self-hosted
|
|
||||||
customers, the feature flag should be removed. This should happen before the
|
|
||||||
22nd, ideally _at least_ 2 days before. That means MRs with feature
|
|
||||||
flags being picked at the 19th would have quite a tight schedule, so picking
|
|
||||||
these _earlier_ is preferable.
|
|
||||||
|
|
||||||
While rare, release managers may decide to reject picking a change into a stable
|
|
||||||
branch, even when feature flags are used. This might be necessary if the changes
|
|
||||||
are deemed problematic, too invasive, or there simply isn't enough time to
|
|
||||||
properly test how the changes behave on GitLab.com.
|
|
||||||
|
|
||||||
### Between the 1st and the 7th
|
### Between the 1st and the 7th
|
||||||
|
|
||||||
These types of merge requests for the upcoming release need special consideration:
|
These types of merge requests for the upcoming release need special consideration:
|
||||||
|
|
|
@ -15,7 +15,7 @@ To see how GitLab looks please see the [features page on our website](https://ab
|
||||||
|
|
||||||
- Manage Git repositories with fine grained access controls that keep your code secure
|
- Manage Git repositories with fine grained access controls that keep your code secure
|
||||||
- Perform code reviews and enhance collaboration with merge requests
|
- Perform code reviews and enhance collaboration with merge requests
|
||||||
- Complete continuous integration (CI) and CD pipelines to builds, test, and deploy your applications
|
- Complete continuous integration (CI) and continuous deployment/delivery (CD) pipelines to build, test, and deploy your applications
|
||||||
- Each project can also have an issue tracker, issue board, and a wiki
|
- Each project can also have an issue tracker, issue board, and a wiki
|
||||||
- Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises
|
- Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises
|
||||||
- Completely free and open source (MIT Expat license)
|
- Completely free and open source (MIT Expat license)
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
12.0.9
|
12.1.11
|
||||||
|
|
BIN
app/assets/images/auth_buttons/salesforce_64.png
Normal file
BIN
app/assets/images/auth_buttons/salesforce_64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
|
@ -12,6 +12,7 @@ const Api = {
|
||||||
groupProjectsPath: '/api/:version/groups/:id/projects.json',
|
groupProjectsPath: '/api/:version/groups/:id/projects.json',
|
||||||
projectsPath: '/api/:version/projects.json',
|
projectsPath: '/api/:version/projects.json',
|
||||||
projectPath: '/api/:version/projects/:id',
|
projectPath: '/api/:version/projects/:id',
|
||||||
|
forkedProjectsPath: '/api/:version/projects/:id/forks',
|
||||||
projectLabelsPath: '/:namespace_path/:project_path/-/labels',
|
projectLabelsPath: '/:namespace_path/:project_path/-/labels',
|
||||||
projectMergeRequestsPath: '/api/:version/projects/:id/merge_requests',
|
projectMergeRequestsPath: '/api/:version/projects/:id/merge_requests',
|
||||||
projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
|
projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
|
||||||
|
@ -23,6 +24,7 @@ const Api = {
|
||||||
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
|
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
|
||||||
projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
|
projectTemplatePath: '/api/:version/projects/:id/templates/:type/:key',
|
||||||
projectTemplatesPath: '/api/:version/projects/:id/templates/:type',
|
projectTemplatesPath: '/api/:version/projects/:id/templates/:type',
|
||||||
|
userCountsPath: '/api/:version/user_counts',
|
||||||
usersPath: '/api/:version/users.json',
|
usersPath: '/api/:version/users.json',
|
||||||
userPath: '/api/:version/users/:id',
|
userPath: '/api/:version/users/:id',
|
||||||
userStatusPath: '/api/:version/users/:id/status',
|
userStatusPath: '/api/:version/users/:id/status',
|
||||||
|
@ -113,6 +115,21 @@ const Api = {
|
||||||
return axios.get(url);
|
return axios.get(url);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all projects for a forked relationship to a specified project
|
||||||
|
* @param {string} projectPath - Path or ID of a project
|
||||||
|
* @param {Object} params - Get request parameters
|
||||||
|
* @returns {Promise} - Request promise
|
||||||
|
*/
|
||||||
|
projectForks(projectPath, params) {
|
||||||
|
const url = Api.buildUrl(Api.forkedProjectsPath).replace(
|
||||||
|
':id',
|
||||||
|
encodeURIComponent(projectPath),
|
||||||
|
);
|
||||||
|
|
||||||
|
return axios.get(url, { params });
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all Merge Requests for a project, eventually filtering based on
|
* Get all Merge Requests for a project, eventually filtering based on
|
||||||
* supplied parameters
|
* supplied parameters
|
||||||
|
@ -296,6 +313,11 @@ const Api = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
userCounts() {
|
||||||
|
const url = Api.buildUrl(this.userCountsPath);
|
||||||
|
return axios.get(url);
|
||||||
|
},
|
||||||
|
|
||||||
userStatus(id, options) {
|
userStatus(id, options) {
|
||||||
const url = Api.buildUrl(this.userStatusPath).replace(':id', encodeURIComponent(id));
|
const url = Api.buildUrl(this.userStatusPath).replace(':id', encodeURIComponent(id));
|
||||||
return axios.get(url, {
|
return axios.get(url, {
|
||||||
|
|
|
@ -36,7 +36,8 @@ export default function renderMermaid($els) {
|
||||||
});
|
});
|
||||||
|
|
||||||
$els.each((i, el) => {
|
$els.each((i, el) => {
|
||||||
const source = el.textContent;
|
// Mermaid doesn't like `<br />` tags, so collapse all like tags into `<br>`, which is parsed correctly.
|
||||||
|
const source = el.textContent.replace(/<br\s*\/>/g, '<br>');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restrict the rendering to a certain amount of character to
|
* Restrict the rendering to a certain amount of character to
|
||||||
|
@ -59,6 +60,14 @@ export default function renderMermaid($els) {
|
||||||
mermaid.init(undefined, el, id => {
|
mermaid.init(undefined, el, id => {
|
||||||
const svg = document.getElementById(id);
|
const svg = document.getElementById(id);
|
||||||
|
|
||||||
|
// As of https://github.com/knsv/mermaid/commit/57b780a0d,
|
||||||
|
// Mermaid will make two init callbacks:one to initialize the
|
||||||
|
// flow charts, and another to initialize the Gannt charts.
|
||||||
|
// Guard against an error caused by double initialization.
|
||||||
|
if (svg.classList.contains('mermaid')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
svg.classList.add('mermaid');
|
svg.classList.add('mermaid');
|
||||||
|
|
||||||
// pre > code > svg
|
// pre > code > svg
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import $ from 'jquery';
|
||||||
import Sortable from 'sortablejs';
|
import Sortable from 'sortablejs';
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { n__ } from '~/locale';
|
import { n__, s__ } from '~/locale';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import Tooltip from '~/vue_shared/directives/tooltip';
|
import Tooltip from '~/vue_shared/directives/tooltip';
|
||||||
import AccessorUtilities from '../../lib/utils/accessor';
|
import AccessorUtilities from '../../lib/utils/accessor';
|
||||||
|
@ -53,12 +54,19 @@ export default Vue.extend({
|
||||||
const { issuesSize } = this.list;
|
const { issuesSize } = this.list;
|
||||||
return `${n__('%d issue', '%d issues', issuesSize)}`;
|
return `${n__('%d issue', '%d issues', issuesSize)}`;
|
||||||
},
|
},
|
||||||
|
caretTooltip() {
|
||||||
|
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
|
||||||
|
},
|
||||||
isNewIssueShown() {
|
isNewIssueShown() {
|
||||||
return (
|
return (
|
||||||
this.list.type === 'backlog' ||
|
this.list.type === 'backlog' ||
|
||||||
(!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank')
|
(!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank')
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
uniqueKey() {
|
||||||
|
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
|
||||||
|
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
filter: {
|
filter: {
|
||||||
|
@ -72,31 +80,34 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.sortableOptions = getBoardSortableDefaultOptions({
|
const instance = this;
|
||||||
|
|
||||||
|
const sortableOptions = getBoardSortableDefaultOptions({
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
group: 'boards',
|
group: 'boards',
|
||||||
draggable: '.is-draggable',
|
draggable: '.is-draggable',
|
||||||
handle: '.js-board-handle',
|
handle: '.js-board-handle',
|
||||||
onEnd: e => {
|
onEnd(e) {
|
||||||
sortableEnd();
|
sortableEnd();
|
||||||
|
|
||||||
|
const sortable = this;
|
||||||
|
|
||||||
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
|
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
|
||||||
const order = this.sortable.toArray();
|
const order = sortable.toArray();
|
||||||
const list = boardsStore.findList('id', parseInt(e.item.dataset.id, 10));
|
const list = boardsStore.findList('id', parseInt(e.item.dataset.id, 10));
|
||||||
|
|
||||||
this.$nextTick(() => {
|
instance.$nextTick(() => {
|
||||||
boardsStore.moveList(list, order);
|
boardsStore.moveList(list, order);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
|
Sortable.create(this.$el.parentNode, sortableOptions);
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
|
if (this.list.isExpandable && AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||||
const isCollapsed =
|
const isCollapsed = localStorage.getItem(`${this.uniqueKey}.expanded`) === 'false';
|
||||||
localStorage.getItem(`boards.${this.boardId}.${this.list.type}.expanded`) === 'false';
|
|
||||||
|
|
||||||
this.list.isExpanded = !isCollapsed;
|
this.list.isExpanded = !isCollapsed;
|
||||||
}
|
}
|
||||||
|
@ -105,16 +116,17 @@ export default Vue.extend({
|
||||||
showNewIssueForm() {
|
showNewIssueForm() {
|
||||||
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
|
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
|
||||||
},
|
},
|
||||||
toggleExpanded(e) {
|
toggleExpanded() {
|
||||||
if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
|
if (this.list.isExpandable) {
|
||||||
this.list.isExpanded = !this.list.isExpanded;
|
this.list.isExpanded = !this.list.isExpanded;
|
||||||
|
|
||||||
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
if (AccessorUtilities.isLocalStorageAccessSafe()) {
|
||||||
localStorage.setItem(
|
localStorage.setItem(`${this.uniqueKey}.expanded`, this.list.isExpanded);
|
||||||
`boards.${this.boardId}.${this.list.type}.expanded`,
|
|
||||||
this.list.isExpanded,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When expanding/collapsing, the tooltip on the caret button sometimes stays open.
|
||||||
|
// Close all tooltips manually to prevent dangling tooltips.
|
||||||
|
$('.tooltip').tooltip('hide');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { __ } from '~/locale';
|
||||||
/* global ListLabel */
|
/* global ListLabel */
|
||||||
import Cookies from 'js-cookie';
|
import Cookies from 'js-cookie';
|
||||||
import boardsStore from '../stores/boards_store';
|
import boardsStore from '../stores/boards_store';
|
||||||
|
@ -7,8 +8,8 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
predefinedLabels: [
|
predefinedLabels: [
|
||||||
new ListLabel({ title: 'To Do', color: '#F0AD4E' }),
|
new ListLabel({ title: __('To Do'), color: '#F0AD4E' }),
|
||||||
new ListLabel({ title: 'Doing', color: '#5CB85C' }),
|
new ListLabel({ title: __('Doing'), color: '#5CB85C' }),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -58,7 +59,11 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="board-blank-state p-3">
|
<div class="board-blank-state p-3">
|
||||||
<p>Add the following default lists to your Issue Board with one click:</p>
|
<p>
|
||||||
|
{{
|
||||||
|
s__('BoardBlankState|Add the following default lists to your Issue Board with one click:')
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
<ul class="list-unstyled board-blank-state-list">
|
<ul class="list-unstyled board-blank-state-list">
|
||||||
<li v-for="(label, index) in predefinedLabels" :key="index">
|
<li v-for="(label, index) in predefinedLabels" :key="index">
|
||||||
<span
|
<span
|
||||||
|
@ -70,18 +75,21 @@ export default {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
Starting out with the default set of lists will get you right on the way to making the most of
|
{{
|
||||||
your board.
|
s__(
|
||||||
|
'BoardBlankState|Starting out with the default set of lists will get you right on the way to making the most of your board.',
|
||||||
|
)
|
||||||
|
}}
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
class="btn btn-success btn-inverted btn-block"
|
class="btn btn-success btn-inverted btn-block"
|
||||||
type="button"
|
type="button"
|
||||||
@click.stop="addDefaultLists"
|
@click.stop="addDefaultLists"
|
||||||
>
|
>
|
||||||
Add default lists
|
{{ s__('BoardBlankState|Add default lists') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-default btn-block" type="button" @click.stop="clearBlankState">
|
<button class="btn btn-default btn-block" type="button" @click.stop="clearBlankState">
|
||||||
Nevermind, I'll use my own
|
{{ s__("BoardBlankState|Nevermind, I'll use my own") }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
216
app/assets/javascripts/boards/components/board_form.vue
Normal file
216
app/assets/javascripts/boards/components/board_form.vue
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
<script>
|
||||||
|
import Flash from '~/flash';
|
||||||
|
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
|
||||||
|
import { visitUrl } from '~/lib/utils/url_utility';
|
||||||
|
import boardsStore from '~/boards/stores/boards_store';
|
||||||
|
|
||||||
|
const boardDefaults = {
|
||||||
|
id: false,
|
||||||
|
name: '',
|
||||||
|
labels: [],
|
||||||
|
milestone_id: undefined,
|
||||||
|
assignee: {},
|
||||||
|
assignee_id: undefined,
|
||||||
|
weight: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BoardScope: () => import('ee_component/boards/components/board_scope.vue'),
|
||||||
|
DeprecatedModal,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
canAdminBoard: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
milestonePath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
labelsPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
scopedIssueBoardFeatureEnabled: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
groupId: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
weights: {
|
||||||
|
type: Array,
|
||||||
|
required: false,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
enableScopedLabels: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
scopedLabelsDocumentationLink: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '#',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
board: { ...boardDefaults, ...this.currentBoard },
|
||||||
|
currentBoard: boardsStore.state.currentBoard,
|
||||||
|
currentPage: boardsStore.state.currentPage,
|
||||||
|
isLoading: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isNewForm() {
|
||||||
|
return this.currentPage === 'new';
|
||||||
|
},
|
||||||
|
isDeleteForm() {
|
||||||
|
return this.currentPage === 'delete';
|
||||||
|
},
|
||||||
|
isEditForm() {
|
||||||
|
return this.currentPage === 'edit';
|
||||||
|
},
|
||||||
|
isVisible() {
|
||||||
|
return this.currentPage !== '';
|
||||||
|
},
|
||||||
|
buttonText() {
|
||||||
|
if (this.isNewForm) {
|
||||||
|
return 'Create board';
|
||||||
|
}
|
||||||
|
if (this.isDeleteForm) {
|
||||||
|
return 'Delete';
|
||||||
|
}
|
||||||
|
return 'Save changes';
|
||||||
|
},
|
||||||
|
buttonKind() {
|
||||||
|
if (this.isNewForm) {
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
if (this.isDeleteForm) {
|
||||||
|
return 'danger';
|
||||||
|
}
|
||||||
|
return 'info';
|
||||||
|
},
|
||||||
|
title() {
|
||||||
|
if (this.isNewForm) {
|
||||||
|
return 'Create new board';
|
||||||
|
}
|
||||||
|
if (this.isDeleteForm) {
|
||||||
|
return 'Delete board';
|
||||||
|
}
|
||||||
|
if (this.readonly) {
|
||||||
|
return 'Board scope';
|
||||||
|
}
|
||||||
|
return 'Edit board';
|
||||||
|
},
|
||||||
|
readonly() {
|
||||||
|
return !this.canAdminBoard;
|
||||||
|
},
|
||||||
|
submitDisabled() {
|
||||||
|
return this.isLoading || this.board.name.length === 0;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.resetFormState();
|
||||||
|
if (this.$refs.name) {
|
||||||
|
this.$refs.name.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
if (this.board.name.length === 0) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
if (this.isDeleteForm) {
|
||||||
|
gl.boardService
|
||||||
|
.deleteBoard(this.currentBoard)
|
||||||
|
.then(() => {
|
||||||
|
visitUrl(boardsStore.rootPath);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Flash('Failed to delete board. Please try again.');
|
||||||
|
this.isLoading = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
gl.boardService
|
||||||
|
.createBoard(this.board)
|
||||||
|
.then(resp => resp.data)
|
||||||
|
.then(data => {
|
||||||
|
visitUrl(data.board_path);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Flash('Unable to save your changes. Please try again.');
|
||||||
|
this.isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
boardsStore.showPage('');
|
||||||
|
},
|
||||||
|
resetFormState() {
|
||||||
|
if (this.isNewForm) {
|
||||||
|
// Clear the form when we open the "New board" modal
|
||||||
|
this.board = { ...boardDefaults };
|
||||||
|
} else if (this.currentBoard && Object.keys(this.currentBoard).length) {
|
||||||
|
this.board = { ...boardDefaults, ...this.currentBoard };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<deprecated-modal
|
||||||
|
v-show="isVisible"
|
||||||
|
:hide-footer="readonly"
|
||||||
|
:title="title"
|
||||||
|
:primary-button-label="buttonText"
|
||||||
|
:kind="buttonKind"
|
||||||
|
:submit-disabled="submitDisabled"
|
||||||
|
modal-dialog-class="board-config-modal"
|
||||||
|
@cancel="cancel"
|
||||||
|
@submit="submit"
|
||||||
|
>
|
||||||
|
<template slot="body">
|
||||||
|
<p v-if="isDeleteForm">Are you sure you want to delete this board?</p>
|
||||||
|
<form v-else class="js-board-config-modal" @submit.prevent>
|
||||||
|
<div v-if="!readonly" class="append-bottom-20">
|
||||||
|
<label class="form-section-title label-bold" for="board-new-name"> Board name </label>
|
||||||
|
<input
|
||||||
|
id="board-new-name"
|
||||||
|
ref="name"
|
||||||
|
v-model="board.name"
|
||||||
|
class="form-control"
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter board name"
|
||||||
|
@keyup.enter="submit"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<board-scope
|
||||||
|
v-if="scopedIssueBoardFeatureEnabled"
|
||||||
|
:collapse-scope="isNewForm"
|
||||||
|
:board="board"
|
||||||
|
:can-admin-board="canAdminBoard"
|
||||||
|
:milestone-path="milestonePath"
|
||||||
|
:labels-path="labelsPath"
|
||||||
|
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
|
||||||
|
:enable-scoped-labels="enableScopedLabels"
|
||||||
|
:project-id="projectId"
|
||||||
|
:group-id="groupId"
|
||||||
|
:weights="weights"
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
</deprecated-modal>
|
||||||
|
</template>
|
|
@ -227,7 +227,7 @@ export default {
|
||||||
:class="{ 'd-none': !list.isExpanded, 'd-flex flex-column': list.isExpanded }"
|
:class="{ 'd-none': !list.isExpanded, 'd-flex flex-column': list.isExpanded }"
|
||||||
class="board-list-component position-relative h-100"
|
class="board-list-component position-relative h-100"
|
||||||
>
|
>
|
||||||
<div v-if="loading" class="board-list-loading text-center" aria-label="Loading issues">
|
<div v-if="loading" class="board-list-loading text-center" :aria-label="__('Loading issues')">
|
||||||
<gl-loading-icon />
|
<gl-loading-icon />
|
||||||
</div>
|
</div>
|
||||||
<board-new-issue
|
<board-new-issue
|
||||||
|
@ -257,7 +257,7 @@ export default {
|
||||||
/>
|
/>
|
||||||
<li v-if="showCount" class="board-list-count text-center" data-issue-id="-1">
|
<li v-if="showCount" class="board-list-count text-center" data-issue-id="-1">
|
||||||
<gl-loading-icon v-show="list.loadingMore" label="Loading more issues" />
|
<gl-loading-icon v-show="list.loadingMore" label="Loading more issues" />
|
||||||
<span v-if="list.issues.length === list.issuesSize"> Showing all issues </span>
|
<span v-if="list.issues.length === list.issuesSize">{{ __('Showing all issues') }}</span>
|
||||||
<span v-else> Showing {{ list.issues.length }} of {{ list.issuesSize }} issues </span>
|
<span v-else> Showing {{ list.issues.length }} of {{ list.issuesSize }} issues </span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -102,9 +102,9 @@ export default {
|
||||||
<div class="board-card position-relative p-3 rounded">
|
<div class="board-card position-relative p-3 rounded">
|
||||||
<form @submit="submit($event)">
|
<form @submit="submit($event)">
|
||||||
<div v-if="error" class="flash-container">
|
<div v-if="error" class="flash-container">
|
||||||
<div class="flash-alert">An error occurred. Please try again.</div>
|
<div class="flash-alert">{{ __('An error occurred. Please try again.') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<label :for="list.id + '-title'" class="label-bold"> Title </label>
|
<label :for="list.id + '-title'" class="label-bold">{{ __('Title') }}</label>
|
||||||
<input
|
<input
|
||||||
:id="list.id + '-title'"
|
:id="list.id + '-title'"
|
||||||
ref="input"
|
ref="input"
|
||||||
|
@ -122,12 +122,11 @@ export default {
|
||||||
class="float-left"
|
class="float-left"
|
||||||
variant="success"
|
variant="success"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
>{{ __('Submit issue') }}</gl-button
|
||||||
>
|
>
|
||||||
Submit issue
|
<gl-button class="float-right" type="button" variant="default" @click="cancel">{{
|
||||||
</gl-button>
|
__('Cancel')
|
||||||
<gl-button class="float-right" type="button" variant="default" @click="cancel">
|
}}</gl-button>
|
||||||
Cancel
|
|
||||||
</gl-button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,6 +38,7 @@ export default Vue.extend({
|
||||||
issue: {},
|
issue: {},
|
||||||
list: {},
|
list: {},
|
||||||
loadingAssignees: false,
|
loadingAssignees: false,
|
||||||
|
timeTrackingLimitToHours: boardsStore.timeTracking.limitToHours,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
334
app/assets/javascripts/boards/components/boards_selector.vue
Normal file
334
app/assets/javascripts/boards/components/boards_selector.vue
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
<script>
|
||||||
|
import { throttle } from 'underscore';
|
||||||
|
import {
|
||||||
|
GlLoadingIcon,
|
||||||
|
GlSearchBoxByType,
|
||||||
|
GlDropdown,
|
||||||
|
GlDropdownDivider,
|
||||||
|
GlDropdownHeader,
|
||||||
|
GlDropdownItem,
|
||||||
|
} from '@gitlab/ui';
|
||||||
|
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import httpStatusCodes from '~/lib/utils/http_status';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
|
import BoardForm from './board_form.vue';
|
||||||
|
|
||||||
|
const MIN_BOARDS_TO_VIEW_RECENT = 10;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BoardsSelector',
|
||||||
|
components: {
|
||||||
|
Icon,
|
||||||
|
BoardForm,
|
||||||
|
GlLoadingIcon,
|
||||||
|
GlSearchBoxByType,
|
||||||
|
GlDropdown,
|
||||||
|
GlDropdownDivider,
|
||||||
|
GlDropdownHeader,
|
||||||
|
GlDropdownItem,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
currentBoard: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
milestonePath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
throttleDuration: {
|
||||||
|
type: Number,
|
||||||
|
default: 200,
|
||||||
|
},
|
||||||
|
boardBaseUrl: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
hasMissingBoards: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
canAdminBoard: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
multipleIssueBoardsAvailable: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
labelsPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
projectId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
groupId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
scopedIssueBoardFeatureEnabled: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
weights: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
enabledScopedLabels: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
scopedLabelsDocumentationLink: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '#',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
hasScrollFade: false,
|
||||||
|
scrollFadeInitialized: false,
|
||||||
|
boards: [],
|
||||||
|
recentBoards: [],
|
||||||
|
state: boardsStore.state,
|
||||||
|
throttledSetScrollFade: throttle(this.setScrollFade, this.throttleDuration),
|
||||||
|
contentClientHeight: 0,
|
||||||
|
maxPosition: 0,
|
||||||
|
store: boardsStore,
|
||||||
|
filterTerm: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
currentPage() {
|
||||||
|
return this.state.currentPage;
|
||||||
|
},
|
||||||
|
filteredBoards() {
|
||||||
|
return this.boards.filter(board =>
|
||||||
|
board.name.toLowerCase().includes(this.filterTerm.toLowerCase()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reload: {
|
||||||
|
get() {
|
||||||
|
return this.state.reload;
|
||||||
|
},
|
||||||
|
set(newValue) {
|
||||||
|
this.state.reload = newValue;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
board() {
|
||||||
|
return this.state.currentBoard;
|
||||||
|
},
|
||||||
|
showDelete() {
|
||||||
|
return this.boards.length > 1;
|
||||||
|
},
|
||||||
|
scrollFadeClass() {
|
||||||
|
return {
|
||||||
|
'fade-out': !this.hasScrollFade,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
showRecentSection() {
|
||||||
|
return (
|
||||||
|
this.recentBoards.length &&
|
||||||
|
this.boards.length > MIN_BOARDS_TO_VIEW_RECENT &&
|
||||||
|
!this.filterTerm.length
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
filteredBoards() {
|
||||||
|
this.scrollFadeInitialized = false;
|
||||||
|
this.$nextTick(this.setScrollFade);
|
||||||
|
},
|
||||||
|
reload() {
|
||||||
|
if (this.reload) {
|
||||||
|
this.boards = [];
|
||||||
|
this.recentBoards = [];
|
||||||
|
this.loading = true;
|
||||||
|
this.reload = false;
|
||||||
|
|
||||||
|
this.loadBoards(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
boardsStore.setCurrentBoard(this.currentBoard);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
showPage(page) {
|
||||||
|
boardsStore.showPage(page);
|
||||||
|
},
|
||||||
|
loadBoards(toggleDropdown = true) {
|
||||||
|
if (toggleDropdown && this.boards.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const recentBoardsPromise = new Promise((resolve, reject) =>
|
||||||
|
gl.boardService
|
||||||
|
.recentBoards()
|
||||||
|
.then(resolve)
|
||||||
|
.catch(err => {
|
||||||
|
/**
|
||||||
|
* If user is unauthorized we'd still want to resolve the
|
||||||
|
* request to display all boards.
|
||||||
|
*/
|
||||||
|
if (err.response.status === httpStatusCodes.UNAUTHORIZED) {
|
||||||
|
resolve({ data: [] }); // recent boards are empty
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reject(err);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
Promise.all([gl.boardService.allBoards(), recentBoardsPromise])
|
||||||
|
.then(([allBoards, recentBoards]) => [allBoards.data, recentBoards.data])
|
||||||
|
.then(([allBoardsJson, recentBoardsJson]) => {
|
||||||
|
this.loading = false;
|
||||||
|
this.boards = allBoardsJson;
|
||||||
|
this.recentBoards = recentBoardsJson;
|
||||||
|
})
|
||||||
|
.then(() => this.$nextTick()) // Wait for boards list in DOM
|
||||||
|
.then(() => {
|
||||||
|
this.setScrollFade();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isScrolledUp() {
|
||||||
|
const { content } = this.$refs;
|
||||||
|
const currentPosition = this.contentClientHeight + content.scrollTop;
|
||||||
|
|
||||||
|
return content && currentPosition < this.maxPosition;
|
||||||
|
},
|
||||||
|
initScrollFade() {
|
||||||
|
this.scrollFadeInitialized = true;
|
||||||
|
|
||||||
|
const { content } = this.$refs;
|
||||||
|
|
||||||
|
this.contentClientHeight = content.clientHeight;
|
||||||
|
this.maxPosition = content.scrollHeight;
|
||||||
|
},
|
||||||
|
setScrollFade() {
|
||||||
|
if (!this.scrollFadeInitialized) this.initScrollFade();
|
||||||
|
|
||||||
|
this.hasScrollFade = this.isScrolledUp();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="boards-switcher js-boards-selector append-right-10">
|
||||||
|
<span class="boards-selector-wrapper js-boards-selector-wrapper">
|
||||||
|
<gl-dropdown
|
||||||
|
toggle-class="dropdown-menu-toggle js-dropdown-toggle"
|
||||||
|
menu-class="flex-column dropdown-extended-height"
|
||||||
|
:text="board.name"
|
||||||
|
@show="loadBoards"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="dropdown-title mb-0" @mousedown.prevent>
|
||||||
|
{{ s__('IssueBoards|Switch board') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<gl-dropdown-header class="mt-0">
|
||||||
|
<gl-search-box-by-type ref="searchBox" v-model="filterTerm" />
|
||||||
|
</gl-dropdown-header>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="!loading"
|
||||||
|
ref="content"
|
||||||
|
class="dropdown-content flex-fill"
|
||||||
|
@scroll.passive="throttledSetScrollFade"
|
||||||
|
>
|
||||||
|
<gl-dropdown-item
|
||||||
|
v-show="filteredBoards.length === 0"
|
||||||
|
class="no-pointer-events text-secondary"
|
||||||
|
>
|
||||||
|
{{ s__('IssueBoards|No matching boards found') }}
|
||||||
|
</gl-dropdown-item>
|
||||||
|
|
||||||
|
<h6 v-if="showRecentSection" class="dropdown-bold-header my-0">
|
||||||
|
{{ __('Recent') }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<template v-if="showRecentSection">
|
||||||
|
<gl-dropdown-item
|
||||||
|
v-for="recentBoard in recentBoards"
|
||||||
|
:key="`recent-${recentBoard.id}`"
|
||||||
|
class="js-dropdown-item"
|
||||||
|
:href="`${boardBaseUrl}/${recentBoard.id}`"
|
||||||
|
>
|
||||||
|
{{ recentBoard.name }}
|
||||||
|
</gl-dropdown-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<hr v-if="showRecentSection" class="my-1" />
|
||||||
|
|
||||||
|
<h6 v-if="showRecentSection" class="dropdown-bold-header my-0">
|
||||||
|
{{ __('All') }}
|
||||||
|
</h6>
|
||||||
|
|
||||||
|
<gl-dropdown-item
|
||||||
|
v-for="otherBoard in filteredBoards"
|
||||||
|
:key="otherBoard.id"
|
||||||
|
class="js-dropdown-item"
|
||||||
|
:href="`${boardBaseUrl}/${otherBoard.id}`"
|
||||||
|
>
|
||||||
|
{{ otherBoard.name }}
|
||||||
|
</gl-dropdown-item>
|
||||||
|
<gl-dropdown-item v-if="hasMissingBoards" class="small unclickable">
|
||||||
|
{{
|
||||||
|
s__(
|
||||||
|
'IssueBoards|Some of your boards are hidden, activate a license to see them again.',
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</gl-dropdown-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-show="filteredBoards.length > 0"
|
||||||
|
class="dropdown-content-faded-mask"
|
||||||
|
:class="scrollFadeClass"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<gl-loading-icon v-if="loading" />
|
||||||
|
|
||||||
|
<div v-if="canAdminBoard">
|
||||||
|
<gl-dropdown-divider />
|
||||||
|
|
||||||
|
<gl-dropdown-item v-if="multipleIssueBoardsAvailable" @click.prevent="showPage('new')">
|
||||||
|
{{ s__('IssueBoards|Create new board') }}
|
||||||
|
</gl-dropdown-item>
|
||||||
|
|
||||||
|
<gl-dropdown-item
|
||||||
|
v-if="showDelete"
|
||||||
|
class="text-danger"
|
||||||
|
@click.prevent="showPage('delete')"
|
||||||
|
>
|
||||||
|
{{ s__('IssueBoards|Delete board') }}
|
||||||
|
</gl-dropdown-item>
|
||||||
|
</div>
|
||||||
|
</gl-dropdown>
|
||||||
|
|
||||||
|
<board-form
|
||||||
|
v-if="currentPage"
|
||||||
|
:milestone-path="milestonePath"
|
||||||
|
:labels-path="labelsPath"
|
||||||
|
:project-id="projectId"
|
||||||
|
:group-id="groupId"
|
||||||
|
:can-admin-board="canAdminBoard"
|
||||||
|
:scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
|
||||||
|
:weights="weights"
|
||||||
|
:enable-scoped-labels="enabledScopedLabels"
|
||||||
|
:scoped-labels-documentation-link="scopedLabelsDocumentationLink"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -124,7 +124,7 @@ export default {
|
||||||
return `${this.rootPath}${assignee.username}`;
|
return `${this.rootPath}${assignee.username}`;
|
||||||
},
|
},
|
||||||
avatarUrlTitle(assignee) {
|
avatarUrlTitle(assignee) {
|
||||||
return `Avatar for ${assignee.name}`;
|
return sprintf(__(`Avatar for %{assigneeName}`), { assigneeName: assignee.name });
|
||||||
},
|
},
|
||||||
showLabel(label) {
|
showLabel(label) {
|
||||||
if (!label.id) return false;
|
if (!label.id) return false;
|
||||||
|
@ -160,9 +160,10 @@ export default {
|
||||||
:title="__('Confidential')"
|
:title="__('Confidential')"
|
||||||
class="confidential-icon append-right-4"
|
class="confidential-icon append-right-4"
|
||||||
:aria-label="__('Confidential')"
|
:aria-label="__('Confidential')"
|
||||||
/><a :href="issue.path" :title="issue.title" class="js-no-trigger" @mousemove.stop>{{
|
/>
|
||||||
issue.title
|
<a :href="issue.path" :title="issue.title" class="js-no-trigger" @mousemove.stop>
|
||||||
}}</a>
|
{{ issue.title }}
|
||||||
|
</a>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showLabelFooter" class="board-card-labels prepend-top-4 d-flex flex-wrap">
|
<div v-if="showLabelFooter" class="board-card-labels prepend-top-4 d-flex flex-wrap">
|
||||||
|
@ -204,13 +205,13 @@ export default {
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
class="board-issue-path block-truncated bold"
|
class="board-issue-path block-truncated bold"
|
||||||
>{{ issueReferencePath }}</tooltip-on-truncate
|
>{{ issueReferencePath }}</tooltip-on-truncate
|
||||||
>#{{ issue.iid }}
|
>
|
||||||
|
#{{ issue.iid }}
|
||||||
</span>
|
</span>
|
||||||
<span class="board-info-items prepend-top-8 d-inline-block">
|
<span class="board-info-items prepend-top-8 d-inline-block">
|
||||||
<issue-due-date v-if="issue.dueDate" :date="issue.dueDate" /><issue-time-estimate
|
<issue-due-date v-if="issue.dueDate" :date="issue.dueDate" />
|
||||||
v-if="issue.timeEstimate"
|
<issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" />
|
||||||
:estimate="issue.timeEstimate"
|
<issue-card-weight
|
||||||
/><issue-card-weight
|
|
||||||
v-if="issue.weight"
|
v-if="issue.weight"
|
||||||
:weight="issue.weight"
|
:weight="issue.weight"
|
||||||
@click="filterByWeight(issue.weight)"
|
@click="filterByWeight(issue.weight)"
|
||||||
|
@ -230,7 +231,8 @@ export default {
|
||||||
tooltip-placement="bottom"
|
tooltip-placement="bottom"
|
||||||
>
|
>
|
||||||
<span class="js-assignee-tooltip">
|
<span class="js-assignee-tooltip">
|
||||||
<span class="bold d-block">Assignee</span> {{ assignee.name }}
|
<span class="bold d-block">{{ __('Assignee') }}</span>
|
||||||
|
{{ assignee.name }}
|
||||||
<span class="text-white-50">@{{ assignee.username }}</span>
|
<span class="text-white-50">@{{ assignee.username }}</span>
|
||||||
</span>
|
</span>
|
||||||
</user-avatar-link>
|
</user-avatar-link>
|
||||||
|
@ -240,9 +242,8 @@ export default {
|
||||||
:title="assigneeCounterTooltip"
|
:title="assigneeCounterTooltip"
|
||||||
class="avatar-counter"
|
class="avatar-counter"
|
||||||
data-placement="bottom"
|
data-placement="bottom"
|
||||||
|
>{{ assigneeCounterLabel }}</span
|
||||||
>
|
>
|
||||||
{{ assigneeCounterLabel }}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { GlTooltip } from '@gitlab/ui';
|
import { GlTooltip } from '@gitlab/ui';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
|
import { parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
|
||||||
|
import boardsStore from '../stores/boards_store';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -14,12 +15,17 @@ export default {
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
limitToHours: boardsStore.timeTracking.limitToHours,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
return stringifyTime(parseSeconds(this.estimate), true);
|
return stringifyTime(parseSeconds(this.estimate, { limitToHours: this.limitToHours }), true);
|
||||||
},
|
},
|
||||||
timeEstimate() {
|
timeEstimate() {
|
||||||
return stringifyTime(parseSeconds(this.estimate));
|
return stringifyTime(parseSeconds(this.estimate, { limitToHours: this.limitToHours }));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { __, sprintf } from '~/locale';
|
||||||
import ModalStore from '../../stores/modal_store';
|
import ModalStore from '../../stores/modal_store';
|
||||||
import modalMixin from '../../mixins/modal_mixins';
|
import modalMixin from '../../mixins/modal_mixins';
|
||||||
|
|
||||||
|
@ -20,19 +21,20 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
contents() {
|
contents() {
|
||||||
const obj = {
|
const obj = {
|
||||||
title: "You haven't added any issues to your project yet",
|
title: __("You haven't added any issues to your project yet"),
|
||||||
content: `
|
content: __(
|
||||||
An issue can be a bug, a todo or a feature request that needs to be
|
'An issue can be a bug, a todo or a feature request that needs to be discussed in a project. Besides, issues are searchable and filterable.',
|
||||||
discussed in a project. Besides, issues are searchable and filterable.
|
),
|
||||||
`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.activeTab === 'selected') {
|
if (this.activeTab === 'selected') {
|
||||||
obj.title = "You haven't selected any issues yet";
|
obj.title = __("You haven't selected any issues yet");
|
||||||
obj.content = `
|
obj.content = sprintf(
|
||||||
Go back to <strong>Open issues</strong> and select some issues
|
__(
|
||||||
to add to your board.
|
'Go back to %{startTag}Open issues%{endTag} and select some issues to add to your board.',
|
||||||
`;
|
),
|
||||||
|
{ startTag: '<strong>', endTag: '</strong>' },
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -51,16 +53,16 @@ export default {
|
||||||
<div class="text-content">
|
<div class="text-content">
|
||||||
<h4>{{ contents.title }}</h4>
|
<h4>{{ contents.title }}</h4>
|
||||||
<p v-html="contents.content"></p>
|
<p v-html="contents.content"></p>
|
||||||
<a v-if="activeTab === 'all'" :href="newIssuePath" class="btn btn-success btn-inverted">
|
<a v-if="activeTab === 'all'" :href="newIssuePath" class="btn btn-success btn-inverted">{{
|
||||||
New issue
|
__('New issue')
|
||||||
</a>
|
}}</a>
|
||||||
<button
|
<button
|
||||||
v-if="activeTab === 'selected'"
|
v-if="activeTab === 'selected'"
|
||||||
class="btn btn-default"
|
class="btn btn-default"
|
||||||
type="button"
|
type="button"
|
||||||
@click="changeTab('all')"
|
@click="changeTab('all')"
|
||||||
>
|
>
|
||||||
Open issues
|
{{ __('Open issues') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<script>
|
<script>
|
||||||
|
import footerEEMixin from 'ee_else_ce/boards/mixins/modal_footer';
|
||||||
import Flash from '../../../flash';
|
import Flash from '../../../flash';
|
||||||
import { __ } from '../../../locale';
|
import { __, n__ } from '../../../locale';
|
||||||
import ListsDropdown from './lists_dropdown.vue';
|
import ListsDropdown from './lists_dropdown.vue';
|
||||||
import { pluralize } from '../../../lib/utils/text_utility';
|
|
||||||
import ModalStore from '../../stores/modal_store';
|
import ModalStore from '../../stores/modal_store';
|
||||||
import modalMixin from '../../mixins/modal_mixins';
|
import modalMixin from '../../mixins/modal_mixins';
|
||||||
import boardsStore from '../../stores/boards_store';
|
import boardsStore from '../../stores/boards_store';
|
||||||
|
@ -11,7 +11,7 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
ListsDropdown,
|
ListsDropdown,
|
||||||
},
|
},
|
||||||
mixins: [modalMixin],
|
mixins: [modalMixin, footerEEMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
modal: ModalStore.store,
|
modal: ModalStore.store,
|
||||||
|
@ -24,8 +24,8 @@ export default {
|
||||||
},
|
},
|
||||||
submitText() {
|
submitText() {
|
||||||
const count = ModalStore.selectedCount();
|
const count = ModalStore.selectedCount();
|
||||||
|
if (!count) return __('Add issues');
|
||||||
return `Add ${count > 0 ? count : ''} ${pluralize('issue', count)}`;
|
return n__(`Add %d issue`, `Add %d issues`, count);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -42,7 +42,7 @@ export default {
|
||||||
const req = this.buildUpdateRequest(list);
|
const req = this.buildUpdateRequest(list);
|
||||||
|
|
||||||
// Post the data to the backend
|
// Post the data to the backend
|
||||||
gl.boardService.bulkUpdate(issueIds, req).catch(() => {
|
boardsStore.bulkUpdate(issueIds, req).catch(() => {
|
||||||
Flash(__('Failed to update issues, please try again.'));
|
Flash(__('Failed to update issues, please try again.'));
|
||||||
|
|
||||||
selectedIssues.forEach(issue => {
|
selectedIssues.forEach(issue => {
|
||||||
|
@ -68,11 +68,11 @@ export default {
|
||||||
<button :disabled="submitDisabled" class="btn btn-success" type="button" @click="addIssues">
|
<button :disabled="submitDisabled" class="btn btn-success" type="button" @click="addIssues">
|
||||||
{{ submitText }}
|
{{ submitText }}
|
||||||
</button>
|
</button>
|
||||||
<span class="inline add-issues-footer-to-list"> to list </span>
|
<span class="inline add-issues-footer-to-list">{{ __('to list') }}</span>
|
||||||
<lists-dropdown />
|
<lists-dropdown />
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-default float-right" type="button" @click="toggleModal(false)">
|
<button class="btn btn-default float-right" type="button" @click="toggleModal(false)">
|
||||||
Cancel
|
{{ __('Cancel') }}
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { __ } from '~/locale';
|
||||||
import ModalFilters from './filters';
|
import ModalFilters from './filters';
|
||||||
import ModalTabs from './tabs.vue';
|
import ModalTabs from './tabs.vue';
|
||||||
import ModalStore from '../../stores/modal_store';
|
import ModalStore from '../../stores/modal_store';
|
||||||
|
@ -30,10 +31,10 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
selectAllText() {
|
selectAllText() {
|
||||||
if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
|
if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
|
||||||
return 'Select all';
|
return __('Select all');
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Deselect all';
|
return __('Deselect all');
|
||||||
},
|
},
|
||||||
showSearch() {
|
showSearch() {
|
||||||
return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
|
return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
|
||||||
|
@ -57,7 +58,7 @@ export default {
|
||||||
type="button"
|
type="button"
|
||||||
class="close"
|
class="close"
|
||||||
data-dismiss="modal"
|
data-dismiss="modal"
|
||||||
aria-label="Close"
|
:aria-label="__('Close')"
|
||||||
@click="toggleModal(false)"
|
@click="toggleModal(false)"
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
|
|
|
@ -123,7 +123,9 @@ export default {
|
||||||
class="empty-state add-issues-empty-state-filter text-center"
|
class="empty-state add-issues-empty-state-filter text-center"
|
||||||
>
|
>
|
||||||
<div class="svg-content"><img :src="emptyStateSvg" /></div>
|
<div class="svg-content"><img :src="emptyStateSvg" /></div>
|
||||||
<div class="text-content"><h4>There are no issues to show.</h4></div>
|
<div class="text-content">
|
||||||
|
<h4>{{ __('There are no issues to show.') }}</h4>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="(group, index) in groupedIssues" :key="index" class="add-issues-list-column">
|
<div v-for="(group, index) in groupedIssues" :key="index" class="add-issues-list-column">
|
||||||
<div v-for="issue in group" v-if="showIssue(issue)" :key="issue.id" class="board-card-parent">
|
<div v-for="issue in group" v-if="showIssue(issue)" :key="issue.id" class="board-card-parent">
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { __ } from '~/locale';
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
@ -27,7 +28,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
selectedProjectName() {
|
selectedProjectName() {
|
||||||
return this.selectedProject.name || 'Select a project';
|
return this.selectedProject.name || __('Select a project');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -81,7 +82,7 @@ export default {
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<label class="label-bold prepend-top-10"> Project </label>
|
<label class="label-bold prepend-top-10">{{ __('Project') }}</label>
|
||||||
<div ref="projectsDropdown" class="dropdown dropdown-projects">
|
<div ref="projectsDropdown" class="dropdown dropdown-projects">
|
||||||
<button
|
<button
|
||||||
class="dropdown-menu-toggle wide"
|
class="dropdown-menu-toggle wide"
|
||||||
|
@ -92,9 +93,9 @@ export default {
|
||||||
{{ selectedProjectName }} <icon name="chevron-down" />
|
{{ selectedProjectName }} <icon name="chevron-down" />
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
|
<div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
|
||||||
<div class="dropdown-title">Projects</div>
|
<div class="dropdown-title">{{ __('Projects') }}</div>
|
||||||
<div class="dropdown-input">
|
<div class="dropdown-input">
|
||||||
<input class="dropdown-input-field" type="search" placeholder="Search projects" />
|
<input class="dropdown-input-field" type="search" :placeholder="__('Search projects')" />
|
||||||
<icon name="search" class="dropdown-input-search" data-hidden="true" />
|
<icon name="search" class="dropdown-input-search" data-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-content"></div>
|
<div class="dropdown-content"></div>
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default Vue.extend({
|
||||||
<template>
|
<template>
|
||||||
<div class="block list">
|
<div class="block list">
|
||||||
<button class="btn btn-default btn-block" type="button" @click="removeIssue">
|
<button class="btn btn-default btn-block" type="button" @click="removeIssue">
|
||||||
Remove from board
|
{{ __('Remove from board') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
1
app/assets/javascripts/boards/config_toggle.js
Normal file
1
app/assets/javascripts/boards/config_toggle.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export default () => {};
|
|
@ -2,7 +2,6 @@ import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable
|
||||||
import FilteredSearchContainer from '../filtered_search/container';
|
import FilteredSearchContainer from '../filtered_search/container';
|
||||||
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
|
import FilteredSearchManager from '../filtered_search/filtered_search_manager';
|
||||||
import boardsStore from './stores/boards_store';
|
import boardsStore from './stores/boards_store';
|
||||||
import { isEE } from '~/lib/utils/common_utils';
|
|
||||||
|
|
||||||
export default class FilteredSearchBoards extends FilteredSearchManager {
|
export default class FilteredSearchBoards extends FilteredSearchManager {
|
||||||
constructor(store, updateUrl = false, cantEdit = []) {
|
constructor(store, updateUrl = false, cantEdit = []) {
|
||||||
|
@ -10,7 +9,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
|
||||||
page: 'boards',
|
page: 'boards',
|
||||||
isGroupDecendent: true,
|
isGroupDecendent: true,
|
||||||
stateFiltersSelector: '.issues-state-filters',
|
stateFiltersSelector: '.issues-state-filters',
|
||||||
isGroup: isEE(),
|
isGroup: IS_EE,
|
||||||
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
|
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,28 +6,31 @@ import { __ } from '~/locale';
|
||||||
import './models/label';
|
import './models/label';
|
||||||
import './models/assignee';
|
import './models/assignee';
|
||||||
|
|
||||||
import FilteredSearchBoards from './filtered_search_boards';
|
import FilteredSearchBoards from '~/boards/filtered_search_boards';
|
||||||
import eventHub from './eventhub';
|
import eventHub from '~/boards/eventhub';
|
||||||
import sidebarEventHub from '~/sidebar/event_hub';
|
import sidebarEventHub from '~/sidebar/event_hub';
|
||||||
import './models/issue';
|
import 'ee_else_ce/boards/models/issue';
|
||||||
import './models/list';
|
import 'ee_else_ce/boards/models/list';
|
||||||
import './models/milestone';
|
import '~/boards/models/milestone';
|
||||||
import './models/project';
|
import '~/boards/models/project';
|
||||||
import boardsStore from './stores/boards_store';
|
import boardsStore from '~/boards/stores/boards_store';
|
||||||
import ModalStore from './stores/modal_store';
|
import ModalStore from '~/boards/stores/modal_store';
|
||||||
import BoardService from './services/board_service';
|
import BoardService from 'ee_else_ce/boards/services/board_service';
|
||||||
import modalMixin from './mixins/modal_mixins';
|
import modalMixin from '~/boards/mixins/modal_mixins';
|
||||||
import './filters/due_date_filters';
|
import '~/boards/filters/due_date_filters';
|
||||||
import Board from './components/board';
|
import Board from 'ee_else_ce/boards/components/board';
|
||||||
import BoardSidebar from './components/board_sidebar';
|
import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar';
|
||||||
import initNewListDropdown from './components/new_list_dropdown';
|
import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
|
||||||
import BoardAddIssuesModal from './components/modal/index.vue';
|
import BoardAddIssuesModal from '~/boards/components/modal/index.vue';
|
||||||
import '~/vue_shared/vue_resource_interceptor';
|
import '~/vue_shared/vue_resource_interceptor';
|
||||||
import {
|
import {
|
||||||
NavigationType,
|
NavigationType,
|
||||||
convertObjectPropsToCamelCase,
|
convertObjectPropsToCamelCase,
|
||||||
parseBoolean,
|
parseBoolean,
|
||||||
} from '~/lib/utils/common_utils';
|
} from '~/lib/utils/common_utils';
|
||||||
|
import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
|
||||||
|
import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
|
||||||
|
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
|
||||||
|
|
||||||
let issueBoardsApp;
|
let issueBoardsApp;
|
||||||
|
|
||||||
|
@ -49,6 +52,7 @@ export default () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
boardsStore.create();
|
boardsStore.create();
|
||||||
|
boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours);
|
||||||
|
|
||||||
issueBoardsApp = new Vue({
|
issueBoardsApp = new Vue({
|
||||||
el: $boardApp,
|
el: $boardApp,
|
||||||
|
@ -77,13 +81,14 @@ export default () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
gl.boardService = new BoardService({
|
boardsStore.setEndpoints({
|
||||||
boardsEndpoint: this.boardsEndpoint,
|
boardsEndpoint: this.boardsEndpoint,
|
||||||
recentBoardsEndpoint: this.recentBoardsEndpoint,
|
recentBoardsEndpoint: this.recentBoardsEndpoint,
|
||||||
listsEndpoint: this.listsEndpoint,
|
listsEndpoint: this.listsEndpoint,
|
||||||
bulkUpdatePath: this.bulkUpdatePath,
|
bulkUpdatePath: this.bulkUpdatePath,
|
||||||
boardId: this.boardId,
|
boardId: this.boardId,
|
||||||
});
|
});
|
||||||
|
gl.boardService = new BoardService();
|
||||||
boardsStore.rootPath = this.boardsEndpoint;
|
boardsStore.rootPath = this.boardsEndpoint;
|
||||||
|
|
||||||
eventHub.$on('updateTokens', this.updateTokens);
|
eventHub.$on('updateTokens', this.updateTokens);
|
||||||
|
@ -204,6 +209,8 @@ export default () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
boardConfigToggle(boardsStore);
|
||||||
|
|
||||||
const issueBoardsModal = document.getElementById('js-add-issues-btn');
|
const issueBoardsModal = document.getElementById('js-add-issues-btn');
|
||||||
|
|
||||||
if (issueBoardsModal) {
|
if (issueBoardsModal) {
|
||||||
|
@ -277,4 +284,7 @@ export default () => {
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleFocusMode(ModalStore, boardsStore);
|
||||||
|
mountMultipleBoardsSwitcher();
|
||||||
};
|
};
|
||||||
|
|
1
app/assets/javascripts/boards/mixins/modal_footer.js
Normal file
1
app/assets/javascripts/boards/mixins/modal_footer.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export default {};
|
|
@ -1,7 +1,7 @@
|
||||||
/* global DocumentTouch */
|
/* global DocumentTouch */
|
||||||
|
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import sortableConfig from '../../sortable/sortable_config';
|
import sortableConfig from 'ee_else_ce/sortable/sortable_config';
|
||||||
|
|
||||||
export function sortableStart() {
|
export function sortableStart() {
|
||||||
$('.has-tooltip')
|
$('.has-tooltip')
|
||||||
|
@ -20,7 +20,7 @@ export function getBoardSortableDefaultOptions(obj) {
|
||||||
'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);
|
'ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch);
|
||||||
|
|
||||||
const defaultSortOptions = Object.assign({}, sortableConfig, {
|
const defaultSortOptions = Object.assign({}, sortableConfig, {
|
||||||
filter: '.board-delete, .btn',
|
filter: '.no-drag',
|
||||||
delay: touchEnabled ? 100 : 0,
|
delay: touchEnabled ? 100 : 0,
|
||||||
scrollSensitivity: touchEnabled ? 60 : 100,
|
scrollSensitivity: touchEnabled ? 60 : 100,
|
||||||
scrollSpeed: 20,
|
scrollSpeed: 20,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import './label';
|
import './label';
|
||||||
import { isEE, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||||
import IssueProject from './project';
|
import IssueProject from './project';
|
||||||
import boardsStore from '../stores/boards_store';
|
import boardsStore from '../stores/boards_store';
|
||||||
|
|
||||||
|
@ -91,13 +91,13 @@ class ListIssue {
|
||||||
|
|
||||||
addMilestone(milestone) {
|
addMilestone(milestone) {
|
||||||
const miletoneId = this.milestone ? this.milestone.id : null;
|
const miletoneId = this.milestone ? this.milestone.id : null;
|
||||||
if (isEE && milestone.id !== miletoneId) {
|
if (IS_EE && milestone.id !== miletoneId) {
|
||||||
this.milestone = new ListMilestone(milestone);
|
this.milestone = new ListMilestone(milestone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeMilestone(removeMilestone) {
|
removeMilestone(removeMilestone) {
|
||||||
if (isEE && removeMilestone && removeMilestone.id === this.milestone.id) {
|
if (IS_EE && removeMilestone && removeMilestone.id === this.milestone.id) {
|
||||||
this.milestone = {};
|
this.milestone = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
import ListLabel from './label';
|
import ListLabel from './label';
|
||||||
import ListAssignee from './assignee';
|
import ListAssignee from './assignee';
|
||||||
import { isEE, urlParamsToObject } from '~/lib/utils/common_utils';
|
import { urlParamsToObject } from '~/lib/utils/common_utils';
|
||||||
import boardsStore from '../stores/boards_store';
|
import boardsStore from '../stores/boards_store';
|
||||||
import ListMilestone from './milestone';
|
import ListMilestone from './milestone';
|
||||||
|
|
||||||
|
@ -26,6 +26,12 @@ const TYPES = {
|
||||||
isExpandable: false,
|
isExpandable: false,
|
||||||
isBlank: true,
|
isBlank: true,
|
||||||
},
|
},
|
||||||
|
default: {
|
||||||
|
// includes label, assignee, and milestone lists
|
||||||
|
isPreset: false,
|
||||||
|
isExpandable: true,
|
||||||
|
isBlank: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
class List {
|
class List {
|
||||||
|
@ -52,7 +58,7 @@ class List {
|
||||||
} else if (obj.user) {
|
} else if (obj.user) {
|
||||||
this.assignee = new ListAssignee(obj.user);
|
this.assignee = new ListAssignee(obj.user);
|
||||||
this.title = this.assignee.name;
|
this.title = this.assignee.name;
|
||||||
} else if (isEE && obj.milestone) {
|
} else if (IS_EE && obj.milestone) {
|
||||||
this.milestone = new ListMilestone(obj.milestone);
|
this.milestone = new ListMilestone(obj.milestone);
|
||||||
this.title = this.milestone.title;
|
this.title = this.milestone.title;
|
||||||
}
|
}
|
||||||
|
@ -79,7 +85,7 @@ class List {
|
||||||
entityType = 'label_id';
|
entityType = 'label_id';
|
||||||
} else if (this.assignee) {
|
} else if (this.assignee) {
|
||||||
entityType = 'assignee_id';
|
entityType = 'assignee_id';
|
||||||
} else if (isEE && this.milestone) {
|
} else if (IS_EE && this.milestone) {
|
||||||
entityType = 'milestone_id';
|
entityType = 'milestone_id';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,7 +205,7 @@ class List {
|
||||||
issue.addAssignee(this.assignee);
|
issue.addAssignee(this.assignee);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEE && this.milestone) {
|
if (IS_EE && this.milestone) {
|
||||||
if (listFrom && listFrom.type === 'milestone') {
|
if (listFrom && listFrom.type === 'milestone') {
|
||||||
issue.removeMilestone(listFrom.milestone);
|
issue.removeMilestone(listFrom.milestone);
|
||||||
}
|
}
|
||||||
|
@ -249,7 +255,7 @@ class List {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTypeInfo(type) {
|
getTypeInfo(type) {
|
||||||
return TYPES[type] || {};
|
return TYPES[type] || TYPES.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
onNewIssueResponse(issue, data) {
|
onNewIssueResponse(issue, data) {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { isEE } from '~/lib/utils/common_utils';
|
|
||||||
|
|
||||||
export default class ListMilestone {
|
export default class ListMilestone {
|
||||||
constructor(obj) {
|
constructor(obj) {
|
||||||
this.id = obj.id;
|
this.id = obj.id;
|
||||||
this.title = obj.title;
|
this.title = obj.title;
|
||||||
|
|
||||||
if (isEE) {
|
if (IS_EE) {
|
||||||
this.path = obj.path;
|
this.path = obj.path;
|
||||||
this.state = obj.state;
|
this.state = obj.state;
|
||||||
this.webUrl = obj.web_url || obj.webUrl;
|
this.webUrl = obj.web_url || obj.webUrl;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||||
|
import BoardsSelector from '~/boards/components/boards_selector.vue';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher');
|
||||||
|
return new Vue({
|
||||||
|
el: boardsSwitcherElement,
|
||||||
|
components: {
|
||||||
|
BoardsSelector,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
const { dataset } = boardsSwitcherElement;
|
||||||
|
|
||||||
|
const boardsSelectorProps = {
|
||||||
|
...dataset,
|
||||||
|
currentBoard: JSON.parse(dataset.currentBoard),
|
||||||
|
hasMissingBoards: parseBoolean(dataset.hasMissingBoards),
|
||||||
|
canAdminBoard: parseBoolean(dataset.canAdminBoard),
|
||||||
|
multipleIssueBoardsAvailable: parseBoolean(dataset.multipleIssueBoardsAvailable),
|
||||||
|
projectId: Number(dataset.projectId),
|
||||||
|
groupId: Number(dataset.groupId),
|
||||||
|
scopedIssueBoardFeatureEnabled: parseBoolean(dataset.scopedIssueBoardFeatureEnabled),
|
||||||
|
weights: JSON.parse(dataset.weights),
|
||||||
|
};
|
||||||
|
|
||||||
|
return { boardsSelectorProps };
|
||||||
|
},
|
||||||
|
render(createElement) {
|
||||||
|
return createElement(BoardsSelector, {
|
||||||
|
props: this.boardsSelectorProps,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,106 +1,82 @@
|
||||||
import axios from '../../lib/utils/axios_utils';
|
/* eslint-disable class-methods-use-this */
|
||||||
import { mergeUrlParams } from '../../lib/utils/url_utility';
|
|
||||||
|
import boardsStore from '~/boards/stores/boards_store';
|
||||||
|
|
||||||
export default class BoardService {
|
export default class BoardService {
|
||||||
constructor({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) {
|
|
||||||
this.boardsEndpoint = boardsEndpoint;
|
|
||||||
this.boardId = boardId;
|
|
||||||
this.listsEndpoint = listsEndpoint;
|
|
||||||
this.listsEndpointGenerate = `${listsEndpoint}/generate.json`;
|
|
||||||
this.bulkUpdatePath = bulkUpdatePath;
|
|
||||||
this.recentBoardsEndpoint = `${recentBoardsEndpoint}.json`;
|
|
||||||
}
|
|
||||||
|
|
||||||
generateBoardsPath(id) {
|
generateBoardsPath(id) {
|
||||||
return `${this.boardsEndpoint}${id ? `/${id}` : ''}.json`;
|
return boardsStore.generateBoardsPath(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateIssuesPath(id) {
|
generateIssuesPath(id) {
|
||||||
return `${this.listsEndpoint}${id ? `/${id}` : ''}/issues`;
|
return boardsStore.generateIssuesPath(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static generateIssuePath(boardId, id) {
|
static generateIssuePath(boardId, id) {
|
||||||
return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
|
return boardsStore.generateIssuePath(boardId, id);
|
||||||
id ? `/${id}` : ''
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
all() {
|
all() {
|
||||||
return axios.get(this.listsEndpoint);
|
return boardsStore.all();
|
||||||
}
|
}
|
||||||
|
|
||||||
generateDefaultLists() {
|
generateDefaultLists() {
|
||||||
return axios.post(this.listsEndpointGenerate, {});
|
return boardsStore.generateDefaultLists();
|
||||||
}
|
}
|
||||||
|
|
||||||
createList(entityId, entityType) {
|
createList(entityId, entityType) {
|
||||||
const list = {
|
return boardsStore.createList(entityId, entityType);
|
||||||
[entityType]: entityId,
|
|
||||||
};
|
|
||||||
|
|
||||||
return axios.post(this.listsEndpoint, {
|
|
||||||
list,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateList(id, position) {
|
updateList(id, position) {
|
||||||
return axios.put(`${this.listsEndpoint}/${id}`, {
|
return boardsStore.updateList(id, position);
|
||||||
list: {
|
|
||||||
position,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyList(id) {
|
destroyList(id) {
|
||||||
return axios.delete(`${this.listsEndpoint}/${id}`);
|
return boardsStore.destroyList(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIssuesForList(id, filter = {}) {
|
getIssuesForList(id, filter = {}) {
|
||||||
const data = { id };
|
return boardsStore.getIssuesForList(id, filter);
|
||||||
Object.keys(filter).forEach(key => {
|
|
||||||
data[key] = filter[key];
|
|
||||||
});
|
|
||||||
|
|
||||||
return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) {
|
moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) {
|
||||||
return axios.put(BoardService.generateIssuePath(this.boardId, id), {
|
return boardsStore.moveIssue(id, fromListId, toListId, moveBeforeId, moveAfterId);
|
||||||
from_list_id: fromListId,
|
|
||||||
to_list_id: toListId,
|
|
||||||
move_before_id: moveBeforeId,
|
|
||||||
move_after_id: moveAfterId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newIssue(id, issue) {
|
newIssue(id, issue) {
|
||||||
return axios.post(this.generateIssuesPath(id), {
|
return boardsStore.newIssue(id, issue);
|
||||||
issue,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getBacklog(data) {
|
getBacklog(data) {
|
||||||
return axios.get(
|
return boardsStore.getBacklog(data);
|
||||||
mergeUrlParams(data, `${gon.relative_url_root}/-/boards/${this.boardId}/issues.json`),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bulkUpdate(issueIds, extraData = {}) {
|
bulkUpdate(issueIds, extraData = {}) {
|
||||||
const data = {
|
return boardsStore.bulkUpdate(issueIds, extraData);
|
||||||
update: Object.assign(extraData, {
|
|
||||||
issuable_ids: issueIds.join(','),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
return axios.post(this.bulkUpdatePath, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getIssueInfo(endpoint) {
|
static getIssueInfo(endpoint) {
|
||||||
return axios.get(endpoint);
|
return boardsStore.getIssueInfo(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
static toggleIssueSubscription(endpoint) {
|
static toggleIssueSubscription(endpoint) {
|
||||||
return axios.post(endpoint);
|
return boardsStore.toggleIssueSubscription(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
allBoards() {
|
||||||
|
return boardsStore.allBoards();
|
||||||
|
}
|
||||||
|
|
||||||
|
recentBoards() {
|
||||||
|
return boardsStore.recentBoards();
|
||||||
|
}
|
||||||
|
|
||||||
|
createBoard(board) {
|
||||||
|
return boardsStore.createBoard(board);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteBoard({ id }) {
|
||||||
|
return boardsStore.deleteBoard({ id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,15 @@ import Cookies from 'js-cookie';
|
||||||
import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
|
import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
|
||||||
import { getUrlParamsArray, parseBoolean } from '~/lib/utils/common_utils';
|
import { getUrlParamsArray, parseBoolean } from '~/lib/utils/common_utils';
|
||||||
import { __ } from '~/locale';
|
import { __ } from '~/locale';
|
||||||
|
import axios from '~/lib/utils/axios_utils';
|
||||||
|
import { mergeUrlParams } from '~/lib/utils/url_utility';
|
||||||
import eventHub from '../eventhub';
|
import eventHub from '../eventhub';
|
||||||
|
|
||||||
const boardsStore = {
|
const boardsStore = {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
timeTracking: {
|
||||||
|
limitToHours: false,
|
||||||
|
},
|
||||||
scopedLabels: {
|
scopedLabels: {
|
||||||
helpLink: '',
|
helpLink: '',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
@ -25,6 +30,7 @@ const boardsStore = {
|
||||||
},
|
},
|
||||||
currentPage: '',
|
currentPage: '',
|
||||||
reload: false,
|
reload: false,
|
||||||
|
endpoints: {},
|
||||||
},
|
},
|
||||||
detail: {
|
detail: {
|
||||||
issue: {},
|
issue: {},
|
||||||
|
@ -33,6 +39,19 @@ const boardsStore = {
|
||||||
issue: {},
|
issue: {},
|
||||||
list: {},
|
list: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setEndpoints({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) {
|
||||||
|
const listsEndpointGenerate = `${listsEndpoint}/generate.json`;
|
||||||
|
this.state.endpoints = {
|
||||||
|
boardsEndpoint,
|
||||||
|
boardId,
|
||||||
|
listsEndpoint,
|
||||||
|
listsEndpointGenerate,
|
||||||
|
bulkUpdatePath,
|
||||||
|
recentBoardsEndpoint: `${recentBoardsEndpoint}.json`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
this.state.lists = [];
|
this.state.lists = [];
|
||||||
this.filter.path = getUrlParamsArray().join('&');
|
this.filter.path = getUrlParamsArray().join('&');
|
||||||
|
@ -222,6 +241,143 @@ const boardsStore = {
|
||||||
setIssueDetail(issueDetail) {
|
setIssueDetail(issueDetail) {
|
||||||
this.detail.issue = issueDetail;
|
this.detail.issue = issueDetail;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setTimeTrackingLimitToHours(limitToHours) {
|
||||||
|
this.timeTracking.limitToHours = parseBoolean(limitToHours);
|
||||||
|
},
|
||||||
|
|
||||||
|
generateBoardsPath(id) {
|
||||||
|
return `${this.state.endpoints.boardsEndpoint}${id ? `/${id}` : ''}.json`;
|
||||||
|
},
|
||||||
|
|
||||||
|
generateIssuesPath(id) {
|
||||||
|
return `${this.state.endpoints.listsEndpoint}${id ? `/${id}` : ''}/issues`;
|
||||||
|
},
|
||||||
|
|
||||||
|
generateIssuePath(boardId, id) {
|
||||||
|
return `${gon.relative_url_root}/-/boards/${boardId ? `${boardId}` : ''}/issues${
|
||||||
|
id ? `/${id}` : ''
|
||||||
|
}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
all() {
|
||||||
|
return axios.get(this.state.endpoints.listsEndpoint);
|
||||||
|
},
|
||||||
|
|
||||||
|
generateDefaultLists() {
|
||||||
|
return axios.post(this.state.endpoints.listsEndpointGenerate, {});
|
||||||
|
},
|
||||||
|
|
||||||
|
createList(entityId, entityType) {
|
||||||
|
const list = {
|
||||||
|
[entityType]: entityId,
|
||||||
|
};
|
||||||
|
|
||||||
|
return axios.post(this.state.endpoints.listsEndpoint, {
|
||||||
|
list,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateList(id, position) {
|
||||||
|
return axios.put(`${this.state.endpoints.listsEndpoint}/${id}`, {
|
||||||
|
list: {
|
||||||
|
position,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroyList(id) {
|
||||||
|
return axios.delete(`${this.state.endpoints.listsEndpoint}/${id}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
getIssuesForList(id, filter = {}) {
|
||||||
|
const data = { id };
|
||||||
|
Object.keys(filter).forEach(key => {
|
||||||
|
data[key] = filter[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return axios.get(mergeUrlParams(data, this.generateIssuesPath(id)));
|
||||||
|
},
|
||||||
|
|
||||||
|
moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) {
|
||||||
|
return axios.put(this.generateIssuePath(this.state.endpoints.boardId, id), {
|
||||||
|
from_list_id: fromListId,
|
||||||
|
to_list_id: toListId,
|
||||||
|
move_before_id: moveBeforeId,
|
||||||
|
move_after_id: moveAfterId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
newIssue(id, issue) {
|
||||||
|
return axios.post(this.generateIssuesPath(id), {
|
||||||
|
issue,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getBacklog(data) {
|
||||||
|
return axios.get(
|
||||||
|
mergeUrlParams(
|
||||||
|
data,
|
||||||
|
`${gon.relative_url_root}/-/boards/${this.state.endpoints.boardId}/issues.json`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
bulkUpdate(issueIds, extraData = {}) {
|
||||||
|
const data = {
|
||||||
|
update: Object.assign(extraData, {
|
||||||
|
issuable_ids: issueIds.join(','),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return axios.post(this.state.endpoints.bulkUpdatePath, data);
|
||||||
|
},
|
||||||
|
|
||||||
|
getIssueInfo(endpoint) {
|
||||||
|
return axios.get(endpoint);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleIssueSubscription(endpoint) {
|
||||||
|
return axios.post(endpoint);
|
||||||
|
},
|
||||||
|
|
||||||
|
allBoards() {
|
||||||
|
return axios.get(this.generateBoardsPath());
|
||||||
|
},
|
||||||
|
|
||||||
|
recentBoards() {
|
||||||
|
return axios.get(this.state.endpoints.recentBoardsEndpoint);
|
||||||
|
},
|
||||||
|
|
||||||
|
createBoard(board) {
|
||||||
|
const boardPayload = { ...board };
|
||||||
|
boardPayload.label_ids = (board.labels || []).map(b => b.id);
|
||||||
|
|
||||||
|
if (boardPayload.label_ids.length === 0) {
|
||||||
|
boardPayload.label_ids = [''];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boardPayload.assignee) {
|
||||||
|
boardPayload.assignee_id = boardPayload.assignee.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boardPayload.milestone) {
|
||||||
|
boardPayload.milestone_id = boardPayload.milestone.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boardPayload.id) {
|
||||||
|
return axios.put(this.generateBoardsPath(boardPayload.id), { board: boardPayload });
|
||||||
|
}
|
||||||
|
return axios.post(this.generateBoardsPath(), { board: boardPayload });
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteBoard({ id }) {
|
||||||
|
return axios.delete(this.generateBoardsPath(id));
|
||||||
|
},
|
||||||
|
|
||||||
|
setCurrentBoard(board) {
|
||||||
|
this.state.currentBoard = board;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
BoardsStoreEE.initEESpecific(boardsStore);
|
BoardsStoreEE.initEESpecific(boardsStore);
|
||||||
|
|
1
app/assets/javascripts/boards/toggle_focus.js
Normal file
1
app/assets/javascripts/boards/toggle_focus.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export default () => {};
|
|
@ -0,0 +1,72 @@
|
||||||
|
<script>
|
||||||
|
import { sprintf, __ } from '~/locale';
|
||||||
|
import GraphBar from './graph_bar.vue';
|
||||||
|
import { MAX_COMMIT_COUNT } from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
GraphBar,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
defaultBranch: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
distance: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
aheadCount: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
behindCount: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
maxCommits: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
title() {
|
||||||
|
if (this.distance) {
|
||||||
|
return sprintf(
|
||||||
|
__('More than %{number_commits_distance} commits different with %{default_branch}'),
|
||||||
|
{
|
||||||
|
number_commits_distance:
|
||||||
|
this.distance >= MAX_COMMIT_COUNT ? `${MAX_COMMIT_COUNT - 1}+` : this.distance,
|
||||||
|
default_branch: this.defaultBranch,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
__(
|
||||||
|
'%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead',
|
||||||
|
),
|
||||||
|
{
|
||||||
|
number_commits_behind: this.behindCount,
|
||||||
|
number_commits_ahead: this.aheadCount,
|
||||||
|
default_branch: this.defaultBranch,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :title="title" class="divergence-graph px-2 d-none d-md-block">
|
||||||
|
<template v-if="distance">
|
||||||
|
<graph-bar :count="distance" :max-commits="maxCommits" position="full" />
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<graph-bar :count="behindCount" :max-commits="maxCommits" position="left" />
|
||||||
|
<div class="graph-separator pull-left mt-1"></div>
|
||||||
|
<graph-bar :count="aheadCount" :max-commits="maxCommits" position="right" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
69
app/assets/javascripts/branches/components/graph_bar.vue
Normal file
69
app/assets/javascripts/branches/components/graph_bar.vue
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<script>
|
||||||
|
import { SIDES, MAX_COMMIT_COUNT } from '../constants';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
position: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
count: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
maxCommits: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
label() {
|
||||||
|
if (this.count >= MAX_COMMIT_COUNT) {
|
||||||
|
return `${MAX_COMMIT_COUNT - 1}+`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.count;
|
||||||
|
},
|
||||||
|
barGraphWidthFactor() {
|
||||||
|
return this.maxCommits > 0 ? 100 / this.maxCommits : 0;
|
||||||
|
},
|
||||||
|
style() {
|
||||||
|
return {
|
||||||
|
width: `${this.count * this.barGraphWidthFactor}%`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
isFullWidth() {
|
||||||
|
return this.position === SIDES.full;
|
||||||
|
},
|
||||||
|
isLeftSide() {
|
||||||
|
return this.position === SIDES.left;
|
||||||
|
},
|
||||||
|
roundedClass() {
|
||||||
|
if (this.isFullWidth) return 'rounded';
|
||||||
|
|
||||||
|
return `rounded-${this.position}`;
|
||||||
|
},
|
||||||
|
textAlignmentClass() {
|
||||||
|
if (this.isFullWidth) return 'text-center';
|
||||||
|
|
||||||
|
return `text-${this.isLeftSide ? SIDES.right : SIDES.left}`;
|
||||||
|
},
|
||||||
|
positionSideClass() {
|
||||||
|
return `position-${this.isLeftSide ? SIDES.right : SIDES.left}-0`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="{ full: isFullWidth }" class="position-relative pull-left pt-1 graph-side h-100">
|
||||||
|
<div
|
||||||
|
:style="style"
|
||||||
|
:class="[roundedClass, positionSideClass]"
|
||||||
|
class="position-absolute bar js-graph-bar"
|
||||||
|
></div>
|
||||||
|
<span :class="textAlignmentClass" class="d-block pt-1 pr-1 count js-graph-count">
|
||||||
|
{{ label }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
6
app/assets/javascripts/branches/constants.js
Normal file
6
app/assets/javascripts/branches/constants.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export const SIDES = {
|
||||||
|
full: 'full',
|
||||||
|
left: 'left',
|
||||||
|
right: 'right',
|
||||||
|
};
|
||||||
|
export const MAX_COMMIT_COUNT = 1000;
|
51
app/assets/javascripts/branches/divergence_graph.js
Normal file
51
app/assets/javascripts/branches/divergence_graph.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { __ } from '../locale';
|
||||||
|
import createFlash from '../flash';
|
||||||
|
import axios from '../lib/utils/axios_utils';
|
||||||
|
import DivergenceGraph from './components/divergence_graph.vue';
|
||||||
|
|
||||||
|
export function createGraphVueApp(el, data, maxCommits) {
|
||||||
|
return new Vue({
|
||||||
|
el,
|
||||||
|
render(h) {
|
||||||
|
return h(DivergenceGraph, {
|
||||||
|
props: {
|
||||||
|
defaultBranch: 'master',
|
||||||
|
distance: data.distance ? parseInt(data.distance, 10) : null,
|
||||||
|
aheadCount: parseInt(data.ahead, 10),
|
||||||
|
behindCount: parseInt(data.behind, 10),
|
||||||
|
maxCommits,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default endpoint => {
|
||||||
|
const names = [...document.querySelectorAll('.js-branch-item')].map(
|
||||||
|
({ dataset }) => dataset.name,
|
||||||
|
);
|
||||||
|
return axios
|
||||||
|
.get(endpoint, {
|
||||||
|
params: { names },
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
const maxCommits = Object.entries(data).reduce((acc, [, val]) => {
|
||||||
|
const max = Math.max(...Object.values(val));
|
||||||
|
return max > acc ? max : acc;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
Object.entries(data).forEach(([branchName, val]) => {
|
||||||
|
const el = document.querySelector(
|
||||||
|
`[data-name="${branchName}"] .js-branch-divergence-graph`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
createGraphVueApp(el, val, maxCommits);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() =>
|
||||||
|
createFlash(__('Error fetching diverging counts for branches. Please try again.')),
|
||||||
|
);
|
||||||
|
};
|
|
@ -207,7 +207,7 @@ export default {
|
||||||
return __('Updating');
|
return __('Updating');
|
||||||
}
|
}
|
||||||
|
|
||||||
return __('Updated');
|
return this.updateSuccessful ? __('Updated to') : __('Updated');
|
||||||
},
|
},
|
||||||
updateFailureDescription() {
|
updateFailureDescription() {
|
||||||
return s__('ClusterIntegration|Update failed. Please check the logs and try again.');
|
return s__('ClusterIntegration|Update failed. Please check the logs and try again.');
|
||||||
|
@ -331,8 +331,6 @@ export default {
|
||||||
class="form-text text-muted label p-0 js-cluster-application-update-details"
|
class="form-text text-muted label p-0 js-cluster-application-update-details"
|
||||||
>
|
>
|
||||||
{{ versionLabel }}
|
{{ versionLabel }}
|
||||||
<span v-if="updateSuccessful">to</span>
|
|
||||||
|
|
||||||
<gl-link
|
<gl-link
|
||||||
v-if="updateSuccessful"
|
v-if="updateSuccessful"
|
||||||
:href="chartRepo"
|
:href="chartRepo"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||||
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
|
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
|
||||||
import { GlLoadingIcon } from '@gitlab/ui';
|
import { GlLoadingIcon } from '@gitlab/ui';
|
||||||
import { s__ } from '~/locale';
|
import { __, s__ } from '~/locale';
|
||||||
|
|
||||||
import { APPLICATION_STATUS } from '~/clusters/constants';
|
import { APPLICATION_STATUS } from '~/clusters/constants';
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ export default {
|
||||||
return [UPDATING].includes(this.knative.status);
|
return [UPDATING].includes(this.knative.status);
|
||||||
},
|
},
|
||||||
saveButtonLabel() {
|
saveButtonLabel() {
|
||||||
return this.saving ? this.__('Saving') : this.__('Save changes');
|
return this.saving ? __('Saving') : __('Save changes');
|
||||||
},
|
},
|
||||||
knativeInstalled() {
|
knativeInstalled() {
|
||||||
return this.knative.installed;
|
return this.knative.installed;
|
||||||
|
@ -122,9 +122,9 @@ export default {
|
||||||
`ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint.`,
|
`ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint.`,
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
|
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">{{
|
||||||
{{ __('More information') }}
|
__('More information')
|
||||||
</a>
|
}}</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||||
import { APPLICATION_STATUS } from '~/clusters/constants';
|
import { APPLICATION_STATUS } from '~/clusters/constants';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
|
||||||
const { UPDATING, UNINSTALLING } = APPLICATION_STATUS;
|
const { UPDATING, UNINSTALLING } = APPLICATION_STATUS;
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ export default {
|
||||||
return this.status === UNINSTALLING;
|
return this.status === UNINSTALLING;
|
||||||
},
|
},
|
||||||
label() {
|
label() {
|
||||||
return this.loading ? this.__('Uninstalling') : this.__('Uninstall');
|
return this.loading ? __('Uninstalling') : __('Uninstall');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,9 @@ const CUSTOM_APP_WARNING_TEXT = {
|
||||||
[PROMETHEUS]: s__('ClusterIntegration|All data will be deleted and cannot be restored.'),
|
[PROMETHEUS]: s__('ClusterIntegration|All data will be deleted and cannot be restored.'),
|
||||||
[RUNNER]: s__('ClusterIntegration|Any running pipelines will be canceled.'),
|
[RUNNER]: s__('ClusterIntegration|Any running pipelines will be canceled.'),
|
||||||
[KNATIVE]: s__('ClusterIntegration|The associated IP will be deleted and cannot be restored.'),
|
[KNATIVE]: s__('ClusterIntegration|The associated IP will be deleted and cannot be restored.'),
|
||||||
[JUPYTER]: '',
|
[JUPYTER]: s__(
|
||||||
|
'ClusterIntegration|All data not committed to GitLab will be deleted and cannot be restored.',
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -80,6 +80,9 @@ const applicationStateMachine = {
|
||||||
installFailed: false,
|
installFailed: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[NOT_INSTALLABLE]: {
|
||||||
|
target: NOT_INSTALLABLE,
|
||||||
|
},
|
||||||
// This is possible in artificial environments for E2E testing
|
// This is possible in artificial environments for E2E testing
|
||||||
[INSTALLED]: {
|
[INSTALLED]: {
|
||||||
target: INSTALLED,
|
target: INSTALLED,
|
||||||
|
@ -108,6 +111,9 @@ const applicationStateMachine = {
|
||||||
updateSuccessful: false,
|
updateSuccessful: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
[NOT_INSTALLABLE]: {
|
||||||
|
target: NOT_INSTALLABLE,
|
||||||
|
},
|
||||||
[UNINSTALL_EVENT]: {
|
[UNINSTALL_EVENT]: {
|
||||||
target: UNINSTALLING,
|
target: UNINSTALLING,
|
||||||
effects: {
|
effects: {
|
||||||
|
|
|
@ -4,3 +4,6 @@ import './jquery';
|
||||||
import './bootstrap';
|
import './bootstrap';
|
||||||
import './vue';
|
import './vue';
|
||||||
import '../lib/utils/axios_utils';
|
import '../lib/utils/axios_utils';
|
||||||
|
import { openUserCountsBroadcast } from './nav/user_merge_requests';
|
||||||
|
|
||||||
|
openUserCountsBroadcast();
|
||||||
|
|
67
app/assets/javascripts/commons/nav/user_merge_requests.js
Normal file
67
app/assets/javascripts/commons/nav/user_merge_requests.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import Api from '~/api';
|
||||||
|
|
||||||
|
let channel;
|
||||||
|
|
||||||
|
function broadcastCount(newCount) {
|
||||||
|
if (!channel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.postMessage(newCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUserMergeRequestCounts(newCount) {
|
||||||
|
const mergeRequestsCountEl = document.querySelector('.merge-requests-count');
|
||||||
|
mergeRequestsCountEl.textContent = newCount.toLocaleString();
|
||||||
|
mergeRequestsCountEl.classList.toggle('hidden', Number(newCount) === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh user counts (and broadcast if open)
|
||||||
|
*/
|
||||||
|
export function refreshUserMergeRequestCounts() {
|
||||||
|
return Api.userCounts()
|
||||||
|
.then(({ data }) => {
|
||||||
|
const count = data.merge_requests;
|
||||||
|
|
||||||
|
updateUserMergeRequestCounts(count);
|
||||||
|
broadcastCount(count);
|
||||||
|
})
|
||||||
|
.catch(ex => {
|
||||||
|
console.error(ex); // eslint-disable-line no-console
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the broadcast channel for user counts
|
||||||
|
*/
|
||||||
|
export function closeUserCountsBroadcast() {
|
||||||
|
if (!channel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.close();
|
||||||
|
channel = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the broadcast channel for user counts, adds user id so we only update
|
||||||
|
*
|
||||||
|
* **Please note:**
|
||||||
|
* Not supported in all browsers, but not polyfilling for now
|
||||||
|
* to keep bundle size small and
|
||||||
|
* no special functionality lost except cross tab notifications
|
||||||
|
*/
|
||||||
|
export function openUserCountsBroadcast() {
|
||||||
|
closeUserCountsBroadcast();
|
||||||
|
|
||||||
|
if (window.BroadcastChannel) {
|
||||||
|
const currentUserId = typeof gon !== 'undefined' && gon && gon.current_user_id;
|
||||||
|
if (currentUserId) {
|
||||||
|
channel = new BroadcastChannel(`mr_count_channel_${currentUserId}`);
|
||||||
|
channel.onmessage = ev => {
|
||||||
|
updateUserMergeRequestCounts(ev.data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import 'core-js/es/promise/finally';
|
||||||
import 'core-js/es/string/code-point-at';
|
import 'core-js/es/string/code-point-at';
|
||||||
import 'core-js/es/string/from-code-point';
|
import 'core-js/es/string/from-code-point';
|
||||||
import 'core-js/es/string/includes';
|
import 'core-js/es/string/includes';
|
||||||
|
import 'core-js/es/string/starts-with';
|
||||||
import 'core-js/es/symbol';
|
import 'core-js/es/symbol';
|
||||||
import 'core-js/es/map';
|
import 'core-js/es/map';
|
||||||
import 'core-js/es/weak-map';
|
import 'core-js/es/weak-map';
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<script>
|
||||||
|
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
GlDropdown,
|
||||||
|
GlDropdownItem,
|
||||||
|
Icon,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
projects: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
selectedProject: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
dropdownText() {
|
||||||
|
if (Object.keys(this.selectedProject).length) {
|
||||||
|
return this.selectedProject.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return __('Select private project');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectProject(project) {
|
||||||
|
this.$emit('click', project);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<gl-dropdown toggle-class="d-flex align-items-center w-100" class="w-100">
|
||||||
|
<template slot="button-content">
|
||||||
|
<span class="str-truncated-100 mr-2">
|
||||||
|
<icon name="lock" />
|
||||||
|
{{ dropdownText }}
|
||||||
|
</span>
|
||||||
|
<icon name="chevron-down" class="ml-auto" />
|
||||||
|
</template>
|
||||||
|
<gl-dropdown-item v-for="project in projects" :key="project.id" @click="selectProject(project)">
|
||||||
|
<icon
|
||||||
|
name="mobile-issue-close"
|
||||||
|
:class="{ icon: project.id !== selectedProject.id }"
|
||||||
|
class="js-active-project-check"
|
||||||
|
/>
|
||||||
|
<span class="ml-1">{{ project.name }}</span>
|
||||||
|
</gl-dropdown-item>
|
||||||
|
</gl-dropdown>
|
||||||
|
</template>
|
|
@ -0,0 +1,140 @@
|
||||||
|
<script>
|
||||||
|
import { GlLink } from '@gitlab/ui';
|
||||||
|
import { __, sprintf } from '../../locale';
|
||||||
|
import createFlash from '../../flash';
|
||||||
|
import Api from '../../api';
|
||||||
|
import state from '../state';
|
||||||
|
import Dropdown from './dropdown.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
GlLink,
|
||||||
|
Dropdown,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
namespacePath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
projectPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
newForkPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
helpPagePath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
projects: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedProject() {
|
||||||
|
return state.selectedProject;
|
||||||
|
},
|
||||||
|
noForkText() {
|
||||||
|
return sprintf(
|
||||||
|
__(
|
||||||
|
"To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private.",
|
||||||
|
),
|
||||||
|
{ link_start: `<a href="${this.newForkPath}" class="help-link">`, link_end: '</a>' },
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchProjects();
|
||||||
|
this.createBtn = document.querySelector('.js-create-target');
|
||||||
|
this.warningText = document.querySelector('.js-exposed-info-warning');
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectProject(project) {
|
||||||
|
if (project) {
|
||||||
|
Object.assign(state, {
|
||||||
|
selectedProject: project,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (project.namespaceFullPath !== this.namespacePath) {
|
||||||
|
this.showWarning();
|
||||||
|
}
|
||||||
|
} else if (this.createBtn) {
|
||||||
|
this.createBtn.setAttribute('disabled', 'disabled');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
normalizeProjectData(data) {
|
||||||
|
return data.map(p => ({
|
||||||
|
id: p.id,
|
||||||
|
name: p.name_with_namespace,
|
||||||
|
pathWithNamespace: p.path_with_namespace,
|
||||||
|
namespaceFullpath: p.namespace.full_path,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
fetchProjects() {
|
||||||
|
Api.projectForks(this.projectPath, {
|
||||||
|
with_merge_requests_enabled: true,
|
||||||
|
min_access_level: 30,
|
||||||
|
visibility: 'private',
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
this.projects = this.normalizeProjectData(data);
|
||||||
|
this.selectProject(this.projects[0]);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
createFlash(__('Error fetching forked projects. Please try again.'));
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
showWarning() {
|
||||||
|
if (this.warningText) {
|
||||||
|
this.warningText.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.createBtn) {
|
||||||
|
this.createBtn.classList.add('btn-warning');
|
||||||
|
this.createBtn.classList.remove('btn-success');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="confidential-merge-request-fork-group form-group">
|
||||||
|
<label>{{ __('Project') }}</label>
|
||||||
|
<div>
|
||||||
|
<dropdown
|
||||||
|
v-if="projects.length"
|
||||||
|
:projects="projects"
|
||||||
|
:selected-project="selectedProject"
|
||||||
|
@click="selectProject"
|
||||||
|
/>
|
||||||
|
<p class="text-muted mt-1 mb-0">
|
||||||
|
<template v-if="projects.length">
|
||||||
|
{{
|
||||||
|
__(
|
||||||
|
"To protect this issue's confidentiality, a private fork of this project was selected.",
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ __('No forks available to you.') }}<br />
|
||||||
|
<span v-html="noForkText"></span>
|
||||||
|
</template>
|
||||||
|
<gl-link
|
||||||
|
:href="helpPagePath"
|
||||||
|
class="w-auto p-0 d-inline-block text-primary bg-transparent"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<span class="sr-only">{{ __('Read more') }}</span>
|
||||||
|
<i class="fa fa-question-circle" aria-hidden="true"></i>
|
||||||
|
</gl-link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
30
app/assets/javascripts/confidential_merge_request/index.js
Normal file
30
app/assets/javascripts/confidential_merge_request/index.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import { parseBoolean } from '../lib/utils/common_utils';
|
||||||
|
import ProjectFormGroup from './components/project_form_group.vue';
|
||||||
|
import state from './state';
|
||||||
|
|
||||||
|
export function isConfidentialIssue() {
|
||||||
|
return parseBoolean(document.querySelector('.js-create-mr').dataset.isConfidential);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canCreateConfidentialMergeRequest() {
|
||||||
|
return isConfidentialIssue() && Object.keys(state.selectedProject).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function init() {
|
||||||
|
const el = document.getElementById('js-forked-project');
|
||||||
|
|
||||||
|
return new Vue({
|
||||||
|
el,
|
||||||
|
render(h) {
|
||||||
|
return h(ProjectFormGroup, {
|
||||||
|
props: {
|
||||||
|
namespacePath: el.dataset.namespacePath,
|
||||||
|
projectPath: el.dataset.projectPath,
|
||||||
|
newForkPath: el.dataset.newForkPath,
|
||||||
|
helpPagePath: el.dataset.helpPagePath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.observable({
|
||||||
|
selectedProject: {},
|
||||||
|
});
|
|
@ -5,6 +5,12 @@ import Flash from './flash';
|
||||||
import DropLab from './droplab/drop_lab';
|
import DropLab from './droplab/drop_lab';
|
||||||
import ISetter from './droplab/plugins/input_setter';
|
import ISetter from './droplab/plugins/input_setter';
|
||||||
import { __, sprintf } from './locale';
|
import { __, sprintf } from './locale';
|
||||||
|
import {
|
||||||
|
init as initConfidentialMergeRequest,
|
||||||
|
isConfidentialIssue,
|
||||||
|
canCreateConfidentialMergeRequest,
|
||||||
|
} from './confidential_merge_request';
|
||||||
|
import confidentialMergeRequestState from './confidential_merge_request/state';
|
||||||
|
|
||||||
// Todo: Remove this when fixing issue in input_setter plugin
|
// Todo: Remove this when fixing issue in input_setter plugin
|
||||||
const InputSetter = Object.assign({}, ISetter);
|
const InputSetter = Object.assign({}, ISetter);
|
||||||
|
@ -12,6 +18,17 @@ const InputSetter = Object.assign({}, ISetter);
|
||||||
const CREATE_MERGE_REQUEST = 'create-mr';
|
const CREATE_MERGE_REQUEST = 'create-mr';
|
||||||
const CREATE_BRANCH = 'create-branch';
|
const CREATE_BRANCH = 'create-branch';
|
||||||
|
|
||||||
|
function createEndpoint(projectPath, endpoint) {
|
||||||
|
if (canCreateConfidentialMergeRequest()) {
|
||||||
|
return endpoint.replace(
|
||||||
|
projectPath,
|
||||||
|
confidentialMergeRequestState.selectedProject.pathWithNamespace,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
export default class CreateMergeRequestDropdown {
|
export default class CreateMergeRequestDropdown {
|
||||||
constructor(wrapperEl) {
|
constructor(wrapperEl) {
|
||||||
this.wrapperEl = wrapperEl;
|
this.wrapperEl = wrapperEl;
|
||||||
|
@ -42,6 +59,8 @@ export default class CreateMergeRequestDropdown {
|
||||||
this.refIsValid = true;
|
this.refIsValid = true;
|
||||||
this.refsPath = this.wrapperEl.dataset.refsPath;
|
this.refsPath = this.wrapperEl.dataset.refsPath;
|
||||||
this.suggestedRef = this.refInput.value;
|
this.suggestedRef = this.refInput.value;
|
||||||
|
this.projectPath = this.wrapperEl.dataset.projectPath;
|
||||||
|
this.projectId = this.wrapperEl.dataset.projectId;
|
||||||
|
|
||||||
// These regexps are used to replace
|
// These regexps are used to replace
|
||||||
// a backend generated new branch name and its source (ref)
|
// a backend generated new branch name and its source (ref)
|
||||||
|
@ -58,6 +77,14 @@ export default class CreateMergeRequestDropdown {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
|
if (isConfidentialIssue()) {
|
||||||
|
this.createMergeRequestButton.setAttribute(
|
||||||
|
'data-dropdown-trigger',
|
||||||
|
'#create-merge-request-dropdown',
|
||||||
|
);
|
||||||
|
initConfidentialMergeRequest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
available() {
|
available() {
|
||||||
|
@ -113,7 +140,9 @@ export default class CreateMergeRequestDropdown {
|
||||||
this.isCreatingBranch = true;
|
this.isCreatingBranch = true;
|
||||||
|
|
||||||
return axios
|
return axios
|
||||||
.post(this.createBranchPath)
|
.post(createEndpoint(this.projectPath, this.createBranchPath), {
|
||||||
|
confidential_issue_project_id: canCreateConfidentialMergeRequest() ? this.projectId : null,
|
||||||
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
this.branchCreated = true;
|
this.branchCreated = true;
|
||||||
window.location.href = data.url;
|
window.location.href = data.url;
|
||||||
|
@ -125,7 +154,11 @@ export default class CreateMergeRequestDropdown {
|
||||||
this.isCreatingMergeRequest = true;
|
this.isCreatingMergeRequest = true;
|
||||||
|
|
||||||
return axios
|
return axios
|
||||||
.post(this.createMrPath)
|
.post(this.createMrPath, {
|
||||||
|
target_project_id: canCreateConfidentialMergeRequest()
|
||||||
|
? confidentialMergeRequestState.selectedProject.id
|
||||||
|
: null,
|
||||||
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
this.mergeRequestCreated = true;
|
this.mergeRequestCreated = true;
|
||||||
window.location.href = data.url;
|
window.location.href = data.url;
|
||||||
|
@ -149,6 +182,8 @@ export default class CreateMergeRequestDropdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
enable() {
|
enable() {
|
||||||
|
if (isConfidentialIssue() && !canCreateConfidentialMergeRequest()) return;
|
||||||
|
|
||||||
this.createMergeRequestButton.classList.remove('disabled');
|
this.createMergeRequestButton.classList.remove('disabled');
|
||||||
this.createMergeRequestButton.removeAttribute('disabled');
|
this.createMergeRequestButton.removeAttribute('disabled');
|
||||||
|
|
||||||
|
@ -205,7 +240,7 @@ export default class CreateMergeRequestDropdown {
|
||||||
if (!ref) return false;
|
if (!ref) return false;
|
||||||
|
|
||||||
return axios
|
return axios
|
||||||
.get(`${this.refsPath}${encodeURIComponent(ref)}`)
|
.get(`${createEndpoint(this.projectPath, this.refsPath)}${encodeURIComponent(ref)}`)
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
const branches = data[Object.keys(data)[0]];
|
const branches = data[Object.keys(data)[0]];
|
||||||
const tags = data[Object.keys(data)[1]];
|
const tags = data[Object.keys(data)[1]];
|
||||||
|
@ -325,6 +360,12 @@ export default class CreateMergeRequestDropdown {
|
||||||
let xhr = null;
|
let xhr = null;
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (isConfidentialIssue() && !event.target.classList.contains('js-create-target')) {
|
||||||
|
this.droplab.hooks.forEach(hook => hook.list.toggle());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isBusy()) {
|
if (this.isBusy()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ Vue.use(Translate);
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
|
const OVERVIEW_DIALOG_COOKIE = 'cycle_analytics_help_dismissed';
|
||||||
|
const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new Vue({
|
new Vue({
|
||||||
|
@ -33,7 +34,6 @@ export default () => {
|
||||||
'stage-production-component': stageComponent,
|
'stage-production-component': stageComponent,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const cycleAnalyticsEl = document.querySelector('#cycle-analytics');
|
|
||||||
const cycleAnalyticsService = new CycleAnalyticsService({
|
const cycleAnalyticsService = new CycleAnalyticsService({
|
||||||
requestPath: cycleAnalyticsEl.dataset.requestPath,
|
requestPath: cycleAnalyticsEl.dataset.requestPath,
|
||||||
});
|
});
|
||||||
|
@ -56,7 +56,13 @@ export default () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.fetchCycleAnalyticsData();
|
// Conditional check placed here to prevent this method from being called on the
|
||||||
|
// new Cycle Analytics page (i.e. the new page will be initialized blank and only
|
||||||
|
// after a group is selected the cycle analyitcs data will be fetched). Once the
|
||||||
|
// old (current) page has been removed this entire created method as well as the
|
||||||
|
// variable itself can be completely removed.
|
||||||
|
// Follow up issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/64490
|
||||||
|
if (cycleAnalyticsEl.dataset.requestPath) this.fetchCycleAnalyticsData();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleError() {
|
handleError() {
|
||||||
|
|
|
@ -32,15 +32,15 @@ const CommentAndResolveBtn = Vue.extend({
|
||||||
buttonText: function() {
|
buttonText: function() {
|
||||||
if (this.isDiscussionResolved) {
|
if (this.isDiscussionResolved) {
|
||||||
if (this.textareaIsEmpty) {
|
if (this.textareaIsEmpty) {
|
||||||
return __('Unresolve discussion');
|
return __('Unresolve thread');
|
||||||
} else {
|
} else {
|
||||||
return __('Comment & unresolve discussion');
|
return __('Comment & unresolve thread');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.textareaIsEmpty) {
|
if (this.textareaIsEmpty) {
|
||||||
return __('Resolve discussion');
|
return __('Resolve thread');
|
||||||
} else {
|
} else {
|
||||||
return __('Comment & resolve discussion');
|
return __('Comment & resolve thread');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -49,6 +49,8 @@ export default {
|
||||||
return this.author.id ? this.author.id : '';
|
return this.author.id ? this.author.id : '';
|
||||||
},
|
},
|
||||||
authorUrl() {
|
authorUrl() {
|
||||||
|
// TODO: when the vue i18n rules are merged need to disable @gitlab/i18n/no-non-i18n-strings
|
||||||
|
// name: 'mailto:' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives
|
||||||
return this.author.web_url || `mailto:${this.commit.author_email}`;
|
return this.author.web_url || `mailto:${this.commit.author_email}`;
|
||||||
},
|
},
|
||||||
authorAvatar() {
|
authorAvatar() {
|
||||||
|
@ -80,7 +82,7 @@ export default {
|
||||||
v-html="commit.title_html"
|
v-html="commit.title_html"
|
||||||
></a>
|
></a>
|
||||||
|
|
||||||
<span class="commit-row-message d-block d-sm-none"> · {{ commit.short_id }} </span>
|
<span class="commit-row-message d-block d-sm-none">· {{ commit.short_id }}</span>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="commit.description_html"
|
v-if="commit.description_html"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import { n__, __ } from '~/locale';
|
import { n__, __, sprintf } from '~/locale';
|
||||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -54,11 +54,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
commitsText(version) {
|
commitsText(version) {
|
||||||
return n__(
|
return n__(`%d commit,`, `%d commits,`, version.commits_count);
|
||||||
`${version.commits_count} commit,`,
|
|
||||||
`${version.commits_count} commits,`,
|
|
||||||
version.commits_count,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
href(version) {
|
href(version) {
|
||||||
if (this.isBase(version)) {
|
if (this.isBase(version)) {
|
||||||
|
@ -76,7 +72,7 @@ export default {
|
||||||
if (this.targetBranch && (this.isBase(version) || !version)) {
|
if (this.targetBranch && (this.isBase(version) || !version)) {
|
||||||
return this.targetBranch.branchName;
|
return this.targetBranch.branchName;
|
||||||
}
|
}
|
||||||
return `version ${version.version_index}`;
|
return sprintf(__(`version %{versionIndex}`), { versionIndex: version.version_index });
|
||||||
},
|
},
|
||||||
isActive(version) {
|
isActive(version) {
|
||||||
if (!version) {
|
if (!version) {
|
||||||
|
@ -125,9 +121,9 @@ export default {
|
||||||
<div>
|
<div>
|
||||||
<strong>
|
<strong>
|
||||||
{{ versionName(version) }}
|
{{ versionName(version) }}
|
||||||
<template v-if="isBase(version)">
|
<template v-if="isBase(version)">{{
|
||||||
(base)
|
s__('DiffsCompareBaseBranch|(base)')
|
||||||
</template>
|
}}</template>
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue';
|
||||||
|
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
|
||||||
|
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DiffDiscussionReply',
|
||||||
|
components: {
|
||||||
|
NoteSignedOutWidget,
|
||||||
|
ReplyPlaceholder,
|
||||||
|
UserAvatarLink,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
hasForm: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
renderReplyPlaceholder: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
currentUser: 'getUserData',
|
||||||
|
userCanReply: 'userCanReply',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="discussion-reply-holder d-flex clearfix">
|
||||||
|
<template v-if="userCanReply">
|
||||||
|
<slot v-if="hasForm" name="form"></slot>
|
||||||
|
<template v-else-if="renderReplyPlaceholder">
|
||||||
|
<user-avatar-link
|
||||||
|
:link-href="currentUser.path"
|
||||||
|
:img-src="currentUser.avatar_url"
|
||||||
|
:img-alt="currentUser.name"
|
||||||
|
:img-size="40"
|
||||||
|
class="d-none d-sm-block"
|
||||||
|
/>
|
||||||
|
<reply-placeholder
|
||||||
|
class="qa-discussion-reply"
|
||||||
|
:button-text="__('Start a new discussion...')"
|
||||||
|
@onClick="$emit('showNewDiscussionForm')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<note-signed-out-widget v-else />
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -80,7 +80,6 @@ export default {
|
||||||
v-show="isExpanded(discussion)"
|
v-show="isExpanded(discussion)"
|
||||||
:discussion="discussion"
|
:discussion="discussion"
|
||||||
:render-diff-file="false"
|
:render-diff-file="false"
|
||||||
:always-expanded="true"
|
|
||||||
:discussions-by-diff-order="true"
|
:discussions-by-diff-order="true"
|
||||||
:line="line"
|
:line="line"
|
||||||
:help-page-path="helpPagePath"
|
:help-page-path="helpPagePath"
|
||||||
|
|
|
@ -67,6 +67,18 @@ export default {
|
||||||
errorMessage() {
|
errorMessage() {
|
||||||
return this.file.viewer.error_message;
|
return this.file.viewer.error_message;
|
||||||
},
|
},
|
||||||
|
forkMessage() {
|
||||||
|
return sprintf(
|
||||||
|
__(
|
||||||
|
"You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request.",
|
||||||
|
),
|
||||||
|
{
|
||||||
|
tag_start: '<span class="js-file-fork-suggestion-section-action">',
|
||||||
|
tag_end: '</span>',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
isCollapsed: function fileCollapsedWatch(newVal, oldVal) {
|
isCollapsed: function fileCollapsedWatch(newVal, oldVal) {
|
||||||
|
@ -150,22 +162,18 @@ export default {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="forkMessageVisible" class="js-file-fork-suggestion-section file-fork-suggestion">
|
<div v-if="forkMessageVisible" class="js-file-fork-suggestion-section file-fork-suggestion">
|
||||||
<span class="file-fork-suggestion-note">
|
<span class="file-fork-suggestion-note" v-html="forkMessage"></span>
|
||||||
You're not allowed to <span class="js-file-fork-suggestion-section-action">edit</span> files
|
|
||||||
in this project directly. Please fork this project, make your changes there, and submit a
|
|
||||||
merge request.
|
|
||||||
</span>
|
|
||||||
<a
|
<a
|
||||||
:href="file.fork_path"
|
:href="file.fork_path"
|
||||||
class="js-fork-suggestion-button btn btn-grouped btn-inverted btn-success"
|
class="js-fork-suggestion-button btn btn-grouped btn-inverted btn-success"
|
||||||
>Fork</a
|
>{{ __('Fork') }}</a
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="js-cancel-fork-suggestion-button btn btn-grouped"
|
class="js-cancel-fork-suggestion-button btn btn-grouped"
|
||||||
type="button"
|
type="button"
|
||||||
@click="hideForkMessage"
|
@click="hideForkMessage"
|
||||||
>
|
>
|
||||||
Cancel
|
{{ __('Cancel') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<gl-loading-icon v-if="showLoadingIcon" class="diff-content loading" />
|
<gl-loading-icon v-if="showLoadingIcon" class="diff-content loading" />
|
||||||
|
|
|
@ -151,7 +151,11 @@ export default {
|
||||||
stickyMonitor(this.$refs.header, contentTop() - fileHeaderHeight - 1, false);
|
stickyMonitor(this.$refs.header, contentTop() - fileHeaderHeight - 1, false);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions('diffs', ['toggleFileDiscussions', 'toggleFullDiff']),
|
...mapActions('diffs', [
|
||||||
|
'toggleFileDiscussions',
|
||||||
|
'toggleFileDiscussionWrappers',
|
||||||
|
'toggleFullDiff',
|
||||||
|
]),
|
||||||
handleToggleFile(e, checkTarget) {
|
handleToggleFile(e, checkTarget) {
|
||||||
if (
|
if (
|
||||||
!checkTarget ||
|
!checkTarget ||
|
||||||
|
@ -165,7 +169,7 @@ export default {
|
||||||
this.$emit('showForkMessage');
|
this.$emit('showForkMessage');
|
||||||
},
|
},
|
||||||
handleToggleDiscussions() {
|
handleToggleDiscussions() {
|
||||||
this.toggleFileDiscussions(this.diffFile);
|
this.toggleFileDiscussionWrappers(this.diffFile);
|
||||||
},
|
},
|
||||||
handleFileNameClick(e) {
|
handleFileNameClick(e) {
|
||||||
const isLinkToOtherPage =
|
const isLinkToOtherPage =
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script>
|
<script>
|
||||||
import { mapActions } from 'vuex';
|
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import { pluralize, truncate } from '~/lib/utils/text_utility';
|
import { pluralize, truncate } from '~/lib/utils/text_utility';
|
||||||
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
|
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
|
||||||
|
@ -19,11 +18,13 @@ export default {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
discussionsExpanded: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
discussionsExpanded() {
|
|
||||||
return this.discussions.every(discussion => discussion.expanded);
|
|
||||||
},
|
|
||||||
allDiscussions() {
|
allDiscussions() {
|
||||||
return this.discussions.reduce((acc, note) => acc.concat(note.notes), []);
|
return this.discussions.reduce((acc, note) => acc.concat(note.notes), []);
|
||||||
},
|
},
|
||||||
|
@ -45,26 +46,14 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['toggleDiscussion']),
|
|
||||||
getTooltipText(noteData) {
|
getTooltipText(noteData) {
|
||||||
let { note } = noteData;
|
let { note } = noteData;
|
||||||
|
|
||||||
if (note.length > LENGTH_OF_AVATAR_TOOLTIP) {
|
if (note.length > LENGTH_OF_AVATAR_TOOLTIP) {
|
||||||
note = truncate(note, LENGTH_OF_AVATAR_TOOLTIP);
|
note = truncate(note, LENGTH_OF_AVATAR_TOOLTIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${noteData.author.name}: ${note}`;
|
return `${noteData.author.name}: ${note}`;
|
||||||
},
|
},
|
||||||
toggleDiscussions() {
|
|
||||||
const forceExpanded = this.discussions.some(discussion => !discussion.expanded);
|
|
||||||
|
|
||||||
this.discussions.forEach(discussion => {
|
|
||||||
this.toggleDiscussion({
|
|
||||||
discussionId: discussion.id,
|
|
||||||
forceExpanded,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -74,9 +63,9 @@ export default {
|
||||||
<button
|
<button
|
||||||
v-if="discussionsExpanded"
|
v-if="discussionsExpanded"
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="Show comments"
|
:aria-label="__('Show comments')"
|
||||||
class="diff-notes-collapse js-diff-comment-avatar js-diff-comment-button"
|
class="diff-notes-collapse js-diff-comment-avatar js-diff-comment-button"
|
||||||
@click="toggleDiscussions"
|
@click="$emit('toggleLineDiscussions')"
|
||||||
>
|
>
|
||||||
<icon :size="12" name="collapse" />
|
<icon :size="12" name="collapse" />
|
||||||
</button>
|
</button>
|
||||||
|
@ -87,7 +76,7 @@ export default {
|
||||||
:img-src="note.author.avatar_url"
|
:img-src="note.author.avatar_url"
|
||||||
:tooltip-text="getTooltipText(note)"
|
:tooltip-text="getTooltipText(note)"
|
||||||
class="diff-comment-avatar js-diff-comment-avatar"
|
class="diff-comment-avatar js-diff-comment-avatar"
|
||||||
@click.native="toggleDiscussions"
|
@click.native="$emit('toggleLineDiscussions')"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
v-if="moreText"
|
v-if="moreText"
|
||||||
|
@ -97,7 +86,7 @@ export default {
|
||||||
data-container="body"
|
data-container="body"
|
||||||
data-placement="top"
|
data-placement="top"
|
||||||
role="button"
|
role="button"
|
||||||
@click="toggleDiscussions"
|
@click="$emit('toggleLineDiscussions')"
|
||||||
>+{{ moreCount }}</span
|
>+{{ moreCount }}</span
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -105,7 +105,13 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions('diffs', ['loadMoreLines', 'showCommentForm', 'setHighlightedRow']),
|
...mapActions('diffs', [
|
||||||
|
'loadMoreLines',
|
||||||
|
'showCommentForm',
|
||||||
|
'setHighlightedRow',
|
||||||
|
'toggleLineDiscussions',
|
||||||
|
'toggleLineDiscussionWrappers',
|
||||||
|
]),
|
||||||
handleCommentButton() {
|
handleCommentButton() {
|
||||||
this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.fileHash });
|
this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.fileHash });
|
||||||
},
|
},
|
||||||
|
@ -184,7 +190,14 @@ export default {
|
||||||
@click="setHighlightedRow(lineCode)"
|
@click="setHighlightedRow(lineCode)"
|
||||||
>
|
>
|
||||||
</a>
|
</a>
|
||||||
<diff-gutter-avatars v-if="shouldShowAvatarsOnGutter" :discussions="line.discussions" />
|
<diff-gutter-avatars
|
||||||
|
v-if="shouldShowAvatarsOnGutter"
|
||||||
|
:discussions="line.discussions"
|
||||||
|
:discussions-expanded="line.discussionsExpanded"
|
||||||
|
@toggleLineDiscussions="
|
||||||
|
toggleLineDiscussions({ lineCode, fileHash, expanded: !line.discussionsExpanded })
|
||||||
|
"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import diffDiscussions from './diff_discussions.vue';
|
import { mapActions } from 'vuex';
|
||||||
import diffLineNoteForm from './diff_line_note_form.vue';
|
import DiffDiscussions from './diff_discussions.vue';
|
||||||
|
import DiffLineNoteForm from './diff_line_note_form.vue';
|
||||||
|
import DiffDiscussionReply from './diff_discussion_reply.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
diffDiscussions,
|
DiffDiscussions,
|
||||||
diffLineNoteForm,
|
DiffLineNoteForm,
|
||||||
|
DiffDiscussionReply,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
line: {
|
line: {
|
||||||
|
@ -21,6 +24,11 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
hasDraft: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
className() {
|
className() {
|
||||||
|
@ -32,10 +40,12 @@ export default {
|
||||||
if (!this.line.discussions || !this.line.discussions.length) {
|
if (!this.line.discussions || !this.line.discussions.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return this.line.discussionsExpanded;
|
||||||
return this.line.discussions.every(discussion => discussion.expanded);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions('diffs', ['showCommentForm']),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -49,13 +59,23 @@ export default {
|
||||||
:discussions="line.discussions"
|
:discussions="line.discussions"
|
||||||
:help-page-path="helpPagePath"
|
:help-page-path="helpPagePath"
|
||||||
/>
|
/>
|
||||||
<diff-line-note-form
|
<diff-discussion-reply
|
||||||
v-if="line.hasForm"
|
v-if="!hasDraft"
|
||||||
:diff-file-hash="diffFileHash"
|
:has-form="line.hasForm"
|
||||||
:line="line"
|
:render-reply-placeholder="Boolean(line.discussions.length)"
|
||||||
:note-target-line="line"
|
@showNewDiscussionForm="
|
||||||
:help-page-path="helpPagePath"
|
showCommentForm({ lineCode: line.line_code, fileHash: diffFileHash })
|
||||||
/>
|
"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<diff-line-note-form
|
||||||
|
:diff-file-hash="diffFileHash"
|
||||||
|
:line="line"
|
||||||
|
:note-target-line="line"
|
||||||
|
:help-page-path="helpPagePath"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</diff-discussion-reply>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -57,6 +57,7 @@ export default {
|
||||||
:diff-file-hash="diffFile.file_hash"
|
:diff-file-hash="diffFile.file_hash"
|
||||||
:line="line"
|
:line="line"
|
||||||
:help-page-path="helpPagePath"
|
:help-page-path="helpPagePath"
|
||||||
|
:has-draft="shouldRenderDraftRow(diffFile.file_hash, line) || false"
|
||||||
/>
|
/>
|
||||||
<inline-draft-comment-row
|
<inline-draft-comment-row
|
||||||
v-if="shouldRenderDraftRow(diffFile.file_hash, line)"
|
v-if="shouldRenderDraftRow(diffFile.file_hash, line)"
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import diffDiscussions from './diff_discussions.vue';
|
import { mapActions } from 'vuex';
|
||||||
import diffLineNoteForm from './diff_line_note_form.vue';
|
import DiffDiscussions from './diff_discussions.vue';
|
||||||
|
import DiffLineNoteForm from './diff_line_note_form.vue';
|
||||||
|
import DiffDiscussionReply from './diff_discussion_reply.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
diffDiscussions,
|
DiffDiscussions,
|
||||||
diffLineNoteForm,
|
DiffLineNoteForm,
|
||||||
|
DiffDiscussionReply,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
line: {
|
line: {
|
||||||
|
@ -25,28 +28,44 @@ export default {
|
||||||
required: false,
|
required: false,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
hasDraftLeft: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
hasDraftRight: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
hasExpandedDiscussionOnLeft() {
|
hasExpandedDiscussionOnLeft() {
|
||||||
return this.line.left && this.line.left.discussions.length
|
return this.line.left && this.line.left.discussions.length
|
||||||
? this.line.left.discussions.every(discussion => discussion.expanded)
|
? this.line.left.discussionsExpanded
|
||||||
: false;
|
: false;
|
||||||
},
|
},
|
||||||
hasExpandedDiscussionOnRight() {
|
hasExpandedDiscussionOnRight() {
|
||||||
return this.line.right && this.line.right.discussions.length
|
return this.line.right && this.line.right.discussions.length
|
||||||
? this.line.right.discussions.every(discussion => discussion.expanded)
|
? this.line.right.discussionsExpanded
|
||||||
: false;
|
: false;
|
||||||
},
|
},
|
||||||
hasAnyExpandedDiscussion() {
|
hasAnyExpandedDiscussion() {
|
||||||
return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight;
|
return this.hasExpandedDiscussionOnLeft || this.hasExpandedDiscussionOnRight;
|
||||||
},
|
},
|
||||||
shouldRenderDiscussionsOnLeft() {
|
shouldRenderDiscussionsOnLeft() {
|
||||||
return this.line.left && this.line.left.discussions && this.hasExpandedDiscussionOnLeft;
|
return (
|
||||||
|
this.line.left &&
|
||||||
|
this.line.left.discussions &&
|
||||||
|
this.line.left.discussions.length &&
|
||||||
|
this.hasExpandedDiscussionOnLeft
|
||||||
|
);
|
||||||
},
|
},
|
||||||
shouldRenderDiscussionsOnRight() {
|
shouldRenderDiscussionsOnRight() {
|
||||||
return (
|
return (
|
||||||
this.line.right &&
|
this.line.right &&
|
||||||
this.line.right.discussions &&
|
this.line.right.discussions &&
|
||||||
|
this.line.right.discussions.length &&
|
||||||
this.hasExpandedDiscussionOnRight &&
|
this.hasExpandedDiscussionOnRight &&
|
||||||
this.line.right.type
|
this.line.right.type
|
||||||
);
|
);
|
||||||
|
@ -81,6 +100,22 @@ export default {
|
||||||
|
|
||||||
return hasCommentFormOnLeft || hasCommentFormOnRight;
|
return hasCommentFormOnLeft || hasCommentFormOnRight;
|
||||||
},
|
},
|
||||||
|
shouldRenderReplyPlaceholderOnLeft() {
|
||||||
|
return Boolean(
|
||||||
|
this.line.left && this.line.left.discussions && this.line.left.discussions.length,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
shouldRenderReplyPlaceholderOnRight() {
|
||||||
|
return Boolean(
|
||||||
|
this.line.right && this.line.right.discussions && this.line.right.discussions.length,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions('diffs', ['showCommentForm']),
|
||||||
|
showNewDiscussionForm() {
|
||||||
|
this.showCommentForm({ lineCode: this.line.line_code, fileHash: this.diffFileHash });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -90,37 +125,51 @@ export default {
|
||||||
<td class="notes-content parallel old" colspan="2">
|
<td class="notes-content parallel old" colspan="2">
|
||||||
<div v-if="shouldRenderDiscussionsOnLeft" class="content">
|
<div v-if="shouldRenderDiscussionsOnLeft" class="content">
|
||||||
<diff-discussions
|
<diff-discussions
|
||||||
v-if="line.left.discussions.length"
|
|
||||||
:discussions="line.left.discussions"
|
:discussions="line.left.discussions"
|
||||||
:line="line.left"
|
:line="line.left"
|
||||||
:help-page-path="helpPagePath"
|
:help-page-path="helpPagePath"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<diff-line-note-form
|
<diff-discussion-reply
|
||||||
v-if="showLeftSideCommentForm"
|
v-if="!hasDraftLeft"
|
||||||
:diff-file-hash="diffFileHash"
|
:has-form="showLeftSideCommentForm"
|
||||||
:line="line.left"
|
:render-reply-placeholder="shouldRenderReplyPlaceholderOnLeft"
|
||||||
:note-target-line="line.left"
|
@showNewDiscussionForm="showNewDiscussionForm"
|
||||||
:help-page-path="helpPagePath"
|
>
|
||||||
line-position="left"
|
<template #form>
|
||||||
/>
|
<diff-line-note-form
|
||||||
|
:diff-file-hash="diffFileHash"
|
||||||
|
:line="line.left"
|
||||||
|
:note-target-line="line.left"
|
||||||
|
:help-page-path="helpPagePath"
|
||||||
|
line-position="left"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</diff-discussion-reply>
|
||||||
</td>
|
</td>
|
||||||
<td class="notes-content parallel new" colspan="2">
|
<td class="notes-content parallel new" colspan="2">
|
||||||
<div v-if="shouldRenderDiscussionsOnRight" class="content">
|
<div v-if="shouldRenderDiscussionsOnRight" class="content">
|
||||||
<diff-discussions
|
<diff-discussions
|
||||||
v-if="line.right.discussions.length"
|
|
||||||
:discussions="line.right.discussions"
|
:discussions="line.right.discussions"
|
||||||
:line="line.right"
|
:line="line.right"
|
||||||
:help-page-path="helpPagePath"
|
:help-page-path="helpPagePath"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<diff-line-note-form
|
<diff-discussion-reply
|
||||||
v-if="showRightSideCommentForm"
|
v-if="!hasDraftRight"
|
||||||
:diff-file-hash="diffFileHash"
|
:has-form="showRightSideCommentForm"
|
||||||
:line="line.right"
|
:render-reply-placeholder="shouldRenderReplyPlaceholderOnRight"
|
||||||
:note-target-line="line.right"
|
@showNewDiscussionForm="showNewDiscussionForm"
|
||||||
line-position="right"
|
>
|
||||||
/>
|
<template #form>
|
||||||
|
<diff-line-note-form
|
||||||
|
:diff-file-hash="diffFileHash"
|
||||||
|
:line="line.right"
|
||||||
|
:note-target-line="line.right"
|
||||||
|
line-position="right"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</diff-discussion-reply>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -58,6 +58,8 @@ export default {
|
||||||
:diff-file-hash="diffFile.file_hash"
|
:diff-file-hash="diffFile.file_hash"
|
||||||
:line-index="index"
|
:line-index="index"
|
||||||
:help-page-path="helpPagePath"
|
:help-page-path="helpPagePath"
|
||||||
|
:has-draft-left="hasParallelDraftLeft(diffFile.file_hash, line) || false"
|
||||||
|
:has-draft-right="hasParallelDraftRight(diffFile.file_hash, line) || false"
|
||||||
/>
|
/>
|
||||||
<parallel-draft-comment-row
|
<parallel-draft-comment-row
|
||||||
v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)"
|
v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)"
|
||||||
|
|
|
@ -6,5 +6,7 @@ export default {
|
||||||
imageDiscussions() {
|
imageDiscussions() {
|
||||||
return this.diffFile.discussions;
|
return this.diffFile.discussions;
|
||||||
},
|
},
|
||||||
|
hasParallelDraftLeft: () => () => false,
|
||||||
|
hasParallelDraftRight: () => () => false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {
|
||||||
getNoteFormData,
|
getNoteFormData,
|
||||||
convertExpandLines,
|
convertExpandLines,
|
||||||
idleCallback,
|
idleCallback,
|
||||||
|
allDiscussionWrappersExpanded,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import * as types from './mutation_types';
|
import * as types from './mutation_types';
|
||||||
import {
|
import {
|
||||||
|
@ -79,6 +80,7 @@ export const assignDiscussionsToDiff = (
|
||||||
discussions = rootState.notes.discussions,
|
discussions = rootState.notes.discussions,
|
||||||
) => {
|
) => {
|
||||||
const diffPositionByLineCode = getDiffPositionByLineCode(state.diffFiles);
|
const diffPositionByLineCode = getDiffPositionByLineCode(state.diffFiles);
|
||||||
|
const hash = getLocationHash();
|
||||||
|
|
||||||
discussions
|
discussions
|
||||||
.filter(discussion => discussion.diff_discussion)
|
.filter(discussion => discussion.diff_discussion)
|
||||||
|
@ -86,6 +88,7 @@ export const assignDiscussionsToDiff = (
|
||||||
commit(types.SET_LINE_DISCUSSIONS_FOR_FILE, {
|
commit(types.SET_LINE_DISCUSSIONS_FOR_FILE, {
|
||||||
discussion,
|
discussion,
|
||||||
diffPositionByLineCode,
|
diffPositionByLineCode,
|
||||||
|
hash,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,6 +102,10 @@ export const removeDiscussionsFromDiff = ({ commit }, removeDiscussion) => {
|
||||||
commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash: file_hash, lineCode: line_code, id });
|
commit(types.REMOVE_LINE_DISCUSSIONS_FOR_FILE, { fileHash: file_hash, lineCode: line_code, id });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const toggleLineDiscussions = ({ commit }, options) => {
|
||||||
|
commit(types.TOGGLE_LINE_DISCUSSIONS, options);
|
||||||
|
};
|
||||||
|
|
||||||
export const renderFileForDiscussionId = ({ commit, rootState, state }, discussionId) => {
|
export const renderFileForDiscussionId = ({ commit, rootState, state }, discussionId) => {
|
||||||
const discussion = rootState.notes.discussions.find(d => d.id === discussionId);
|
const discussion = rootState.notes.discussions.find(d => d.id === discussionId);
|
||||||
|
|
||||||
|
@ -257,6 +264,31 @@ export const toggleFileDiscussions = ({ getters, dispatch }, diff) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const toggleFileDiscussionWrappers = ({ commit }, diff) => {
|
||||||
|
const discussionWrappersExpanded = allDiscussionWrappersExpanded(diff);
|
||||||
|
let linesWithDiscussions;
|
||||||
|
if (diff.highlighted_diff_lines) {
|
||||||
|
linesWithDiscussions = diff.highlighted_diff_lines.filter(line => line.discussions.length);
|
||||||
|
}
|
||||||
|
if (diff.parallel_diff_lines) {
|
||||||
|
linesWithDiscussions = diff.parallel_diff_lines.filter(
|
||||||
|
line =>
|
||||||
|
(line.left && line.left.discussions.length) ||
|
||||||
|
(line.right && line.right.discussions.length),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linesWithDiscussions.length) {
|
||||||
|
linesWithDiscussions.forEach(line => {
|
||||||
|
commit(types.TOGGLE_LINE_DISCUSSIONS, {
|
||||||
|
fileHash: diff.file_hash,
|
||||||
|
lineCode: line.line_code,
|
||||||
|
expanded: !discussionWrappersExpanded,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const saveDiffDiscussion = ({ state, dispatch }, { note, formData }) => {
|
export const saveDiffDiscussion = ({ state, dispatch }, { note, formData }) => {
|
||||||
const postData = getNoteFormData({
|
const postData = getNoteFormData({
|
||||||
commit: state.commit,
|
commit: state.commit,
|
||||||
|
@ -267,7 +299,7 @@ export const saveDiffDiscussion = ({ state, dispatch }, { note, formData }) => {
|
||||||
return dispatch('saveNote', postData, { root: true })
|
return dispatch('saveNote', postData, { root: true })
|
||||||
.then(result => dispatch('updateDiscussion', result.discussion, { root: true }))
|
.then(result => dispatch('updateDiscussion', result.discussion, { root: true }))
|
||||||
.then(discussion => dispatch('assignDiscussionsToDiff', [discussion]))
|
.then(discussion => dispatch('assignDiscussionsToDiff', [discussion]))
|
||||||
.then(() => dispatch('updateResolvableDiscussonsCounts', null, { root: true }))
|
.then(() => dispatch('updateResolvableDiscussionsCounts', null, { root: true }))
|
||||||
.then(() => dispatch('closeDiffFileCommentForm', formData.diffFile.file_hash))
|
.then(() => dispatch('closeDiffFileCommentForm', formData.diffFile.file_hash))
|
||||||
.catch(() => createFlash(s__('MergeRequests|Saving the comment failed')));
|
.catch(() => createFlash(s__('MergeRequests|Saving the comment failed')));
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,3 +35,5 @@ export const ADD_CURRENT_VIEW_DIFF_FILE_LINES = 'ADD_CURRENT_VIEW_DIFF_FILE_LINE
|
||||||
export const TOGGLE_DIFF_FILE_RENDERING_MORE = 'TOGGLE_DIFF_FILE_RENDERING_MORE';
|
export const TOGGLE_DIFF_FILE_RENDERING_MORE = 'TOGGLE_DIFF_FILE_RENDERING_MORE';
|
||||||
|
|
||||||
export const SET_SHOW_SUGGEST_POPOVER = 'SET_SHOW_SUGGEST_POPOVER';
|
export const SET_SHOW_SUGGEST_POPOVER = 'SET_SHOW_SUGGEST_POPOVER';
|
||||||
|
|
||||||
|
export const TOGGLE_LINE_DISCUSSIONS = 'TOGGLE_LINE_DISCUSSIONS';
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
addContextLines,
|
addContextLines,
|
||||||
prepareDiffData,
|
prepareDiffData,
|
||||||
isDiscussionApplicableToLine,
|
isDiscussionApplicableToLine,
|
||||||
|
updateLineInFile,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import * as types from './mutation_types';
|
import * as types from './mutation_types';
|
||||||
|
|
||||||
|
@ -109,7 +110,7 @@ export default {
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode }) {
|
[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { discussion, diffPositionByLineCode, hash }) {
|
||||||
const { latestDiff } = state;
|
const { latestDiff } = state;
|
||||||
|
|
||||||
const discussionLineCode = discussion.line_code;
|
const discussionLineCode = discussion.line_code;
|
||||||
|
@ -130,13 +131,27 @@ export default {
|
||||||
: [],
|
: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const setDiscussionsExpanded = line => {
|
||||||
|
const isLineNoteTargeted = line.discussions.some(
|
||||||
|
disc => disc.notes && disc.notes.find(note => hash === `note_${note.id}`),
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...line,
|
||||||
|
discussionsExpanded:
|
||||||
|
line.discussions && line.discussions.length
|
||||||
|
? line.discussions.some(disc => !disc.resolved) || isLineNoteTargeted
|
||||||
|
: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
state.diffFiles = state.diffFiles.map(diffFile => {
|
state.diffFiles = state.diffFiles.map(diffFile => {
|
||||||
if (diffFile.file_hash === fileHash) {
|
if (diffFile.file_hash === fileHash) {
|
||||||
const file = { ...diffFile };
|
const file = { ...diffFile };
|
||||||
|
|
||||||
if (file.highlighted_diff_lines) {
|
if (file.highlighted_diff_lines) {
|
||||||
file.highlighted_diff_lines = file.highlighted_diff_lines.map(line =>
|
file.highlighted_diff_lines = file.highlighted_diff_lines.map(line =>
|
||||||
lineCheck(line) ? mapDiscussions(line) : line,
|
setDiscussionsExpanded(lineCheck(line) ? mapDiscussions(line) : line),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +163,10 @@ export default {
|
||||||
if (left || right) {
|
if (left || right) {
|
||||||
return {
|
return {
|
||||||
...line,
|
...line,
|
||||||
left: line.left ? mapDiscussions(line.left) : null,
|
left: line.left ? setDiscussionsExpanded(mapDiscussions(line.left)) : null,
|
||||||
right: line.right ? mapDiscussions(line.right, () => !left) : null,
|
right: line.right
|
||||||
|
? setDiscussionsExpanded(mapDiscussions(line.right, () => !left))
|
||||||
|
: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,32 +190,11 @@ export default {
|
||||||
[types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode }) {
|
[types.REMOVE_LINE_DISCUSSIONS_FOR_FILE](state, { fileHash, lineCode }) {
|
||||||
const selectedFile = state.diffFiles.find(f => f.file_hash === fileHash);
|
const selectedFile = state.diffFiles.find(f => f.file_hash === fileHash);
|
||||||
if (selectedFile) {
|
if (selectedFile) {
|
||||||
if (selectedFile.parallel_diff_lines) {
|
updateLineInFile(selectedFile, lineCode, line =>
|
||||||
const targetLine = selectedFile.parallel_diff_lines.find(
|
Object.assign(line, {
|
||||||
line =>
|
discussions: line.discussions.filter(discussion => discussion.notes.length),
|
||||||
(line.left && line.left.line_code === lineCode) ||
|
}),
|
||||||
(line.right && line.right.line_code === lineCode),
|
);
|
||||||
);
|
|
||||||
if (targetLine) {
|
|
||||||
const side = targetLine.left && targetLine.left.line_code === lineCode ? 'left' : 'right';
|
|
||||||
|
|
||||||
Object.assign(targetLine[side], {
|
|
||||||
discussions: targetLine[side].discussions.filter(discussion => discussion.notes.length),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedFile.highlighted_diff_lines) {
|
|
||||||
const targetInlineLine = selectedFile.highlighted_diff_lines.find(
|
|
||||||
line => line.line_code === lineCode,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (targetInlineLine) {
|
|
||||||
Object.assign(targetInlineLine, {
|
|
||||||
discussions: targetInlineLine.discussions.filter(discussion => discussion.notes.length),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedFile.discussions && selectedFile.discussions.length) {
|
if (selectedFile.discussions && selectedFile.discussions.length) {
|
||||||
selectedFile.discussions = selectedFile.discussions.filter(
|
selectedFile.discussions = selectedFile.discussions.filter(
|
||||||
|
@ -207,6 +203,15 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[types.TOGGLE_LINE_DISCUSSIONS](state, { fileHash, lineCode, expanded }) {
|
||||||
|
const selectedFile = state.diffFiles.find(f => f.file_hash === fileHash);
|
||||||
|
|
||||||
|
updateLineInFile(selectedFile, lineCode, line =>
|
||||||
|
Object.assign(line, { discussionsExpanded: expanded }),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
[types.TOGGLE_FOLDER_OPEN](state, path) {
|
[types.TOGGLE_FOLDER_OPEN](state, path) {
|
||||||
state.treeEntries[path].opened = !state.treeEntries[path].opened;
|
state.treeEntries[path].opened = !state.treeEntries[path].opened;
|
||||||
},
|
},
|
||||||
|
|
|
@ -454,3 +454,48 @@ export const convertExpandLines = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
export const idleCallback = cb => requestIdleCallback(cb);
|
export const idleCallback = cb => requestIdleCallback(cb);
|
||||||
|
|
||||||
|
export const updateLineInFile = (selectedFile, lineCode, updateFn) => {
|
||||||
|
if (selectedFile.parallel_diff_lines) {
|
||||||
|
const targetLine = selectedFile.parallel_diff_lines.find(
|
||||||
|
line =>
|
||||||
|
(line.left && line.left.line_code === lineCode) ||
|
||||||
|
(line.right && line.right.line_code === lineCode),
|
||||||
|
);
|
||||||
|
if (targetLine) {
|
||||||
|
const side = targetLine.left && targetLine.left.line_code === lineCode ? 'left' : 'right';
|
||||||
|
|
||||||
|
updateFn(targetLine[side]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selectedFile.highlighted_diff_lines) {
|
||||||
|
const targetInlineLine = selectedFile.highlighted_diff_lines.find(
|
||||||
|
line => line.line_code === lineCode,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (targetInlineLine) {
|
||||||
|
updateFn(targetInlineLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const allDiscussionWrappersExpanded = diff => {
|
||||||
|
const discussionsExpandedArray = [];
|
||||||
|
if (diff.parallel_diff_lines) {
|
||||||
|
diff.parallel_diff_lines.forEach(line => {
|
||||||
|
if (line.left && line.left.discussions.length) {
|
||||||
|
discussionsExpandedArray.push(line.left.discussionsExpanded);
|
||||||
|
}
|
||||||
|
if (line.right && line.right.discussions.length) {
|
||||||
|
discussionsExpandedArray.push(line.right.discussionsExpanded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (diff.highlighted_diff_lines) {
|
||||||
|
diff.parallel_diff_lines.forEach(line => {
|
||||||
|
if (line.discussions.length) {
|
||||||
|
discussionsExpandedArray.push(line.discussionsExpanded);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return discussionsExpandedArray.every(el => el);
|
||||||
|
};
|
||||||
|
|
|
@ -57,6 +57,7 @@ export default {
|
||||||
:user-callouts-path="userCalloutsPath"
|
:user-callouts-path="userCalloutsPath"
|
||||||
:lock-promotion-svg-path="lockPromotionSvgPath"
|
:lock-promotion-svg-path="lockPromotionSvgPath"
|
||||||
:help-canary-deployments-path="helpCanaryDeploymentsPath"
|
:help-canary-deployments-path="helpCanaryDeploymentsPath"
|
||||||
|
:deploy-boards-help-path="deployBoardsHelpPath"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<table-pagination
|
<table-pagination
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { s__, sprintf } from '~/locale';
|
import { __, s__, sprintf } from '~/locale';
|
||||||
import { formatTime } from '~/lib/utils/datetime_utility';
|
import { formatTime } from '~/lib/utils/datetime_utility';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
|
@ -28,7 +28,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
return 'Deploy to...';
|
return __('Deploy to...');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -80,7 +80,8 @@ export default {
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
>
|
>
|
||||||
<span>
|
<span>
|
||||||
<icon name="play" /> <icon name="chevron-down" />
|
<icon name="play" />
|
||||||
|
<icon name="chevron-down" />
|
||||||
<gl-loading-icon v-if="isLoading" />
|
<gl-loading-icon v-if="isLoading" />
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -94,9 +95,10 @@ export default {
|
||||||
class="js-manual-action-link no-btn btn d-flex align-items-center"
|
class="js-manual-action-link no-btn btn d-flex align-items-center"
|
||||||
@click="onClickAction(action)"
|
@click="onClickAction(action)"
|
||||||
>
|
>
|
||||||
<span class="flex-fill"> {{ action.name }} </span>
|
<span class="flex-fill">{{ action.name }}</span>
|
||||||
<span v-if="action.scheduledAt" class="text-secondary">
|
<span v-if="action.scheduledAt" class="text-secondary">
|
||||||
<icon name="clock" /> {{ remainingTime(action) }}
|
<icon name="clock" />
|
||||||
|
{{ remainingTime(action) }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { __, sprintf } from '~/locale';
|
||||||
import Timeago from 'timeago.js';
|
import Timeago from 'timeago.js';
|
||||||
import _ from 'underscore';
|
import _ from 'underscore';
|
||||||
import { GlTooltipDirective } from '@gitlab/ui';
|
import { GlTooltipDirective } from '@gitlab/ui';
|
||||||
|
@ -14,7 +15,6 @@ import MonitoringButtonComponent from './environment_monitoring.vue';
|
||||||
import CommitComponent from '../../vue_shared/components/commit.vue';
|
import CommitComponent from '../../vue_shared/components/commit.vue';
|
||||||
import eventHub from '../event_hub';
|
import eventHub from '../event_hub';
|
||||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||||
import { CLUSTER_TYPE } from '~/clusters/constants';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Environment Item Component
|
* Environment Item Component
|
||||||
|
@ -79,15 +79,6 @@ export default {
|
||||||
return this.model && this.model.is_protected;
|
return this.model && this.model.is_protected;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide group cluster features which are not currently implemented.
|
|
||||||
*
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
disableGroupClusterFeatures() {
|
|
||||||
return this.model && this.model.cluster_type === CLUSTER_TYPE.GROUP;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the environment can be stopped.
|
* Returns whether the environment can be stopped.
|
||||||
*
|
*
|
||||||
|
@ -172,7 +163,9 @@ export default {
|
||||||
this.model.last_deployment.user &&
|
this.model.last_deployment.user &&
|
||||||
this.model.last_deployment.user.username
|
this.model.last_deployment.user.username
|
||||||
) {
|
) {
|
||||||
return `${this.model.last_deployment.user.username}'s avatar'`;
|
return sprintf(__("%{username}'s avatar"), {
|
||||||
|
username: this.model.last_deployment.user.username,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
@ -293,6 +286,9 @@ export default {
|
||||||
* @returns {Boolean|Undefined}
|
* @returns {Boolean|Undefined}
|
||||||
*/
|
*/
|
||||||
isLastDeployment() {
|
isLastDeployment() {
|
||||||
|
// TODO: when the vue i18n rules are merged need to disable @gitlab/i18n/no-non-i18n-strings
|
||||||
|
// name: 'last?' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives
|
||||||
|
// Vue i18n ESLint rules issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/63560
|
||||||
return this.model && this.model.last_deployment && this.model.last_deployment['last?'];
|
return this.model && this.model.last_deployment && this.model.last_deployment['last?'];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -575,7 +571,6 @@ export default {
|
||||||
<terminal-button-component
|
<terminal-button-component
|
||||||
v-if="model && model.terminal_path"
|
v-if="model && model.terminal_path"
|
||||||
:terminal-path="model.terminal_path"
|
:terminal-path="model.terminal_path"
|
||||||
:disabled="disableGroupClusterFeatures"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<rollback-component
|
<rollback-component
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { __ } from '~/locale';
|
||||||
/**
|
/**
|
||||||
* Renders the Monitoring (Metrics) link in environments table.
|
* Renders the Monitoring (Metrics) link in environments table.
|
||||||
*/
|
*/
|
||||||
|
@ -21,7 +22,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
return 'Monitoring';
|
return __('Monitoring');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
import { GlTooltipDirective } from '@gitlab/ui';
|
import { GlTooltipDirective } from '@gitlab/ui';
|
||||||
import Icon from '~/vue_shared/components/icon.vue';
|
import Icon from '~/vue_shared/components/icon.vue';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -27,7 +28,7 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
title() {
|
title() {
|
||||||
return 'Terminal';
|
return __('Terminal');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,6 +43,11 @@ export default {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
deployBoardsHelpPath: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
@ -112,6 +117,7 @@ export default {
|
||||||
:user-callouts-path="userCalloutsPath"
|
:user-callouts-path="userCalloutsPath"
|
||||||
:lock-promotion-svg-path="lockPromotionSvgPath"
|
:lock-promotion-svg-path="lockPromotionSvgPath"
|
||||||
:help-canary-deployments-path="helpCanaryDeploymentsPath"
|
:help-canary-deployments-path="helpCanaryDeploymentsPath"
|
||||||
|
:deploy-boards-help-path="deployBoardsHelpPath"
|
||||||
@onChangePage="onChangePage"
|
@onChangePage="onChangePage"
|
||||||
>
|
>
|
||||||
<empty-state
|
<empty-state
|
||||||
|
|
|
@ -22,6 +22,11 @@ export default {
|
||||||
required: true,
|
required: true,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
deployBoardsHelpPath: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
canReadEnvironment: {
|
canReadEnvironment: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
|
@ -106,8 +111,10 @@ export default {
|
||||||
<div class="deploy-board-container">
|
<div class="deploy-board-container">
|
||||||
<deploy-board
|
<deploy-board
|
||||||
:deploy-board-data="model.deployBoardData"
|
:deploy-board-data="model.deployBoardData"
|
||||||
|
:deploy-boards-help-path="deployBoardsHelpPath"
|
||||||
:is-loading="model.isLoadingDeployBoard"
|
:is-loading="model.isLoadingDeployBoard"
|
||||||
:is-empty="model.isEmptyDeployBoard"
|
:is-empty="model.isEmptyDeployBoard"
|
||||||
|
:has-legacy-app-label="model.hasLegacyAppLabel"
|
||||||
:logs-path="model.logs_path"
|
:logs-path="model.logs_path"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue