Merge tag 'debian/12.10.0-1' into buster-fasttrack

gitlab Debian release 12.10.0-1
This commit is contained in:
Pirate Praveen 2020-04-23 18:22:19 +05:30
commit f50b703e68
4122 changed files with 371861 additions and 87845 deletions

View file

@ -38,3 +38,4 @@ exclude_paths:
- backups/
- coverage-javascript/
- plugins/
- file_hooks/

View file

@ -1,6 +1,6 @@
extends:
- '@gitlab'
- plugin:promise/recommended
- plugin:@gitlab/default
- plugin:@gitlab/i18n
- plugin:no-jquery/slim
- plugin:no-jquery/deprecated-3.4
globals:
@ -16,9 +16,6 @@ settings:
webpack:
config: './config/webpack.config.js'
rules:
"@gitlab/i18n/no-non-i18n-strings": error
"@gitlab/vue-i18n/no-bare-strings": error
"@gitlab/vue-i18n/no-bare-attribute-strings": error
import/no-commonjs: error
no-underscore-dangle:
- error
@ -54,4 +51,4 @@ overrides:
- files:
- '**/spec/**/*'
rules:
"@gitlab/i18n/no-non-i18n-strings": off
"@gitlab/require-i18n-strings": off

1
.gitignore vendored
View file

@ -34,6 +34,7 @@ eslint-report.html
/config/database*.yml
/config/gitlab.yml
/config/gitlab_ci.yml
/config/Gitlab.gitlab-license
/config/initializers/rack_attack.rb
/config/initializers/smtp_settings.rb
/config/initializers/relative_url.rb

View file

@ -1,4 +1,4 @@
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
stages:
- sync
@ -12,10 +12,14 @@ stages:
- post-qa
- pages
# always use `gitlab-org` runners
# always use `gitlab-org` runners, however
# in cases where jobs require Docker-in-Docker, the job
# definition must be extended with `.use-docker-in-docker`
default:
tags:
- gitlab-org
# All jobs are interruptible by default
interruptible: true
workflow:
rules:
@ -47,6 +51,7 @@ variables:
BUILD_ASSETS_IMAGE: "false"
ES_JAVA_OPTS: "-Xms256m -Xmx256m"
ELASTIC_URL: "http://elastic:changeme@elasticsearch:9200"
DOCKER_VERSION: "19.03.0"
include:
- local: .gitlab/ci/cache-repo.gitlab-ci.yml

View file

@ -3,15 +3,15 @@
- .default-retry
- .default-cache
- .default-before_script
- .use-pg9
- .use-pg11
stage: test
needs: ["setup-test-env"]
needs: ["setup-test-env pg11"]
variables:
FIXTURE_PATH: "db/fixtures/development"
SEED_CYCLE_ANALYTICS: "true"
SEED_PRODUCTIVITY_ANALYTICS: "true"
CYCLE_ANALYTICS_ISSUE_COUNT: 1
SIZE: 0 # number of external projects to fork, requires network connection
SIZE: 0 # number of external projects to fork, requires network connection
# SEED_NESTED_GROUPS: "false" # requires network connection
run-dev-fixtures:
@ -26,7 +26,7 @@ run-dev-fixtures-ee:
extends:
- .run-dev-fixtures
- .dev-fixtures:rules:ee-only
- .use-pg9-ee
- .use-pg11-ee
script:
- scripts/gitaly-test-spawn
- cp ee/db/fixtures/development/* $FIXTURE_PATH

View file

@ -66,9 +66,9 @@ graphql-reference-verify:
- .default-cache
- .default-before_script
- .docs:rules:graphql-reference-verify
- .use-pg9
- .use-pg11
stage: test
needs: ["setup-test-env"]
needs: ["setup-test-env pg11"]
script:
- bundle exec rake gitlab:graphql:check_docs
- bundle exec rake gitlab:graphql:check_schema

View file

@ -2,6 +2,8 @@
cache:
paths:
- vendor/ruby/
- public/assets/webpack/
- assets-hash.txt
- .yarn-cache/
- tmp/cache/assets/sprockets
- tmp/cache/babel-loader
@ -13,10 +15,9 @@
- .default-retry
- .default-before_script
- .assets-compile-cache
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-graphicsmagick-1.3.34-docker-19.03.1
- .use-docker-in-docker
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-graphicsmagick-1.3.34-docker-19.03.1
stage: prepare
services:
- docker:19.03.0-dind
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
@ -25,27 +26,30 @@
WEBPACK_REPORT: "true"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375
cache:
key: "assets-compile:production:vendor_ruby:.yarn-cache:tmp_cache_assets_sprockets:tmp_cache_webpack:v9"
key: "assets-compile:production:v1"
artifacts:
name: webpack-report
expire_in: 31d
paths:
- webpack-report/
- public/assets/
- assets-compile.log
# We consume these files in GitLab UI for integration tests:
# https://gitlab.com/gitlab-org/gitlab-ui/-/blob/e88493b3c855aea30bf60baee692a64606b0eb1e/.storybook/preview-head.pug#L1
- public/assets/application-*.css
- public/assets/application-*.css.gz
when: always
script:
- node --version
- retry yarn install --frozen-lockfile --production --cache-folder .yarn-cache --prefer-offline
- free -m
- retry bundle exec rake gitlab:assets:compile
- time bin/rake gitlab:assets:compile > assets-compile.log 2>&1
# TODO: Change the image tag to be the MD5 of assets files and skip image building if the image exists
# We'll also need to pass GITLAB_ASSETS_TAG to the trigerred omnibus-gitlab pipeline similarly to how we do it for trigerred CNG pipelines
# https://gitlab.com/gitlab-org/gitlab/issues/208389
- time scripts/build_assets_image
- scripts/clean-old-cached-assets
- rm -f /etc/apt/sources.list.d/google*.list # We don't need to update Chrome here
tags:
- gitlab-org
- docker
gitlab:assets:compile pull-push-cache:
extends:
@ -71,7 +75,7 @@ gitlab:assets:compile pull-cache:
- node --version
- retry yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline
- free -m
- retry bundle exec rake gitlab:assets:compile
- time bin/rake gitlab:assets:compile > assets-compile.log 2>&1
- scripts/clean-old-cached-assets
variables:
SETUP_DB: "false"
@ -79,12 +83,14 @@ gitlab:assets:compile pull-cache:
NODE_OPTIONS: --max_old_space_size=3584
WEBPACK_VENDOR_DLL: "true"
cache:
key: "assets-compile:v9"
key: "assets-compile:test:v1"
artifacts:
expire_in: 7d
paths:
- node_modules
- public/assets
- assets-compile.log
when: always
compile-assets pull-push-cache:
extends:
@ -100,7 +106,7 @@ compile-assets pull-push-cache as-if-foss:
- .as-if-foss
cache:
policy: pull-push
key: "assets-compile:v9:foss"
key: "assets-compile:test:as-if-foss:v1"
compile-assets pull-cache:
extends:
@ -116,20 +122,16 @@ compile-assets pull-cache as-if-foss:
- .as-if-foss
cache:
policy: pull
key: "assets-compile:v9:foss"
key: "assets-compile:test:as-if-foss:v1"
.frontend-fixtures-base:
extends:
- .default-retry
- .default-cache
- .default-before_script
- .use-pg9
- .use-pg11
stage: fixtures
needs:
- job: "setup-test-env"
artifacts: true
- job: "compile-assets pull-cache"
artifacts: true
needs: ["setup-test-env pg11", "compile-assets pull-cache"]
script:
- date
- scripts/gitaly-test-spawn
@ -152,7 +154,7 @@ frontend-fixtures:
frontend-fixtures-as-if-foss:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss
.frontend-job-base:
@ -195,7 +197,7 @@ karma:
karma-as-if-foss:
extends:
- .karma-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
@ -230,7 +232,7 @@ jest:
jest-as-if-foss:
extends:
- .jest-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .frontend:rules:default-frontend-jobs-no-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
cache:
@ -239,7 +241,7 @@ jest-as-if-foss:
coverage-frontend:
extends:
- .default-retry
- .frontend:rules:default-frontend-jobs
- .frontend:rules:default-frontend-jobs-no-foss
needs: ["jest"]
stage: post-test
before_script:
@ -258,7 +260,6 @@ coverage-frontend:
.qa-frontend-node:
extends:
- .default-retry
- .default-cache
- .frontend:rules:qa-frontend-node
stage: test
dependencies: []
@ -288,7 +289,7 @@ webpack-dev-server:
- .default-cache
- .frontend:rules:default-frontend-jobs
stage: test
needs: ["setup-test-env", "compile-assets pull-cache"]
needs: ["setup-test-env pg11", "compile-assets pull-cache"]
variables:
WEBPACK_MEMORY_TEST: "true"
WEBPACK_VENDOR_DLL: "true"

View file

@ -21,7 +21,7 @@
# Jobs that only need to pull cache
.default-cache:
cache:
key: "debian-stretch-ruby-2.6.5-pg9.6-node-12.x"
key: "debian-stretch-ruby-2.6.5-pg11-node-12.x"
paths:
- .go/pkg/mod
- vendor/ruby
@ -30,32 +30,40 @@
policy: pull
.use-pg9:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34"
services:
- name: postgres:9.6.17
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.5-pg9-node-12.x"
.use-pg10:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
services:
- name: postgres:10.12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.5-pg10-node-12.x"
.use-pg11:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
services:
- name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.5-pg11-node-12.x"
.use-pg9-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34"
services:
- name: postgres:9.6.17
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -63,9 +71,11 @@
- name: elasticsearch:6.4.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.5-pg9-node-12.x"
.use-pg10-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
services:
- name: postgres:10.12
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -73,9 +83,11 @@
- name: elasticsearch:6.4.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.5-pg10-node-12.x"
.use-pg11-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
services:
- name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -83,7 +95,29 @@
- name: elasticsearch:6.4.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.5-pg11-node-12.x"
# Pin kaniko to v0.16.0 due to https://github.com/GoogleContainerTools/kaniko/issues/1162
.use-kaniko:
image:
name: gcr.io/kaniko-project/executor:debug-v0.16.0
entrypoint: [""]
before_script:
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
.as-if-foss:
variables:
FOSS_ONLY: '1'
.use-docker-in-docker:
image: docker:${DOCKER_VERSION}
services:
- docker:${DOCKER_VERSION}-dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
tags:
# See https://gitlab.com/gitlab-com/www-gitlab-com/-/issues/7019 for tag descriptions
- gitlab-org-docker

View file

@ -8,9 +8,7 @@
memory-static:
extends: .only-code-memory-job-base
stage: test
needs:
- job: setup-test-env
artifacts: true
needs: ["setup-test-env pg11"]
variables:
SETUP_DB: "false"
script:
@ -38,13 +36,9 @@ memory-static:
memory-on-boot:
extends:
- .only-code-memory-job-base
- .use-pg10
- .use-pg11
stage: test
needs:
- job: setup-test-env
artifacts: true
- job: compile-assets pull-cache
artifacts: true
needs: ["setup-test-env pg11", "compile-assets pull-cache"]
variables:
NODE_ENV: "production"
RAILS_ENV: "production"

View file

@ -1,7 +1,6 @@
pages:
extends:
- .default-retry
- .default-cache
- .pages:rules
stage: pages
dependencies: ["rspec:coverage", "karma", "gitlab:assets:compile pull-cache"]

View file

@ -55,5 +55,9 @@ package-and-qa:
extends:
- .package-and-qa-base
- .qa:rules:package-and-qa
needs: ["build-qa-image", "gitlab:assets:compile pull-cache"]
needs:
- job: build-qa-image
artifacts: false
- job: gitlab:assets:compile pull-cache
artifacts: false
allow_failure: true

View file

@ -1,9 +1,5 @@
.rails:needs:setup-and-assets:
needs:
- job: setup-test-env
artifacts: true
- job: compile-assets pull-cache
artifacts: true
needs: ["setup-test-env pg11", "compile-assets pull-cache"]
.rails-job-base:
extends:
@ -12,12 +8,10 @@
- .default-before_script
####################
# ee and foss jobs #
setup-test-env:
# EE and FOSS jobs #
.base-setup-test-env:
extends:
- .rails-job-base
- .rails:rules:default-refs-code-backstage-qa
- .use-pg9
stage: prepare
script:
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
@ -31,6 +25,24 @@ setup-test-env:
cache:
policy: pull-push
setup-test-env pg11:
extends:
- .base-setup-test-env
- .rails:rules:default-refs-code-backstage-qa
- .use-pg11
setup-test-env pg10:
extends:
- .base-setup-test-env
- .rails:rules:master-refs-code-backstage
- .use-pg10
setup-test-env pg9:
extends:
- .base-setup-test-env
- .rails:rules:nightly-master-refs-code-backstage
- .use-pg9
static-analysis:
extends:
- .rails-job-base
@ -43,7 +55,7 @@ static-analysis:
script:
- scripts/static-analysis
cache:
key: "debian-stretch-ruby-2.6-pg9.6-rubocop"
key: "ruby-2.6.5-pg11-rubocop"
paths:
- vendor/ruby
- tmp/rubocop_cache
@ -63,13 +75,7 @@ downtime_check:
.rspec-base:
extends: .rails-job-base
stage: test
needs:
- job: setup-test-env
artifacts: true
- job: retrieve-tests-metadata
artifacts: true
- job: compile-assets pull-cache
artifacts: true
needs: ["setup-test-env pg11", "retrieve-tests-metadata", "compile-assets pull-cache"]
script:
- source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration"
@ -87,48 +93,37 @@ downtime_check:
reports:
junit: junit_rspec.xml
.rspec-base-quarantine:
extends:
- .rspec-base
- .use-pg9
variables:
RSPEC_OPTS: "--tag quarantine -- spec/"
script:
- source scripts/rspec_helpers.sh
- rspec_simple_job "${RSPEC_OPTS}"
allow_failure: true
.rspec-base-pg9:
.rspec-base-pg11:
extends:
- .rspec-base
- .rails:rules:ee-and-foss
- .use-pg9
- .use-pg11
.rspec-base-migration:
script:
- source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag level:migration"
rspec migration pg9:
rspec migration pg11:
extends:
- .rspec-base-pg9
- .rspec-base-pg11
- .rspec-base-migration
parallel: 5
rspec unit pg9:
extends: .rspec-base-pg9
rspec unit pg11:
extends: .rspec-base-pg11
parallel: 20
rspec integration pg9:
extends: .rspec-base-pg9
rspec integration pg11:
extends: .rspec-base-pg11
parallel: 8
rspec system pg9:
extends: .rspec-base-pg9
rspec system pg11:
extends: .rspec-base-pg11
parallel: 24
rspec fast_spec_helper:
extends: .rspec-base-pg9
extends: .rspec-base-pg11
script:
- bin/rspec spec/fast_spec_helper.rb
@ -136,11 +131,9 @@ rspec fast_spec_helper:
extends:
- .rails-job-base
- .rails:rules:ee-and-foss
- .use-pg9
- .use-pg11
stage: test
needs:
- job: setup-test-env
artifacts: true
needs: ["setup-test-env pg11"]
db:migrate:reset:
extends: .db-job-base
@ -204,8 +197,25 @@ gitlab:setup:
rspec:coverage:
extends:
- .rails-job-base
- .rails:rules:ee-and-foss
- .rails:rules:ee-only
stage: post-test
# We cannot use needs since it would mean needing 84 jobs (since most are parallelized)
# so we use `dependencies` here.
dependencies:
- setup-test-env pg11
- rspec migration pg11
- rspec unit pg11
- rspec integration pg11
- rspec system pg11
- rspec-ee migration pg11
- rspec-ee unit pg11
- rspec-ee integration pg11
- rspec-ee system pg11
- rspec-ee unit pg11 geo
- rspec-ee integration pg11 geo
- rspec-ee system pg11 geo
- memory-static
- memory-on-boot
variables:
SETUP_DB: "false"
cache:
@ -221,27 +231,23 @@ rspec:coverage:
- coverage/index.html
- coverage/assets/
- tmp/memory_test/
# ee and foss jobs #
# EE and FOSS jobs #
####################
####################
# master-only jobs #
rspec quarantine pg9:
extends:
- .rspec-base-quarantine
- .rails:rules:master-refs-code-backstage
.rspec-base-pg10:
extends:
- .rspec-base
- .rails:rules:master-refs-code-backstage
- .use-pg10
needs: ["setup-test-env pg10", "retrieve-tests-metadata", "compile-assets pull-cache"]
rspec migration pg10:
extends:
- .rspec-base-pg10
- .rspec-base-migration
parallel: 2
parallel: 5
rspec unit pg10:
extends: .rspec-base-pg10
@ -257,42 +263,42 @@ rspec system pg10:
# master-only jobs #
####################
############################
# nightly master-only jobs #
.rspec-base-pg11:
######################
# nightly-only jobs #
.rspec-base-pg9:
extends:
- .rspec-base
- .rails:rules:nightly-master-refs-code-backstage
- .use-pg11
- .use-pg9
needs: ["setup-test-env pg9", "retrieve-tests-metadata", "compile-assets pull-cache"]
rspec migration pg11:
rspec migration pg9:
extends:
- .rspec-base-pg11
- .rspec-base-pg9
- .rspec-base-migration
parallel: 2
parallel: 5
rspec unit pg11:
extends: .rspec-base-pg11
rspec unit pg9:
extends: .rspec-base-pg9
parallel: 20
rspec integration pg11:
extends: .rspec-base-pg11
rspec integration pg9:
extends: .rspec-base-pg9
parallel: 8
rspec system pg11:
extends: .rspec-base-pg11
rspec system pg9:
extends: .rspec-base-pg9
parallel: 24
# nightly master-only jobs #
############################
# nightly-only jobs #
#####################
#########################
# ee + master-only jobs #
rspec-ee quarantine pg9:
#######################
# EE master-only jobs #
.rspec-ee-base-pg10:
extends:
- .rspec-base-quarantine
- .rails:rules:master-refs-code-backstage-ee-only
variables:
RSPEC_OPTS: "--tag quarantine -- ee/spec/"
- .rspec-base-ee
- .use-pg10-ee
needs: ["setup-test-env pg10", "retrieve-tests-metadata", "compile-assets pull-cache"]
rspec-ee migration pg10:
extends:
@ -318,73 +324,62 @@ rspec-ee system pg10:
- .rspec-ee-base-pg10
- .rails:rules:master-refs-code-backstage
parallel: 6
# ee + master-only jobs #
#########################
# EE master-only jobs #
#######################
#################
# ee-only jobs #
################
# EE-only jobs #
.rspec-base-ee:
extends:
- .rspec-base
- .rails:rules:ee-only
.rspec-base-pg9-as-if-foss:
.rspec-base-pg11-as-if-foss:
extends:
- .rspec-base-ee
- .as-if-foss
- .use-pg9
needs:
- job: setup-test-env
artifacts: true
- job: retrieve-tests-metadata
artifacts: true
- job: compile-assets pull-cache as-if-foss
artifacts: true
- .use-pg11
needs: ["setup-test-env pg11", "retrieve-tests-metadata", "compile-assets pull-cache as-if-foss"]
.rspec-ee-base-pg9:
.rspec-ee-base-pg11:
extends:
- .rspec-base-ee
- .use-pg9-ee
- .use-pg11-ee
.rspec-ee-base-pg10:
rspec migration pg11-as-if-foss:
extends:
- .rspec-base-ee
- .use-pg10-ee
rspec migration pg9-as-if-foss:
extends:
- .rspec-base-pg9-as-if-foss
- .rspec-base-pg11-as-if-foss
- .rspec-base-migration
parallel: 5
rspec unit pg9-as-if-foss:
extends: .rspec-base-pg9-as-if-foss
rspec unit pg11-as-if-foss:
extends: .rspec-base-pg11-as-if-foss
parallel: 20
rspec integration pg9-as-if-foss:
extends: .rspec-base-pg9-as-if-foss
rspec integration pg11-as-if-foss:
extends: .rspec-base-pg11-as-if-foss
parallel: 8
rspec system pg9-as-if-foss:
extends: .rspec-base-pg9-as-if-foss
rspec system pg11-as-if-foss:
extends: .rspec-base-pg11-as-if-foss
parallel: 24
rspec-ee migration pg9:
rspec-ee migration pg11:
extends:
- .rspec-ee-base-pg9
- .rspec-ee-base-pg11
- .rspec-base-migration
parallel: 2
rspec-ee unit pg9:
extends: .rspec-ee-base-pg9
rspec-ee unit pg11:
extends: .rspec-ee-base-pg11
parallel: 10
rspec-ee integration pg9:
extends: .rspec-ee-base-pg9
rspec-ee integration pg11:
extends: .rspec-ee-base-pg11
parallel: 4
rspec-ee system pg9:
extends: .rspec-ee-base-pg9
rspec-ee system pg11:
extends: .rspec-ee-base-pg11
parallel: 6
.rspec-ee-base-geo:
@ -394,15 +389,39 @@ rspec-ee system pg9:
- scripts/prepare_postgres_fdw.sh
- rspec_paralellized_job "--tag ~quarantine --tag geo"
.rspec-ee-base-geo-pg11:
extends:
- .rspec-ee-base-geo
- .use-pg11-ee
rspec-ee unit pg11 geo:
extends: .rspec-ee-base-geo-pg11
parallel: 2
rspec-ee integration pg11 geo:
extends: .rspec-ee-base-geo-pg11
rspec-ee system pg11 geo:
extends: .rspec-ee-base-geo-pg11
db:rollback geo:
extends:
- db:rollback
- .rails:rules:ee-only
script:
- bundle exec rake geo:db:migrate VERSION=20170627195211
- bundle exec rake geo:db:migrate
# EE-only jobs #
################
########################
# EE nightly-only jobs #
.rspec-ee-base-geo-pg9:
extends:
- .rspec-ee-base-geo
- .use-pg9-ee
.rspec-ee-base-geo-pg10:
extends:
- .rspec-ee-base-geo
- .use-pg10-ee
- .rails:rules:nightly-master-refs-code-backstage-ee-only
needs: ["setup-test-env pg9", "retrieve-tests-metadata", "compile-assets pull-cache"]
rspec-ee unit pg9 geo:
extends: .rspec-ee-base-geo-pg9
@ -413,23 +432,5 @@ rspec-ee integration pg9 geo:
rspec-ee system pg9 geo:
extends: .rspec-ee-base-geo-pg9
rspec-ee unit pg10 geo:
extends: .rspec-ee-base-geo-pg10
parallel: 2
rspec-ee integration pg10 geo:
extends: .rspec-ee-base-geo-pg10
rspec-ee system pg10 geo:
extends: .rspec-ee-base-geo-pg10
db:rollback geo:
extends:
- db:rollback
- .rails:rules:ee-only
script:
- bundle exec rake geo:db:migrate VERSION=20170627195211
- bundle exec rake geo:db:migrate
# ee-only jobs #
################
# EE nightly-only jobs #
########################

View file

@ -11,15 +11,14 @@ code_quality:
extends:
- .default-retry
- .reports:rules:code_quality
- .use-docker-in-docker
stage: test
needs: []
image: docker:stable
allow_failure: true
services:
- docker:stable-dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
# emptying DOCKER_HOST so it can be detected properly on kubernetes executor
# with the script below
DOCKER_HOST: ""
CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.9"
script:
- |
@ -44,43 +43,59 @@ code_quality:
# We need to duplicate this job's definition because it seems it's impossible to
# override an included `only.refs`.
# See https://gitlab.com/gitlab-org/gitlab/issues/31371.
# Once https://gitlab.com/gitlab-org/gitlab/merge_requests/16487 will be deployed
# to GitLab.com, we should be able to use the template and set SAST_DISABLE_DIND: "true".
sast:
.sast:
extends:
- .default-retry
- .reports:rules:sast
- .use-docker-in-docker
stage: test
allow_failure: true
# `needs: []` starts the job immediately in the pipeline
# https://docs.gitlab.com/ee/ci/yaml/README.html#needs
needs: []
allow_failure: true
artifacts:
paths:
- gl-sast-report.json # GitLab-specific
reports:
sast: gl-sast-report.json
expire_in: 1 week # GitLab-specific
image: docker:stable
variables:
# emptying DOCKER_HOST so it can be detected properly on kubernetes executor
# with the script below
DOCKER_HOST: ""
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
SAST_ANALYZER_IMAGE_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
SAST_ANALYZER_IMAGE_TAG: 2
SAST_BRAKEMAN_LEVEL: 2 # GitLab-specific
SAST_EXCLUDED_PATHS: qa,spec,doc,ee/spec # GitLab-specific
services:
- docker:stable-dind
script:
- export SAST_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
- |
if ! docker info &>/dev/null; then
if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
export DOCKER_HOST='tcp://localhost:2375'
fi
fi
- |
ENVS=`printenv | grep -vE '^(DOCKER_|CI|GITLAB_|FF_|HOME|PWD|OLDPWD|PATH|SHLVL|HOSTNAME)' | sed -n '/^[^\t]/s/=.*//p' | sed '/^$/d' | sed 's/^/-e /g' | tr '\n' ' '`
docker run "$ENVS" \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code
- /analyzer run
brakeman-sast:
extends: .sast
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/brakeman:$SAST_ANALYZER_IMAGE_TAG"
eslint-sast:
extends: .sast
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/eslint:$SAST_ANALYZER_IMAGE_TAG"
kubesec-sast:
extends: .sast
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/kubesec:$SAST_ANALYZER_IMAGE_TAG"
nodejs-scan-sast:
extends: .sast
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/nodejs-scan:$SAST_ANALYZER_IMAGE_TAG"
secrets-sast:
extends: .sast
image:
name: "$SAST_ANALYZER_IMAGE_PREFIX/secrets:$SAST_ANALYZER_IMAGE_TAG"
# We need to duplicate this job's definition because it seems it's impossible to
# override an included `only.refs`.
@ -89,16 +104,15 @@ dependency_scanning:
extends:
- .default-retry
- .reports:rules:dependency_scanning
- .use-docker-in-docker
stage: test
needs: []
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
# emptying DOCKER_HOST so it can be detected properly on kubernetes executor
# with the script below
DOCKER_HOST: ""
DS_EXCLUDED_PATHS: "qa/qa/ee/fixtures/secure_premade_reports,spec,ee/spec" # GitLab-specific
allow_failure: true
services:
- docker:stable-dind
script:
- export DS_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
- |
@ -157,9 +171,9 @@ dast:
extends:
- .default-retry
- .reports:rules:dast
needs:
- job: review-deploy
artifacts: true
# This is needed so that manual jobs with needs don't block the pipeline.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/199979.
dependencies: ["review-deploy"]
stage: qa # GitLab-specific
image:
name: "registry.gitlab.com/gitlab-org/security-products/dast:$DAST_VERSION"

View file

@ -1,37 +1,27 @@
.review-docker:
extends:
- .default-retry
- .use-docker-in-docker
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.6
services:
- docker:19.03.0-dind
tags:
- gitlab-org
- docker
variables:
DOCKER_DRIVER: overlay2
DOCKER_HOST: tcp://docker:2375
GITLAB_EDITION: "ce"
build-qa-image:
extends:
- .review-docker
- .review:rules:mr-and-schedule
- .use-kaniko
- .default-retry
- .review:rules:mr-and-schedule-auto
stage: prepare
script:
- '[[ ! -d "ee/" ]] || export GITLAB_EDITION="ee"'
- export QA_MASTER_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab/gitlab-${GITLAB_EDITION}-qa:master"
- export QA_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab/gitlab-${GITLAB_EDITION}-qa:${CI_COMMIT_REF_SLUG}"
- echo "${CI_JOB_TOKEN}" | docker login --username gitlab-ci-token --password-stdin ${CI_REGISTRY}
- time docker pull "${QA_MASTER_IMAGE}"
- time docker build --cache-from "${QA_MASTER_IMAGE}" --tag ${QA_IMAGE} --file ./qa/Dockerfile ./
- time docker push ${QA_IMAGE}
- export QA_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-${GITLAB_EDITION}-qa:${CI_COMMIT_REF_SLUG}"
- /kaniko/executor --context=${CI_PROJECT_DIR} --dockerfile=${CI_PROJECT_DIR}/qa/Dockerfile --destination=${QA_IMAGE} --cache=true
review-cleanup:
.review-cleanup-base:
extends:
- .default-retry
- .review:rules:review-cleanup
stage: prepare
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
allow_failure: true
environment:
name: review/auto-cleanup
@ -42,10 +32,39 @@ review-cleanup:
script:
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
review-cleanup:
extends:
- .review-cleanup-base
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
review-cleanup-helm3:
extends:
- .review-cleanup-base
variables:
HELM_3: 1
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-helm3-kubectl1.14
review-gcp-cleanup:
extends:
- .review:rules:review-gcp-cleanup
stage: prepare
image: gcr.io/google.com/cloudsdktool/cloud-sdk:latest
allow_failure: true
environment:
name: review/auto-gcp-cleanup
action: stop
before_script:
- gcloud auth activate-service-account --key-file=$REVIEW_APPS_GCP_CREDENTIALS
- gcloud config set project $REVIEW_APPS_GCP_PROJECT
- apt-get install -y jq
- source scripts/review_apps/gcp_cleanup.sh
script:
- gcp_cleanup
review-build-cng:
extends:
- .default-retry
- .review:rules:mr-and-schedule
- .review:rules:mr-and-schedule-auto-if-frontend-manual-otherwise
image: ruby:2.6-alpine
stage: review-prepare
before_script:
@ -57,15 +76,18 @@ review-build-cng:
artifacts: false
script:
- BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng
# When the job is manual, review-deploy is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them.
- '[ -z $CI_JOB_MANUAL ] || play_job "review-deploy"'
.review-workflow-base:
extends:
- .default-retry
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-helm3-kubectl1.14
variables:
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
GITLAB_HELM_CHART_REF: "v2.6.8"
GITLAB_HELM_CHART_REF: "v3.2.2"
GITLAB_EDITION: "ce"
environment:
name: review/${CI_COMMIT_REF_NAME}
@ -76,11 +98,9 @@ review-build-cng:
review-deploy:
extends:
- .review-workflow-base
- .review:rules:mr-and-schedule
- .review:rules:mr-and-schedule-auto-if-frontend-manual-otherwise
stage: review
needs:
- job: review-build-cng
artifacts: false
dependencies: []
resource_group: "review/${CI_COMMIT_REF_NAME}"
allow_failure: true
before_script:
@ -95,11 +115,14 @@ review-deploy:
script:
- check_kube_domain
- ensure_namespace
- install_tiller
- install_external_dns
- download_chart
- date
- deploy || (display_deployment_debug && exit 1)
# When the job is manual, review-qa-smoke is also manual and we don't want people
# to have to manually start the jobs in sequence, so we do it for them.
- '[ -z $CI_JOB_MANUAL ] || play_job "review-qa-smoke"'
- '[ -z $CI_JOB_MANUAL ] || play_job "review-performance"'
artifacts:
paths: [environment_url.txt]
expire_in: 2 days
@ -127,6 +150,7 @@ review-stop-failed-deployment:
stage: prepare
script:
- delete_failed_release
- delete_helm2_release
review-stop:
extends:
@ -140,9 +164,9 @@ review-stop:
.review-qa-base:
extends: .review-docker
stage: qa
needs:
- job: review-deploy
artifacts: true
# This is needed so that manual jobs with needs don't block the pipeline.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/199979.
dependencies: ["review-deploy"]
allow_failure: true
variables:
QA_ARTIFACTS_DIR: "${CI_PROJECT_DIR}/qa"
@ -156,7 +180,7 @@ review-stop:
EE_LICENSE: "${REVIEW_APPS_EE_LICENSE}"
before_script:
- '[[ ! -d "ee/" ]] || export GITLAB_EDITION="ee"'
- export QA_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab/gitlab-${GITLAB_EDITION}-qa:${CI_COMMIT_REF_SLUG}"
- export QA_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-${GITLAB_EDITION}-qa:${CI_COMMIT_REF_SLUG}"
- export CI_ENVIRONMENT_URL="$(cat environment_url.txt)"
- echo "${CI_ENVIRONMENT_URL}"
- echo "${QA_IMAGE}"
@ -172,7 +196,7 @@ review-stop:
review-qa-smoke:
extends:
- .review-qa-base
- .review:rules:mr-only-auto
- .review:rules:mr-only-auto-if-frontend-manual-otherwise
script:
- gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
@ -188,12 +212,15 @@ review-qa-all:
review-performance:
extends:
- .review-docker
- .review:rules:mr-and-schedule
- .default-retry
- .review:rules:mr-and-schedule-auto-if-frontend-manual-otherwise
image:
name: sitespeedio/sitespeed.io:6.3.1
entrypoint: [""]
stage: qa
needs:
- job: review-deploy
artifacts: true
# This is needed so that manual jobs with needs don't block the pipeline.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/199979.
dependencies: ["review-deploy"]
allow_failure: true
before_script:
- export CI_ENVIRONMENT_URL="$(cat environment_url.txt)"
@ -202,7 +229,7 @@ review-performance:
- wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
- mkdir -p sitespeed-results
script:
- docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "${CI_ENVIRONMENT_URL}"
- /start.sh --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "${CI_ENVIRONMENT_URL}"
after_script:
- mv sitespeed-results/data/performance.json performance.json
artifacts:

View file

@ -16,6 +16,9 @@
.if-master-refs: &if-master-refs
if: '$CI_COMMIT_REF_NAME == "master"'
.if-auto-deploy-branches: &if-auto-deploy-branches
if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
.if-master-or-tag: &if-master-or-tag
if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_TAG'
@ -57,6 +60,17 @@
- "doc/**/*"
- ".markdownlint.json"
.frontend-dependency-patterns: &frontend-dependency-patterns
- "{package.json,yarn.lock}"
.frontend-patterns: &frontend-patterns
- "{package.json,yarn.lock}"
- "{babel.config,jest.config}.js"
- ".csscomb.json"
- "Dockerfile.assets"
- "vendor/assets/**/*"
- "{,ee/}{app/assets,app/helpers,app/presenters,app/views,locale,public,symbol}/**/*"
.backstage-patterns: &backstage-patterns
- "Dangerfile"
- "danger/**/*"
@ -66,39 +80,38 @@
- "doc/README.md" # Some RSpec test rely on this file
.code-patterns: &code-patterns
- "{package.json,yarn.lock}"
- "{babel.config,jest.config}.js"
- ".csscomb.json"
- "Dockerfile.assets"
- "vendor/assets/**/*"
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
.frontend-dependency-patterns: &frontend-dependency-patterns
- "{package.json,yarn.lock}"
.qa-patterns: &qa-patterns
- ".dockerignore"
- "qa/**/*"
.code-backstage-patterns: &code-backstage-patterns
- "{package.json,yarn.lock}"
- "{babel.config,jest.config}.js"
- ".csscomb.json"
- "Dockerfile.assets"
- "vendor/assets/**/*"
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes
@ -110,17 +123,18 @@
- "doc/README.md" # Some RSpec test rely on this file
.code-qa-patterns: &code-qa-patterns
- "{package.json,yarn.lock}"
- "{babel.config,jest.config}.js"
- ".csscomb.json"
- "Dockerfile.assets"
- "vendor/assets/**/*"
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# QA changes
@ -128,17 +142,18 @@
- "qa/**/*"
.code-backstage-qa-patterns: &code-backstage-qa-patterns
- "{package.json,yarn.lock}"
- "{babel.config,jest.config}.js"
- ".csscomb.json"
- "Dockerfile.assets"
- "vendor/assets/**/*"
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes
@ -261,7 +276,7 @@
changes: *code-backstage-patterns
when: on_success
.frontend:rules:default-frontend-jobs-as-if-foss:
.frontend:rules:default-frontend-jobs-no-foss:
rules:
- <<: *if-not-ee
when: never
@ -272,6 +287,7 @@
.frontend:rules:qa-frontend-node:
rules:
- <<: *if-master-refs
changes: *frontend-dependency-patterns
when: on_success
- <<: *if-merge-request
changes: *frontend-dependency-patterns
@ -291,6 +307,8 @@
###############
.pages:rules:
rules:
- <<: *if-not-ee
when: never
- <<: *if-dot-com-gitlab-org-master
changes: *code-backstage-qa-patterns
when: on_success
@ -343,20 +361,23 @@
- <<: *if-master-refs
changes: *code-backstage-patterns
when: on_success
.rails:rules:master-refs-code-backstage-ee-only:
rules:
- <<: *if-not-ee
when: never
- <<: *if-master-refs
changes: *code-backstage-patterns
when: on_success
- changes: [".gitlab/ci/rails.gitlab-ci.yml"]
.rails:rules:nightly-master-refs-code-backstage:
rules:
- <<: *if-nightly-master-schedule
changes: *code-backstage-patterns
when: on_success
- changes: [".gitlab/ci/rails.gitlab-ci.yml"]
.rails:rules:nightly-master-refs-code-backstage-ee-only:
rules:
- <<: *if-not-ee
when: never
- <<: *if-nightly-master-schedule
changes: *code-backstage-patterns
when: on_success
- changes: [".gitlab/ci/rails.gitlab-ci.yml"]
.rails:rules:ee-only:
rules:
@ -414,8 +435,12 @@
rules:
- if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/'
when: never
- <<: *if-dot-com-gitlab-org-merge-request
changes: *frontend-patterns
when: on_success
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-qa-patterns
when: manual
.reports:schedule-dast:
rules:
@ -426,7 +451,7 @@
################
# Review rules #
################
.review:rules:mr-and-schedule:
.review:rules:mr-and-schedule-auto:
rules:
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-qa-patterns
@ -434,12 +459,33 @@
- <<: *if-dot-com-gitlab-org-schedule
when: on_success
.review:rules:mr-and-schedule-auto-if-frontend-manual-otherwise:
rules:
- <<: *if-dot-com-gitlab-org-merge-request
changes: *frontend-patterns
when: on_success
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-qa-patterns
when: manual
allow_failure: true
- <<: *if-dot-com-gitlab-org-schedule
when: on_success
.review:rules:mr-only-auto:
rules:
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-qa-patterns
when: on_success
.review:rules:mr-only-auto-if-frontend-manual-otherwise:
rules:
- <<: *if-dot-com-gitlab-org-merge-request
changes: *frontend-patterns
when: on_success
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-qa-patterns
when: manual
.review:rules:mr-only-manual:
rules:
- <<: *if-dot-com-gitlab-org-merge-request
@ -454,6 +500,14 @@
- <<: *if-dot-com-gitlab-org-schedule
when: on_success
.review:rules:review-gcp-cleanup:
rules:
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-qa-patterns
when: manual
- <<: *if-dot-com-gitlab-org-schedule
when: on_success
.review:rules:danger:
rules:
- if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID'
@ -470,6 +524,14 @@
changes: *code-backstage-qa-patterns
when: on_success
.setup:rules:dont-interrupt-me:
rules:
- <<: *if-master-or-tag
when: on_success
- <<: *if-auto-deploy-branches
when: on_success
- when: manual
.setup:rules:gitlab_git_test:
rules:
- <<: *if-default-refs

View file

@ -7,9 +7,7 @@ cache gems:
- .default-before_script
- .setup:rules:cache-gems
stage: test
needs:
- job: setup-test-env
artifacts: true
needs: ["setup-test-env pg11"]
variables:
SETUP_DB: "false"
script:
@ -23,6 +21,17 @@ cache gems:
- .default-retry
needs: []
dont-interrupt-me:
extends: .setup:rules:dont-interrupt-me
stage: sync
image: alpine:edge
interruptible: false
allow_failure: true
variables:
GIT_STRATEGY: none
script:
- echo "This jobs makes sure this pipeline won't be interrupted! See https://docs.gitlab.com/ee/ci/yaml/#interruptible."
gitlab_git_test:
extends:
- .minimal-job

View file

@ -45,6 +45,7 @@ logs, and code as it's tough to read otherwise.)
<details>
<summary>Expand for output related to GitLab environment info</summary>
<pre>
(For installations with omnibus-gitlab package run and paste the output of:

View file

@ -37,7 +37,7 @@ If applicable, any groups/projects that are happy to have this feature turned on
- [ ] Coordinate a time to enable the flag with `#production` and `#g_delivery` on slack.
- [ ] Announce on the issue an estimated time this will be enabled on GitLab.com
- [ ] Enable on GitLab.com by running chatops command in `#production`
- [ ] Cross post chatops slack command to `#support_gitlab-com` and in your team channel
- [ ] Cross post chatops slack command to `#support_gitlab-com` ([more guidance when this is necessary in the dev docs](https://docs.gitlab.com/ee/development/feature_flags/controls.html#where-to-run-commands)) and in your team channel
- [ ] Announce on the issue that the flag has been enabled
- [ ] Remove feature flag and add changelog entry
- [ ] After the flag removal is deployed, [clean up the feature flag](https://docs.gitlab.com/ee/development/feature_flags/controls.html#cleaning-up) by running chatops command in `#production` channel

View file

@ -1,11 +1,15 @@
<!-- The first three sections: "Problem to solve", "Intended users" and "Proposal", are strongly recommended, while the rest of the sections can be filled out during the problem validation or breakdown phase. However, keep in mind that providing complete and relevant information early helps our product team validate the problem and start working on a solution. -->
### Problem to solve
<!-- What problem do we solve? -->
<!-- What problem do we solve? Try to define the who/what/why of the opportunity as a user story. For example, "As a (who), I want (what), so I can (why/value)." -->
### Intended users
<!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
Personas are described at https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/
* [Rachel (Release Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#rachel-release-manager)
* [Parker (Product Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#parker-product-manager)
* [Delaney (Development Team Lead)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead)
@ -16,13 +20,11 @@
* [Sam (Security Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sam-security-analyst)
* [Dana (Data Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#dana-data-analyst)
* [Simone (Software Engineer in Test)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#simone-software-engineer-in-test)
* [Allison (Application Ops)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#allison-application-ops)
Personas are described at https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/ -->
* [Allison (Application Ops)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#allison-application-ops) -->
### Further details
<!-- Include use cases, benefits, and/or goals (contributes to our vision?) -->
<!-- Include use cases, benefits, goals, or any other details that will help us understand the problem better. -->
### Proposal
@ -35,14 +37,15 @@ Personas are described at https://about.gitlab.com/handbook/marketing/product-ma
### Documentation
<!-- See the Feature Change Documentation Workflow https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html
Add all known Documentation Requirements here, per https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#documentation-requirements
If this feature requires changing permissions, this document https://docs.gitlab.com/ee/user/permissions.html must be updated accordingly. -->
* Add all known Documentation Requirements in this section. See https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#documentation-requirements
* If this feature requires changing permissions, update the permissions document. See https://docs.gitlab.com/ee/user/permissions.html -->
### Availability & Testing
<!-- This section needs to be retained and filled in during the workflow planning breakdown phase of this feature proposal, if not earlier.
What risks does this change pose to our availability? How might it affect the quality of the product? What additional test coverage or changes to tests will be needed? Will it require cross-browser testing?
What risks does this change pose to our availability? How might it affect the quality of the product? What additional test coverage or changes to tests will be needed? Will it require cross-browser testing?
Please list the test areas (unit, integration and end-to-end) that needs to be added or updated to ensure that this feature will work as intended. Please use the list below as guidance.
* Unit test changes
@ -57,7 +60,8 @@ See the test engineering planning process and reach out to your counterpart Soft
### What is the type of buyer?
<!-- Which leads to: in which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#four-tiers -->
<!-- What is the buyer persona for this feature? See https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/buyer-persona/
In which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#four-tiers -->
### Is this a cross-stage feature?

View file

@ -0,0 +1,67 @@
# Project Name | Migration Tracker
<!-- Please edit this header with your project / organization's name. -->
## Background
<!--
Please add information here about why you're planning on migrating. Include any initial announcements that have been made about the decision or status.
-->
### Goals
<!-- What are some of the goals of your migration to GitLab? Delete this section if you don't want to enumerate goals. -->
## Quick Facts
<!-- Please complete as many items in this list as possible. If you're not sure yet, add "TBD" (To be Decided) or "Unknown" -->
* **Timeline.** -
* **Product.** - GitLab Gold/Ultimate or Commnunity Edition
* **Project's License.** What kind of OSI-approved license does your project use?
## Current Tooling and Replacements
<!--
Please fill in the table to give an overview of your current tooling. Here's a description of what to include in each column:
- Tool: which tool or platform you are currently using
- Feature: which particular feature you are using in that tool or platform
- GitLab feature: equivalent GitLab feature (the GitLab team can help fill this in, as well as the info in the next column)
- GitLab edition: in which GitLab edition (CE or EE) is this feature available?
Here's an example of a replacements overview from one of the projects which migrated to GitLab: https://gitlab.com/gitlab-org/gitlab/-/issues/25657#gitlab-replacements
-->
| Tool | Feature | GitLab feature | GitLab edition |
| --- | --- | --- | --- |
| | | | |
## Collaborators
<!-- Please add names of collaborators in the format: Name, Title, Role (what will you be helping to do, or how should you be involved), GitLab username -->
## Related Issues
<!-- Add any related issues that are important for your project by adding the title of the issue and a link to it (preferably as an embedded link). You will probably keep editing this section as the migration progresses, so don't worry if it's mostly blank for now.
Here is an example of what this list might look like once populated: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/55039#outstanding-issues
-->
### Blockers
* [ ] ADD_LINK_TO_ISSUE_HERE
### Urgent
* [ ]
### Important but not urgent
* [ ]
### Nice to have
* [ ]
------
/label ~"Open Source" ~movingtogitlab
/cc @nuritzi

View file

@ -25,13 +25,6 @@ Please provide pros/cons and a weight estimate for each solution.
- [ ] All potential solutions are listed.
- [ ] A solution has been chosen for the first iteration: `PUT THE CHOSEN SOLUTION HERE`
## Who and when will the solution be implemented?
<!--
For history reason, please list the person that will implement the solution and
the planned milestone/date.
-->
## Verify that the solution has improved the situation
<!--

View file

@ -31,6 +31,15 @@ Attach the screenshot and HTML snapshot of the page from the job's artifacts:
1. Open the `gitlab-qa-run-2020-*/gitlab-{ce,ee}-qa-*/{,ee}/{api,browser_ui}/<path to failed test>` folder.
1. Select the `.png` and `.html` files that appears in the job logs (look for `HTML screenshot: /path/to/html/page.html` / `Image screenshot: `/path/to/html/page.png`).
1. Drag and drop them here.
Note: You don't need to include a screenshot if the information it contains can be included as text. Include the text instead.
E.g., error 500/404, "Retry later" errors, etc.
If you include multiple screenshots it can be helpful to hide all but the first in a details/summary element, to avoid excessive scrolling:
<details><summary>Expand for screenshot</summary>
drag and drop the screenshot here
</details>
-->
### Possible fixes

View file

@ -21,7 +21,7 @@ Set the title to: `Description of the original issue`
- [ ] Create a merge request targeting `master` on `gitlab.com/gitlab-org/security` and use the [Security Release merge request template].
- [ ] Follow the same [code review process]: Assign to a reviewer, then to a maintainer.
After your merge request has being approved according to our [approval guidelines], you're ready to prepare the backports
After your merge request has been approved according to our [approval guidelines], you're ready to prepare the backports
## Backports

View file

@ -0,0 +1,32 @@
<!--Please answer the below questions to the best of your ability.-->
#### What's this issue all about? (Background and context)
#### What hypotheses and/or assumptions do you have?
#### What questions are you trying to answer?
#### What research methodology do you intend to use?
<!-- What needs to be answered to move work forward? If you have a completed Opportunity Canvas include a link.-->
#### What persona, persona segment, or customer type experiences the problem most acutely?
#### What business decisions will be made based on this information?
#### What, if any, relevant prior research already exists?
<!-- Have a look at our UXR_Insights repo: https://gitlab.com/gitlab-org/uxr_insights -->
#### Who will be leading the research?
#### What timescales do you have in mind for the research?
#### Relevant links (problem validation issue, design issue, script, prototype, notes, etc.)
<!-- #### TODO Checklist
Consider adding a checklist in order to keep track of what stage the research is up to. Some possible checklist templates are here:
https://about.gitlab.com/handbook/engineering/ux/ux-research-training/templates-resources-for-research-studies/#checklists
-->
/label ~"workflow::solution validation"

View file

@ -8,7 +8,6 @@ Please describe the proposal and add a link to the source (for example, http://w
- [ ] Make sure this MR enables a static analysis check rule for new usage but
ignores current offenses
- [ ] Create a follow-up issue to fix the current offenses as a separate iteration: ISSUE_LINK
- [ ] Mention this proposal in the relevant Slack channels (e.g. `#development`, `#backend`, `#frontend`)
- [ ] If there is a choice to make between two potential styles, set up an emoji vote in the MR:
- CHOICE_A: :a:
@ -17,6 +16,7 @@ Please describe the proposal and add a link to the source (for example, http://w
- [ ] The MR doesn't have significant objections, and is getting a majority of :+1: vs :-1: (remember that [we don't need to reach a consensus](https://about.gitlab.com/handbook/values/#collaboration-is-not-consensus))
- [ ] (If applicable) One style is getting a majority of vote (compared to the other choice)
- [ ] (If applicable) Update the MR with the chosen style
- [ ] Create a follow-up issue to fix the current offenses as a separate iteration: ISSUE_LINK
- [ ] Follow the [review process](https://docs.gitlab.com/ee/development/code_review.html) as usual
- [ ] Once approved and merged by a maintainer, mention it again:
- [ ] In the relevant Slack channels (e.g. `#development`, `#backend`, `#frontend`)

View file

@ -27,7 +27,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
## Maintainer checklist
- [ ] Correct milestone is applied and the title is matching across all backports
- [ ] Assigned to `@gitlab-release-tools-bot` with passing CI pipelines
- [ ] Assigned to `@gitlab-release-tools-bot` with passing CI pipelines and **when all backports including the MR targeting master are ready.**
/label ~security

View file

@ -409,7 +409,6 @@ linters:
- 'ee/app/views/projects/push_rules/_index.html.haml'
- 'ee/app/views/projects/services/gitlab_slack_application/_help.html.haml'
- 'ee/app/views/projects/services/gitlab_slack_application/_slack_integration_form.html.haml'
- 'ee/app/views/projects/services/prometheus/_metrics.html.haml'
- 'ee/app/views/projects/settings/slacks/edit.html.haml'
- 'ee/app/views/shared/_additional_email_text.html.haml'
- 'ee/app/views/shared/_mirror_update_button.html.haml'

View file

@ -22,7 +22,6 @@
"style": "---"
},
"no-emphasis-as-heading": false,
"fenced-code-language": false,
"first-line-h1": false,
"code-block-style": {
"style": "fenced"
@ -43,6 +42,7 @@
"CentOS",
"Consul",
"Debian",
"DevOps",
"Elasticsearch",
"Facebook",
"Git LFS",
@ -84,7 +84,9 @@
"markdownlint",
"Mattermost",
"Microsoft",
"Minikube",
"MinIO",
"ModSecurity",
"NGINX Ingress",
"NGINX",
"OAuth",
@ -98,9 +100,11 @@
"Prometheus",
"Puma",
"Python",
"Rake",
"Redis",
"Redmine",
"reCAPTCHA",
"Ruby",
"runit",
"Salesforce",
"SAML",
@ -123,6 +127,5 @@
"YouTrack"
],
"code_blocks": false
},
"code-fence-style": false
}
}

View file

@ -15,8 +15,8 @@
#
# Uncomment the following lines to make the configuration take effect.
# Make sure to run `cd tooling/overcommit && make && cd -`
gemfile: 'tooling/overcommit/gems.rb'
# Make sure to run `make -C tooling/overcommit`
gemfile: 'tooling/overcommit/Gemfile'
PostCheckout:
BundleInstall:
@ -28,7 +28,9 @@ PreCommit:
EsLint:
enabled: true
# https://github.com/sds/overcommit/issues/338
command: './node_modules/eslint/bin/eslint.js'
required_executable: 'yarn'
command: ['yarn', 'eslint']
flags: []
HamlLint:
enabled: true
MergeConflicts:

View file

@ -7,6 +7,10 @@ require:
- ./rubocop/rubocop
- rubocop-rspec
inherit_mode:
merge:
- Include
AllCops:
TargetRubyVersion: 2.6
TargetRailsVersion: 5.0
@ -21,8 +25,15 @@ AllCops:
- 'generator_templates/**/*'
- 'builds/**/*'
- 'plugins/**/*'
- 'file_hooks/**/*'
CacheRootDirectory: tmp
Cop/StaticTranslationDefinition:
Enabled: true
Exclude:
- 'spec/**/*'
- 'ee/spec/**/*'
# This cop checks whether some constant value isn't a
# mutable literal (e.g. array or hash).
Style/MutableConstant:
@ -161,6 +172,14 @@ Rails/ApplicationRecord:
- ee/db/**/*.rb
- ee/spec/**/*.rb
Rails/FindBy:
Enabled: true
Include:
- 'ee/app/**/*.rb'
- 'ee/lib/**/*.rb'
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
# GitLab ###################################################################
Gitlab/ModuleWithInstanceVariables:
@ -200,6 +219,14 @@ GitlabSecurity/PublicSend:
- 'ee/lib/**/*.rake'
- 'ee/spec/**/*'
Gitlab/DuplicateSpecLocation:
Exclude:
- ee/spec/helpers/auth_helper_spec.rb
- ee/spec/lib/gitlab/gl_repository_spec.rb
- ee/spec/services/merge_requests/refresh_service_spec.rb
- ee/spec/helpers/ee/auth_helper_spec.rb
- ee/spec/services/ee/merge_requests/refresh_service_spec.rb
Cop/InjectEnterpriseEditionModule:
Enabled: true
Exclude:
@ -299,7 +326,10 @@ RSpec/ImplicitSubject:
Enabled: false
RSpec/LeakyConstantDeclaration:
Enabled: false
Enabled: true
Exclude:
- 'spec/**/*.rb'
- 'qa/spec/**/*.rb'
RSpec/EmptyLineAfterHook:
Enabled: false
@ -347,3 +377,13 @@ Style/FloatDivision:
Cop/BanCatchThrow:
Enabled: true
Performance/ReadlinesEach:
Enabled: true
Performance/ChainArrayAllocation:
Enabled: true
Include:
- 'lib/gitlab/import_export/**/*'
- 'ee/lib/gitlab/import_export/**/*'
- 'ee/lib/ee/gitlab/import_export/**/*'

View file

@ -251,10 +251,6 @@ RSpec/LetBeforeExamples:
RSpec/PredicateMatcher:
Enabled: false
# Offense count: 69
RSpec/RepeatedExample:
Enabled: false
# Offense count: 584
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
@ -273,14 +269,6 @@ RSpec/ScatteredSetup:
- 'spec/requests/api/jobs_spec.rb'
- 'spec/services/projects/create_service_spec.rb'
# Offense count: 4
RSpec/VoidExpect:
Exclude:
- 'spec/features/projects/services/user_activates_mattermost_slash_command_spec.rb'
- 'spec/models/ci/group_spec.rb'
- 'spec/models/ci/runner_spec.rb'
- 'spec/services/users/destroy_service_spec.rb'
# Offense count: 10
# Cop supports --auto-correct.
Rails/ApplicationController:
@ -491,13 +479,6 @@ Style/MultilineIfModifier:
- 'app/services/ci/process_pipeline_service.rb'
- 'lib/api/commit_statuses.rb'
# Offense count: 34
# Cop supports --auto-correct.
# Configuration parameters: Whitelist.
# Whitelist: be, be_a, be_an, be_between, be_falsey, be_kind_of, be_instance_of, be_truthy, be_within, eq, eql, end_with, include, match, raise_error, respond_to, start_with
Style/NestedParenthesizedCalls:
Enabled: false
# Offense count: 25
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, MinBodyLength.

View file

@ -1,5 +1,25 @@
Please view this file on the master branch, on stable branches it's out of date.
## 12.9.4 (2020-04-16)
- No changes.
### Fixed (2 changes)
- Update index_options to fix advanced search queries. !28712
- Geo: Fix repository verification on the primary. !28893
### Other (1 change)
- Use prefix search instead of ngrams for sha fields. !27597
## 12.9.3 (2020-04-14)
### Security (1 change)
- Fix filename bypass when uploading NuGet packages.
## 12.9.2 (2020-03-31)
### Fixed (4 changes)
@ -152,6 +172,13 @@ Please view this file on the master branch, on stable branches it's out of date.
- Allow users to be marked as service users. !202680
## 12.8.9 (2020-04-14)
### Security (1 change)
- Fix filename bypass when uploading NuGet packages.
## 12.8.7 (2020-03-16)
### Fixed (1 change)
@ -305,6 +332,13 @@ Please view this file on the master branch, on stable branches it's out of date.
- Prepare DB structure for GMA forking changes. !22002
## 12.7.9 (2020-04-14)
### Security (1 change)
- Fix filename bypass when uploading NuGet packages.
## 12.7.5
### Fixed (1 change)

View file

@ -2,6 +2,460 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 12.10.0 (2020-04-22)
### Removed (3 changes)
- Revert LDAP readonly attributes feature. !28541
- Remove deprecated /ci/lint page. !28562
- Remove open in file view link from Web IDE. !28705
### Fixed (118 changes, 26 of them are from the community)
- Return 202 for command only notes in REST API. !19624
- Run SAST using awk to pass env variables directly to docker without creating .env file. !21174 (Florian Gaultier)
- #42671: Project and group storage statistics now support values up to 8 PiB (up from 4GiB)
. !23131 (Matthias van de Meent)
- Fix 500 error on profile/chat_names for deleted projects. !24341
- Migrate the database to activate projects prometheus service integration for projects with prometheus installed on shared k8s cluster. !24684
- Fix archived corrupted projects not displaying in admin. !25171 (erickcspice)
- Fix some Web IDE bugs with empty projects. !25463
- Fix failing ci variable e2e test. !25924
- Fix new file not being created in non-ascii character folders. !26165
- Validate uniqueness of project_id and type when a new project service is created. !26308
- Fix assignee dropdown on new issue page. !26971
- Resolve Unable to expand multiple downstream pipelines. !27029
- Hide admin user actions for ghost and bot users. !27162
- Fix invalid ancestor group milestones when moving projects. !27262
- Fix right sidebar when scrollbars are always visible. !27314 (Shawn @CasualBot)
- Fix OpenAPI file detector. !27321 (Roger Meier)
- Fix managed_free_namespaces scope to only groups without a license or a free license. !27356
- Set commit status to failed if the TeamCity connection is refused. !27395
- Resolve Improve format support message in issue design. !27409
- Add tooltips with full path to file headers on file tree. !27437
- Scope WAF Statistics anomalies to environment.external_url. !27466
- Show the proper information in snippet edit form. !27479
- Fixes the repository Vue router not working with Chinese characters. !27494
- Fix smartcard config initialization. !27560
- Fix audit event that weren't being created for failed LDAP log-in tries. !27608
- Fix filtered search tokenization. !27648
- Fix processing of GrapqhQL query complexity based on used resolvers. !27652
- Update board scopes when promoting a label. !27662
- Reuse default generated snippet file name in repository. !27673
- Revert user bio back to non-italicized font to fix rendering of emojis. !27693
- Filter out Releases with missing tags. !27716
- Update detected languages for dependency scanning in no dind mode. !27723
- Fix logic for ingress can_uninstall?. !27729
- Fix dropped filter when paging groups. !27737 (Lee Tickett)
- Amend GraphQL merge requests resolver to check for project presence. !27783
- Fix bug issue template handling of markdown. !27808 (Lee Tickett)
- Update discord notifications to be a single embed and include log messages. !27812 (Sam Bingner)
- Update detected languages for sast in no dind mode. !27831
- Fix bug inviting members whose emails start with numbers. !27848 (Lee Tickett)
- Allow self monitoring project to query internal Prometheus even when "Allow local requests in webhooks and services" setting is false. !27865
- Add missing docstring to Prometheus metric. !27868
- Resolve Snippet creation failure bug. !27891
- Fix optional params for deploy token API. !27961 (Nejc Habjan)
- Use Ci::Pipeline#all_merge_requests.first as Ci::Build#merge_request. !27968
- Fix bug tracking snippet shard name. !27979
- Add `discussion_locked` to Webhook. !28018
- Fix invalid class option for ionice. !28023
- Improve SAST NO_DIND file detection with proper boundary conditions. !28036
- Detect skipped specs in JUnit reports and set TestCase status. !28053
- Allow 0 for pages size limit setting in admin settings. !28086
- Fix wrong colors displayed in charts. !28095
- Fix incorrect content returned on empty dotfile. !28144
- Include LDAP UID attribute in default attributes for all LDAP lookups. !28148
- Fix deploy token API to properly delete all associated deploy token records. !28156
- Fix Gitlab::Auth to handle orphaned oauth tokens. !28159
- Protect sidekiq admin UI with admin mode. !28164 (Diego Louzán)
- Prevent overriding the username when creating a Deploy Token via the API. !28175 (Ayoub Mrini)
- Resolve Snippet actions with binary data. !28191
- Make all HTTPS cookies set SameSite to none. !28205
- Don't send 'accept-encoding' in HttpIO requests. !28239
- Gracefully handle missing latest CI pipeline. !28263
- Fix error removing secondary email. !28267 (Lee Tickett)
- Fix name of approvals column in merge requests. !28274 (Steffen Köhler)
- Add management_project_id to group and project cluster creation, clarifies docs. !28289
- Check first if feature flag version_snippet is enabled. !28352
- Fix single stat panel percentile format support. !28365
- Use CTE optimization for searching board issues. !28430
- Fix missing synthetic milestone change notes for disabled milestone change event tracking feature flag. !28440
- Fix Releases page for Guest users of private projects. !28447
- Prevent ProjectUpdateRepositoryStorageWorker from moving to same filesystem. !28469
- Return error message for create_merge_request. !28482
- Include MR times in Milestone time overview. !28519 (Bob van de Vijver)
- Fix daily report result to use average of coverage values if there are multiple builds for a given group name. !28556
- Token creation uses HTTP status CREATED. !28587
- Allow award emoji same name & user duplicates when Importing. !28588
- Fix pagination in Merge Request GraphQL api. !28667 (briankabiro)
- Remove duplicate spec in web hook service spec. !28669 (Rajendra Kadam)
- Fix GraphQL SnippetType repo urls. !28673
- Add missing ON DELETE FK constraints referencing users table. !28720
- Update duplicate specs in notification service spec. !28742 (Rajendra Kadam)
- Fix styling of MR dropdown in Web IDE. !28746
- Better error message when importing a Github project and Github API rate limit is exceeded. !28785
- Prevent false positives in Ci::Pipeline#all_merge_requests. !28800
- Enable toggle all discussions button for logged out users. !28809 (Diego Louzán)
- Fix display of PyCharm generated Jupyter notebooks. !28810 (Jan Beckmann)
- Resolve Snippet update error with version flag disabled. !28815
- Show multimetric embeds on a single chart. !28841
- Fix race condition updating snippet without repository. !28851
- Normalize signature mime types when filtering attachments in emails. !28865 (Diego Louzán)
- Add autostop check to folder table. !28937
- Fix 500 error on create release API when providing an invalid tag_name. !28969 (Sashi Kumar)
- Fix missing group icons on profile page when screen < 576px. !28973
- Stringify Sidekiq job args in exception logs. !28996
- Ensure members are always added on Project Import when importing as admin. !29046
- Elasticsearch recommendation alert does not appears while screen is loaded. !29097
- Prevent wrong environment being used when processing Prometheus alert. !29119
- Fix Slack slash commands using relative URL. !29160
- Exclude 'trial_ends_on', 'shared_runners_minutes_limit' & 'extra_shared_runners_minutes_limit' from list of exported Group attributes. !29259
- Group level container registry show subgroups repos. !29263
- Move prepend to last line in finders files. !29274 (Rajendra Kadam)
- Remove 'error' from diff note error message. !29281
- Migrate legacy uploads out of deprecated paths. !29295
- Move prepend to last line in commit status presenter. !29328 (Rajendra Kadam)
- Move prepend to last line in app serializers. !29332 (Rajendra Kadam)
- Move prepend to last line in app workers and uploaders. !29379 (Rajendra Kadam)
- fix: Publish toolbar dissappears when submitting empty content. !29410
- Replace deprecated GlLoadingIcon sizes. !29417
- fix display head and base in version dropdowns. !29433
- Fix Web IDE not showing diff when opening commit tab. !29439
- Use music icon for files with .ogg extension. !29514
- Fix dashboard processing error which prevented dashboards with unknown attributes inside panels from being displayed. !29517
- Fix Deploy Token creation when no scope selected. !29614
- Update auto-build-image to v0.2.2 with fixes for docker caching. !29730
- Fix resolve WIP clearing merge request area. !29757
- Enable the Add metric button for CE users. !29769
- Fix Error 500 when inviting user to a few projects. !29778
- Fixed whitespace toggle not showing the correct diff.
- Fixed upload file creating a file in the wrong directory.
### Deprecated (1 change)
- Deprecate 'token' attribute from Runners API. !29481
### Changed (62 changes, 7 of them are from the community)
- Only enable searching of projects by full path / name on certain dropdowns. !21910
- Support wiki events in activity streams. !23869
- Fix for issue 26426: Details of runners of nested groups of an owned group are now available for users with enough permissions. !24169 (nachootal@gmail.com)
- Rename "Project Services" to "Integrations" in frontend and docs. !26244
- Support multiple Evidences for a Release. !26509
- Move some global routes to - scope. !27106
- Only display mirrored URL to users who can manage Repository settings. !27166
- Disable lookup of other ActiveSessions to determine admin mode status. !27318 (Diego Louzán)
- Extract X509::Signature from X509::Commit. !27327 (Roger Meier)
- Show user statistics in admin area also in CE, and use daily generated data for these statistics. !27345
- Update aws-ecs image location in CI template. !27382
- Update More Pages button on Wiki Page. !27499
- Update ApplicationLimits to prefer defaults. !27574
- Allow external diff files to be removed. !27602
- Add atomic and cleanup-on-fail parameters for Helm. !27721
- Change the url when the timeslider changes. !27726
- Add user_details.bio column and migrate data from users.bio. !27773
- WAF settings will be read-only if there is a new version of ingress available. !27845
- Add an helper to check if a notification_event is enabled. !27880 (Jacopo Beschi @jacopo-beschi)
- Ensure freshness of settings with snippet creation. !27897
- Update copies in Admin Panel > Repository Storage section. !27986
- Add event tracking to Container regstry quickstart. !27990
- Render snippet repository blobs. !28085
- Accept `author_username` as a param in Merge Requests API. !28100
- Use rich icons for thw rows on the file tree. !28112
- Renamed Contribution Charts as Repository Analytics. !28162
- Move Alerting feature to Core. !28196
- Add file-based pipeline conditions to default Auto DevOps CI template. !28242
- Make pipeline info in chat notifications concise. !28284
- Use different approval icon if current user approved. !28290 (Steffen Köhler)
- Remove repeated examples in user model specs. !28450 (Rajendra Kadam)
- Show only active environments in monitoring dropdown. !28456
- Enable container expiration policies by default for new projects. !28480
- Show snippet error update to the user. !28516
- Move 'Additional Metrics' feature to GitLab Core. !28527
- Add ability to search by environment state in environments GraphQL API. !28567
- Add correlation_id to project_mirror_data, expose in /import API endpoints. !28662
- Add status column to container_registry. !28682
- Cleanup the descriptions of some fields of GraphQL ProjectType. !28735
- Add Project template for Static Site Editor / Middleman. !28758
- Remove duplicate show spec in admin routing. !28790 (Rajendra Kadam)
- Add Fluentd model for cluster apps. !28846
- Add grab cursor for operations dashboard cards. !28868
- Update copy when snippet git feature disabled. !28913
- Expose relations that failed to import in /import endpoints. !28915
- Update informational text on Edit Release page. !28938
- Add support for dot (.) in variables masking. !29022
- Update Auto DevOps docker version to 19.03.8. !29081
- Make search redaction more robust. !29166
- Enable async delete in container repository list. !29175
- Make manual prometheus configuration section always editable. !29209
- Adjust label title applied to issues on import from Jira. !29246
- Track statistics per project for jira imported issues. !29406
- Display local timezone in log explorer. !29409
- Allow to retry submitting changes when an error occurs. !29434
- Define dashboard dropdowns layout in flex to improve support smaller screens. !29477
- Update auto-deploy-image to v0.13.0 for deploy job, enabling more granular control over service.enabled. !29524
- Do not display branch link in saved changes message UI. !29611
- Redesign Jira issue import UI. !29671
- Add support for /file_hooks directory. !29675
- Sort the project dropdown by star count when moving issues. !29766
- Increase the timing of polling for the merge request widget.
### Performance (45 changes)
- Limits issues displayed on milestones. !23102
- Optimize suggestions counters. !26443
- Prefetch DNS for asset host. !26868
- Move bots functionality to user_type column. !26981
- Optimize projects_service_active queries performance in usage data. !27093
- Optimize projects_mirrored_with_pipelines_enabled query performance in usage data. !27110
- Optimize ldap keys counters query performance in usage data. !27309
- Enable Workhorse upload acceleration for Project Import uploads via UI. !27332
- Cache ES enabled namespaces and projects. !27348
- Optimize template_repositories query by using batch counting. !27352
- Reduce SQL queries when rendering webhook settings. !27359
- Reduce number of SQL queries for service templates. !27396
- Improve Advanced global search performance by using routing. !27398
- Improve performance of the container repository cleanup tags service. !27441
- Optimize usage ping queries by using batch counting. !27455
- Fix redundant query execution when loading board issues. !27505
- Optimize projects_enforcing_code_owner_approval counter query performance for usage ping. !27526
- Optimize projects_reporting_ci_cd_back_to_github query performance for usage data. !27533
- Optimize service desk enabled projects counter. !27589
- Improve pagination in discussions API. !27697
- Improve API response for archived project searchs. !27717
- Optimize ci builds counters in usage data. !27770
- Enable streaming serializer feature flag by default. !27813
- Harden jira usage data. !27973
- Create merge request pipelines in background jobs. !28024
- Optimize ci builds non distinct counters in usage data. !28027
- Remove feature flag 'export_fast_serialize' and 'export_fast_serialize_with_raw_json'. !28037
- Improve API response for descending internal project searches. !28038
- Make Rails.cache and Gitlab::Redis::Cache share the same Redis connection pool. !28074
- Introduce rate limit for creating issues via web UI. !28129
- Introduce rate limit for creating issues via API. !28130
- Remove unnecessary index index_ci_builds_on_name_for_security_reports_values. !28224
- Disallow distinct count for regular batch count. !28518
- Resolve an N+1 in merge request CI variables. !28688
- Use faster streaming serializer for project exports. !28925
- Add index for created_at of resource_milestone_events. !28929
- Optimize issues with embedded grafana charts usage counter. !28936
- Avoid scheduling duplicate sidekiq jobs. !29116
- Optimize projects with repositories enabled usage data. !29117
- Use diff-stats for calculating raw diffs modified paths. !29134
- Optimize protected branches usage data. !29148
- Refresh only existing MRs on push. !29420
- Reduce SQL requests number for CreateCommitSignatureWorker. !29479
- Remove redundant index from projects table. !29507
- Add index on users.unlock_token. !276298
### Added (140 changes, 33 of them are from the community)
- New package list is enabled which includes filtering by type. !18860
- Create a rake task to cleanup unused LFS files. !21747
- Support Asciidoc docname attribute. !22313 (Jouke Witteveen)
- Adds features to delete stopped environments. !22629
- Highlight line which includes search term is code search results. !22914 (Alex Terekhov (terales))
- Allow embedded metrics charts to be hidden. !23929
- Add toggle all discussions button to MRs. !24670 (Martin Hobert & Diego Louzán)
- Store daily code coverages into ci_daily_report_results table. !24695
- Add cluster management project template. !25318
- Add limit metric to lists. !25532
- Add support for Okta as a SCIM provider. !25649
- Add grape custom validator for git reference params. !26102 (Rajendra Kadam)
- Add healthy column to clusters_applications_prometheus table. !26168
- Add API endpoint to list runners for a group. !26328
- Add unlock_membership_to_ldap boolean to Groups. !26474
- Adds wiki metadata models. !26529
- Create model to store Terraform state files. !26619
- Improve logs dropdown with more clear labels. !26635
- Add all pods view to logs explorer. !26883
- Add first_contribution to single merge request API. !26926
- Populate user_highest_roles table. !27127
- Add option for switching between blocking and logging for WAF. !27133
- Add bar chart support to monitoring dashboard. !27155
- Start merge request for custom dashboard if new branch is provided. !27189
- Update user's highest role to keep the users statistics up to date. !27231
- Make "Value Stream" the default page that appears when clicking the project-level "Analytics" sidebar item. !27279 (Gilang Gumilar)
- Add metric to derive new users count. !27351
- Display cluster type in cluster info page. !27366
- Improve logs filters on mobile, simplify kubernetes API logs filters. !27484
- Adds branch information to the package details title section. !27488
- Add forking_access_level to projects API. !27514 (Mathieu Parent)
- Add a DB column to track external issue and epic ids when importing from external sources. !27522
- Added Edit Title shared component. !27582
- Add metrics dashboard annotation model, relation, policy, create and delete services. To provide interface for create and delete operations. !27583
- Adds filter by name to the packages list. !27586
- Allow querying of Jira imports and their status via GraphQL. !27587
- Update Gitaly to 12.9.0-rc5. !27631
- Add filtered search for elastic search in logs. !27654
- Add cost factor fields to ci runners. !27666
- Add auto_ssl_failed to pages_domains. !27671
- Allow to start Jira import through graphql mutation. !27684
- Add terraform report to merge request widget. !27700
- Read metadata from Wiki front-matter. !27706
- Support custom graceful timeout for Sidekiq Cluster processes. !27710
- Show storage size on project page. !27724 (Roger Meier)
- Upload a design by copy/pasting the file into the Design Tab. !27776
- Update Active checkbox component to use toggle. !27778
- Add namespace_storage_size_limit to application settings. !27786
- Add issues to graphQL group endpoint. !27789
- Enable container registry at the group level. !27814
- Expose created_at property in Groups API. !27824
- Add an endpoint to allow group admin users to purge the dependency proxy for a group. !27843
- Filter health endpoint metrics. !27847
- Add support for system note metadata in project Import/Export. !27853 (Melvin Vermeeren)
- Add daily job to create users statistics. !27883
- Add DS_REMEDIATE env var to dependency scanning template. !27947
- Add Swift Dockerfile to GitLab templates. !28035
- Generate JWT and provide it to CI jobs for integration with other systems. !28063
- Update user's highest role to keep the users statistics up to date. !28087
- Add jira_imports table to track current jira import progress as well as historical imports data. !28108
- Add initial support for Cloud Native Buildpacks in Auto DevOps builds. !28165
- Add app server type to usage ping. !28189
- Add last_activity_before and last_activity_after filter to /api/projects endpoint. !28221 (Roger Meier)
- Expose basic project services attributes through GraphQL. !28234
- Add environment-state flag to metrics data. !28237
- Allow defining of metric step in dashboard yml. !28247
- Separate validators into own class files. !28266 (Rajendra Kadam)
- Refactor push rules and add push_rule_id columns in project settings and application settings. !28286
- Added support for single-token deletion via option/ctrl-backspace or search-filter clearing via command-backspace in filtered search. !28295 (James Becker)
- Enable log explorer to use the full height of the screen. !28312
- Automatically assign id to each panel within dashboard to support panel scoped annotations. !28341
- Add Praefect rake task to print out replica checksums. !28369
- Add rake task to update x509 signatures. !28406 (Roger Meier)
- Add application setting to enable container expiration and retention policies on pre 12.8 projects. !28479
- Add Prometheus alerts automatically after Prometheus Service was created. !28503
- Add ability to filter commits by author. !28509
- Add usage data metrics for instance level clusters and clusters with management projects. !28510
- Add slash command support for merge train. !28532
- Add metrics dashboard annotations to GraphQL API. !28550
- Refactor duplicate specs in wiki page specs. !28551 (Rajendra Kadam)
- Refactor duplicate member specs. !28574 (Rajendra Kadam)
- Remove design management as a license feature. !28589
- Add api endpoint to get x509 signature. !28590 (Roger Meier)
- Refactored Snippet edit form to Vue. !28600
- Add support for database-independent embedded metric charts. !28618
- Fix issuable duplicate spec. !28632 (Rajendra Kadam)
- Fix build duplicate spec. !28633 (Rajendra Kadam)
- Remove duplicate specs in ability model. !28644 (Rajendra Kadam)
- Remove duplicate specs in update service spec. !28650 (Rajendra Kadam)
- Add added_lines and removed_lines columns to merge_request_metrics table. !28658
- Remove duplicate specs in pipeline message spec. !28664 (Rajendra Kadam)
- Implement Terraform State API with locking. !28692
- Move export issues feature to core. !28703
- Add status endpoint to Pages Internal API. !28743
- Enable last user activity logging on the REST API. !28755
- Refresh metrics dashboard data without reloading the page. !28756
- Update duplicate specs in update large table spec. !28787 (Rajendra Kadam)
- Fix duplicate spec in factory relation spec. !28794 (Rajendra Kadam)
- Remove duplicate spec from changelog spec. !28801 (Rajendra Kadam)
- Remove duplicate spec from closing issue spec. !28803 (Rajendra Kadam)
- Allow Release links to be edited on the Edit Release page. !28816
- Create operations_user_lists table. !28822
- Added the clone button for Snippet view. !28840
- Add Fluentd table for cluster apps. !28844
- Fix duplicate spec from user helper spec. !28854 (Rajendra Kadam)
- Add missing spec for gitlab schema. !28855 (Rajendra Kadam)
- Fix duplciate spec in merge requests. !28856 (Rajendra Kadam)
- Fix duplicate spec in environment finder. !28857 (Rajendra Kadam)
- Fix duplicate spec in template dropdown spec. !28858 (Rajendra Kadam)
- Fix duplicate spec in user post diff notes. !28859 (Rajendra Kadam)
- Fix duplicate spec in filter issues. !28860 (Rajendra Kadam)
- Remove `ci_dag_support` feature flag. !28863 (Lee Tickett)
- Validate dependency on job generating a CI config when using dynamic child pipelines. !28901
- Add read_api scope to personal access tokens for granting read only API access. !28944
- Add a new default format(engineering notation) for yAxis labels in monitor charts. !28953
- Add write_registry scope to deploy tokens for container registry push access. !28958
- Add Nginx error percentage metric. !28983
- Provide configuration options for Static Site Editor. !29058
- Remove blobs_fetch_in_batches feature flag. !29069
- API endpoint to create annotations for environments dashboard. !29089
- Add graphQL interface to fetch metrics dashboard. !29112
- Add typed AWS environment variables for access keys & region. !29124
- Add line range to diff note position. !29135
- Add push rules association for groups. !29144
- Gather historical pod list from Elasticsearch. !29168
- Save changes in Static Site Editor using REST GitLab API. !29286
- Add temporary empty message when no result is found. !29306
- Add API endpoint to get users without projects. !29347
- Add status page url field to DB and setting model. !29357
- Add metrics_dashboard_access_level to project features. !29371
- Add a database column to enable or disable group owners from changing the default branch protection setting of a group. !29397
- Allow sorting of issue and MR discussions. !29492
- Update UI for project and group settings CI variables. !29584
- Add GRADLE_CLI_OPTS and SBT_CLI_OPTS env vars to dependency scanning orchestrator. !29595
- Add name_regex_keep to container_expiration_policies. !29618
- Adds Knative and Fluentd as CI/CD managed applications. !29637
- Add jira issues import feature.
- Add wildcard case in documentation for artifacts. (Fábio Matavelli)
- Add namespace storage size limit setting.
- Add placeholders to broadcast message notifications.
### Other (48 changes, 16 of them are from the community)
- Convert schema to plain SQL using structure.sql. !22808
- Provide link to a survey for Knative users. !23025
- Complete the migration of Job Artifact to Security Scan. !24244
- Migrate .fa-spinner to .spinner for app/views/shared/notes. !25028 (nuwe1)
- Migrate .fa-spinner to .spinner for app/views/ci/variables. !25030 (nuwe1)
- Migrate .fa-spinner to .spinner for ee/app/views/projects/settings. !25038 (nuwe1)
- Migrate .fa-spinner to .spinner for app/views/projects/mirrors. !25041 (nuwe1)
- Migrate .fa-spinner to .spinner for app/views/projects/network. !25050 (nuwe1)
- Migrate .fa-spinner to .spinner for app/views/groups. !25053 (nuwe1)
- Replace underscore with lodash for ./app/assets/javascripts/vue_shared. !25108 (Tobias Spagert)
- Remove health_status column from epics. !26302
- Show object access warning when disabling repo LFS. !26696
- Update icons in Sentry Error Tracking list for ignored/resolved errors. !27125
- Use Ruby 2.7 in specs to remove Ruby 2.1/2.2/2.3. !27269 (Takuya Noguchi)
- Fill user_type for ghost users. !27387
- Add Bitbucket Importer metrics. !27524
- Consume remaining LinkLFsObjectsProjects jobs. !27558
- Update GitLab Runner Helm Chart to 0.15.0. !27670
- Log Redis call count and duration to log files. !27735
- Use id instead of cve where possible when parsing remediations. !27815
- Log member additions when importing Project/Group. !27930
- Change project_export_worker urgency to throttled. !27941
- Add missing track_exception() call to Ci::CreateJobArtifactsService. !27954
- Add possibility to conigure additional rails hosts with env variable. !28133
- Remove new issue tooltip. !28261 (Victor Wu)
- Improve message when promoting project labels. !28265
- Change the link to chart copy text. !28371
- Conditional mocking of admin mode in specs by directory. !28420 (Diego Louzán)
- Align color and font-weight styles of heading elements and their typography classes. !28422
- Fix merge request threads icon buttons color. !28465
- Updated spinner next to forking message. !28506 (Victor Wu)
- Replaced old-style buttons with the new ones on Snippet view. !28614
- Change redo for retry icon in metrics dashboard. !28670
- Remove User's association max_access_level_membership. !28757
- Reduce urgency of EmailsOnPushWorker. !28783
- Use concern instead of service to update highest role. !28791
- Normalize error message between Gitea and Fogbugz importers. !28802
- Fix keyboard shortcut to navigate to your groups. !28873 (Victor Wu)
- Fix keyboard shortcut to navigate to dashboard activity. !28985 (Victor Wu)
- Remove unused index for vulnerability severity levels. !29023
- Update query labels dynamically for embedded charts. !29034
- Refactor projects/:id/packages API to supply only necessary params to PackagesFinder. !29052 (Sashi Kumar)
- Implement showing CI bridge error messages. !29123
- Update GitLab Shell to v12.1.0. !29167
- Update GitLab Elasticsearch Indexer. !29256
- Add Gitlab User-Agent to ContainerRegistry::Client. !29294 (Sashi Kumar)
- Improve error message in DAST CI template. !29388
- Remove store_mentions! in Snippets::CreateService. !29581 (Sashi Kumar)
## 12.9.4 (2020-04-16)
- No changes.
### Fixed (5 changes, 1 of them is from the community)
- Fix not working File upload from Project overview page. !26828 (Gilang Gumilar)
- Fix storage rollback regression caused by previous refactor. !28496
- Fix incorrect regex used in FileUploader#extract_dynamic_path. !28683
- Fully qualify id columns for keyset pagination (Projects API). !29026
- Fix Slack notifications when upgrading from old GitLab versions. !29111
## 12.9.3 (2020-04-14)
### Security (3 changes)
@ -508,6 +962,15 @@ entry.
- Improvement in token reference.
## 12.8.9 (2020-04-14)
### Security (3 changes)
- Refresh ProjectAuthorization during Group deletion.
- Prevent filename bypass on artifact upload.
- Update rack and related gems to 2.0.9 to fix security issue.
## 12.8.7 (2020-03-16)
### Fixed (1 change, 1 of them is from the community)
@ -1098,6 +1561,15 @@ entry.
- Add clarifying content to account fields.
## 12.7.9 (2020-04-14)
### Security (3 changes)
- Refresh ProjectAuthorization during Group deletion.
- Prevent filename bypass on artifact upload.
- Update rack and related gems to 2.0.9 to fix security issue.
## 12.7.5
### Fixed (4 changes, 1 of them is from the community)

View file

@ -1 +1 @@
12.9.3
66fd5d1b9018ebf5427141c733234060b45bf626

View file

@ -1 +1 @@
2.1.0
2.2.0

View file

@ -1 +1 @@
12.0.0
12.2.0

View file

@ -1 +1 @@
8.25.2
8.30.1

16
Gemfile
View file

@ -87,7 +87,7 @@ gem 'grape-entity', '~> 0.7.1'
gem 'rack-cors', '~> 1.0.6', require: 'rack/cors'
# GraphQL API
gem 'graphql', '~> 1.9.19'
gem 'graphql', '~> 1.10.5'
# NOTE: graphiql-rails v1.5+ doesn't work: https://gitlab.com/gitlab-org/gitlab/issues/31771
# TODO: remove app/views/graphiql/rails/editors/show.html.erb when https://github.com/rmosolgo/graphiql-rails/pull/71 is released:
# https://gitlab.com/gitlab-org/gitlab/issues/31747
@ -149,7 +149,7 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.10'
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
gem 'asciidoctor-plantuml', '0.0.10'
gem 'rouge', '~> 3.17.0'
gem 'rouge', '~> 3.18.0'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.5'
@ -301,7 +301,7 @@ gem 'sentry-raven', '~> 2.9'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '0.11.0'
gem 'gitlab-labkit', '0.12.0'
# I18n
gem 'ruby_parser', '~> 3.8', require: false
@ -319,7 +319,7 @@ gem 'peek', '~> 1.1'
gem 'snowplow-tracker', '~> 0.6.1'
# Memory benchmarks
gem 'gitlab-derailed_benchmarks', require: false
gem 'derailed_benchmarks', require: false
# Metrics
group :metrics do
@ -366,7 +366,7 @@ group :development, :test do
gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'gitlab-styles', '~> 3.1.0', require: false
gem 'gitlab-styles', '~> 3.2.0', require: false
# Pin these dependencies, otherwise a new rule could break the CI pipelines
gem 'rubocop', '~> 0.74.0'
gem 'rubocop-performance', '~> 1.4.1'
@ -399,7 +399,6 @@ group :test do
gem 'fuubar', '~> 2.2.0'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
gem 'rspec-parameterized', require: false
gem 'capybara', '~> 3.22.0'
@ -425,7 +424,7 @@ gem 'gitlab-mail_room', '~> 0.0.3', require: 'mail_room'
gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
gem 'ruby-prof', '~> 1.0.0'
gem 'ruby-prof', '~> 1.3.0'
gem 'stackprof', '~> 0.2.15', require: false
gem 'rbtrace', '~> 0.4', require: false
gem 'memory_profiler', '~> 0.9', require: false
@ -493,3 +492,6 @@ gem 'erubi', '~> 1.9.0'
# Monkey-patched in `config/initializers/mail_encoding_patch.rb`
# See https://gitlab.com/gitlab-org/gitlab/issues/197386
gem 'mail', '= 2.7.1'
# File encryption
gem 'lockbox', '~> 0.3.3'

View file

@ -211,6 +211,17 @@ GEM
declarative-option (0.1.0)
default_value_for (3.3.0)
activerecord (>= 3.2.0, < 6.1)
derailed_benchmarks (1.7.0)
benchmark-ips (~> 2)
get_process_mem (~> 0)
heapy (~> 0)
memory_profiler (~> 0)
mini_histogram (~> 0)
rack (>= 1)
rake (> 10, < 14)
ruby-statistics (>= 2.1)
thor (>= 0.19, < 2)
unicode_plot (>= 0.0.4, < 1.0.0)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
@ -259,6 +270,7 @@ GEM
launchy (~> 2.1)
mail (~> 2.7)
encryptor (3.0.0)
enumerable-statistics (2.0.1)
equalizer (0.0.11)
erubi (1.9.0)
escape_utils (1.2.1)
@ -290,7 +302,7 @@ GEM
fast_blank (1.0.0)
fast_gettext (1.6.0)
ffaker (2.10.0)
ffi (1.11.3)
ffi (1.12.2)
ffi-compiler (1.0.1)
ffi (>= 1.0.0)
rake
@ -371,16 +383,7 @@ GEM
github-markup (1.7.0)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
gitlab-derailed_benchmarks (1.6.1)
benchmark-ips (~> 2)
get_process_mem (~> 0)
heapy (~> 0)
memory_profiler (~> 0)
rack (>= 1)
rake (> 10, < 14)
ruby-statistics (>= 2.1)
thor (>= 0.19, < 2)
gitlab-labkit (0.11.0)
gitlab-labkit (0.12.0)
actionpack (>= 5.0.0, < 6.1.0)
activesupport (>= 5.0.0, < 6.1.0)
grpc (~> 1.19)
@ -398,7 +401,7 @@ GEM
gitlab-puma (>= 2.7, < 5)
gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5)
gitlab-styles (3.1.0)
gitlab-styles (3.2.0)
rubocop (~> 0.74.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-performance (~> 1.4.1)
@ -456,7 +459,7 @@ GEM
graphiql-rails (1.4.10)
railties
sprockets-rails
graphql (1.9.19)
graphql (1.10.5)
graphql-docs (1.6.0)
commonmarker (~> 0.16)
escape_utils (~> 1.2)
@ -605,6 +608,7 @@ GEM
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
locale (2.1.2)
lockbox (0.3.3)
lograge (0.10.0)
actionpack (>= 4)
activesupport (>= 4)
@ -631,6 +635,7 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2019.0331)
mimemagic (0.3.3)
mini_histogram (0.1.3)
mini_magick (4.9.5)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
@ -750,7 +755,7 @@ GEM
orm_adapter (0.5.0)
os (1.0.0)
parallel (1.19.1)
parser (2.6.5.0)
parser (2.7.0.4)
ast (~> 2.4.0)
parslet (1.8.2)
peek (1.1.0)
@ -889,7 +894,7 @@ GEM
retriable (3.1.2)
rinku (2.0.0)
rotp (2.1.2)
rouge (3.17.0)
rouge (3.18.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
@ -922,7 +927,6 @@ GEM
rspec-support (~> 3.9)
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-set (0.1.3)
rspec-support (3.9.2)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
@ -951,7 +955,7 @@ GEM
i18n
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (1.0.0)
ruby-prof (1.3.1)
ruby-progressbar (1.10.1)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
@ -1084,6 +1088,8 @@ GEM
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.6.0)
unicode_plot (0.0.4)
enumerable-statistics (>= 2.0.1)
unicode_utils (1.4.0)
unicorn (5.4.1)
kgio (~> 2.6)
@ -1094,13 +1100,13 @@ GEM
uniform_notifier (1.13.0)
unleash (0.1.5)
murmurhash3 (~> 0.1.6)
unparser (0.4.5)
unparser (0.4.7)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
concord (~> 0.1.5)
diff-lcs (~> 1.3)
equalizer (~> 0.0.9)
parser (~> 2.6.3)
parser (>= 2.6.5)
procto (~> 0.0.2)
validate_email (0.1.6)
activemodel (>= 3.0)
@ -1189,6 +1195,7 @@ DEPENDENCIES
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.3.1)
default_value_for (~> 3.3.0)
derailed_benchmarks
device_detector
devise (~> 4.6)
devise-two-factor (~> 3.1.0)
@ -1231,8 +1238,7 @@ DEPENDENCIES
gitaly (~> 12.9.0.pre.rc4)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-derailed_benchmarks
gitlab-labkit (= 0.11.0)
gitlab-labkit (= 0.12.0)
gitlab-license (~> 1.0)
gitlab-mail_room (~> 0.0.3)
gitlab-markup (~> 1.7.0)
@ -1240,7 +1246,7 @@ DEPENDENCIES
gitlab-puma (~> 4.3.3.gitlab.2)
gitlab-puma_worker_killer (~> 0.1.1.gitlab.1)
gitlab-sidekiq-fetcher (= 0.5.2)
gitlab-styles (~> 3.1.0)
gitlab-styles (~> 3.2.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2)
@ -1252,7 +1258,7 @@ DEPENDENCIES
grape-path-helpers (~> 1.2)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.9.19)
graphql (~> 1.10.5)
graphql-docs (~> 1.6.0)
grpc (~> 1.24.0)
gssapi
@ -1280,6 +1286,7 @@ DEPENDENCIES
license_finder (~> 5.4)
licensee (~> 8.9)
liquid (~> 4.0)
lockbox (~> 0.3.3)
lograge (~> 0.5)
loofah (~> 2.2)
lru_redux
@ -1346,19 +1353,18 @@ DEPENDENCIES
request_store (~> 1.3)
responders (~> 3.0)
retriable (~> 3.1.2)
rouge (~> 3.17.0)
rouge (~> 3.18.0)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 4.0.0.beta4)
rspec-retry (~> 0.6.1)
rspec-set (~> 0.1.3)
rspec_junit_formatter
rspec_profiling (~> 0.0.5)
rubocop (~> 0.74.0)
rubocop-performance (~> 1.4.1)
rubocop-rspec (~> 1.37.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 1.0.0)
ruby-prof (~> 1.3.0)
ruby-progressbar
ruby_parser (~> 3.8)
rubyzip (~> 2.0.0)

View file

@ -1,6 +0,0 @@
# For DEVELOPMENT only. Production uses Runit in
# https://gitlab.com/gitlab-org/omnibus-gitlab or the init scripts in
# lib/support/init.d, which call scripts in bin/ .
#
web: RAILS_ENV=development bin/web start_foreground
worker: RAILS_ENV=development bin/background_jobs start_foreground

View file

@ -1 +1 @@
12.9.3
12.10.0

View file

@ -0,0 +1,8 @@
import PayloadPreviewer from '~/pages/admin/application_settings/payload_previewer';
export default () => {
new PayloadPreviewer(
document.querySelector('.js-usage-ping-payload-trigger'),
document.querySelector('.js-usage-ping-payload'),
).init();
};

View file

@ -1,6 +1,12 @@
<script>
import { GlButton, GlFormGroup, GlFormInput, GlModal, GlModalDirective } from '@gitlab/ui';
import _ from 'underscore';
import {
GlDeprecatedButton,
GlFormGroup,
GlFormInput,
GlModal,
GlModalDirective,
} from '@gitlab/ui';
import { escape as esc } from 'lodash';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ToggleButton from '~/vue_shared/components/toggle_button.vue';
import axios from '~/lib/utils/axios_utils';
@ -11,7 +17,7 @@ export default {
COPY_TO_CLIPBOARD: __('Copy'),
RESET_KEY: __('Reset key'),
components: {
GlButton,
GlDeprecatedButton,
GlFormGroup,
GlFormInput,
GlModal,
@ -59,7 +65,7 @@ export default {
'AlertService|%{linkStart}Learn more%{linkEnd} about configuring this endpoint to receive alerts.',
),
{
linkStart: `<a href="${_.escape(
linkStart: `<a href="${esc(
this.learnMoreUrl,
)}" target="_blank" rel="noopener noreferrer">`,
linkEnd: '</a>',
@ -149,7 +155,9 @@ export default {
<clipboard-button :text="authorizationKey" :title="$options.COPY_TO_CLIPBOARD" />
</span>
</div>
<gl-button v-gl-modal.authKeyModal class="mt-2">{{ $options.RESET_KEY }}</gl-button>
<gl-deprecated-button v-gl-modal.authKeyModal class="mt-2">{{
$options.RESET_KEY
}}</gl-deprecated-button>
<gl-modal
modal-id="authKeyModal"
:title="$options.RESET_KEY"

View file

@ -41,11 +41,13 @@ const Api = {
createBranchPath: '/api/:version/projects/:id/repository/branches',
releasesPath: '/api/:version/projects/:id/releases',
releasePath: '/api/:version/projects/:id/releases/:tag_name',
releaseLinksPath: '/api/:version/projects/:id/releases/:tag_name/assets/links',
releaseLinkPath: '/api/:version/projects/:id/releases/:tag_name/assets/links/:link_id',
mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
adminStatisticsPath: '/api/:version/application/statistics',
pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id',
lsifPath: '/api/:version/projects/:id/commits/:commit_id/lsif/info',
environmentsPath: '/api/:version/projects/:id/environments',
rawFilePath: '/api/:version/projects/:id/repository/files/:path/raw',
group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
@ -186,6 +188,15 @@ const Api = {
return axios.get(url, { params });
},
createProjectMergeRequest(projectPath, options) {
const url = Api.buildUrl(Api.projectMergeRequestsPath).replace(
':id',
encodeURIComponent(projectPath),
);
return axios.post(url, options);
},
// Return Merge Request for project
projectMergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.projectMergeRequestPath)
@ -461,6 +472,23 @@ const Api = {
return axios.put(url, release);
},
createReleaseLink(projectPath, tagName, link) {
const url = Api.buildUrl(this.releaseLinksPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':tag_name', encodeURIComponent(tagName));
return axios.post(url, link);
},
deleteReleaseLink(projectPath, tagName, linkId) {
const url = Api.buildUrl(this.releaseLinkPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':tag_name', encodeURIComponent(tagName))
.replace(':link_id', encodeURIComponent(linkId));
return axios.delete(url);
},
adminStatistics() {
const url = Api.buildUrl(this.adminStatisticsPath);
return axios.get(url);
@ -474,19 +502,19 @@ const Api = {
return axios.get(url);
},
lsifData(projectPath, commitId, paths) {
const url = Api.buildUrl(this.lsifPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':commit_id', commitId);
return axios.get(url, { params: { paths } });
},
environments(id) {
const url = Api.buildUrl(this.environmentsPath).replace(':id', encodeURIComponent(id));
return axios.get(url);
},
getRawFile(id, path, params = { ref: 'master' }) {
const url = Api.buildUrl(this.rawFilePath)
.replace(':id', encodeURIComponent(id))
.replace(':path', encodeURIComponent(path));
return axios.get(url, { params });
},
buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
},

View file

@ -1,4 +1,4 @@
/* eslint-disable class-methods-use-this, @gitlab/i18n/no-non-i18n-strings */
/* eslint-disable class-methods-use-this, @gitlab/require-i18n-strings */
import $ from 'jquery';
import _ from 'underscore';

View file

@ -4,7 +4,7 @@ import Icon from '~/vue_shared/components/icon.vue';
export default {
// name: 'Badge' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
// eslint-disable-next-line @gitlab/require-i18n-strings
name: 'Badge',
components: {
Icon,

View file

@ -28,7 +28,7 @@ export default {
{{ s__('Badges|Your badges') }}
<span v-show="!isLoading" class="badge badge-pill">{{ badges.length }}</span>
</div>
<gl-loading-icon v-show="isLoading" :size="2" class="card-body" />
<gl-loading-icon v-show="isLoading" size="lg" class="card-body" />
<div v-if="hasNoBadges" class="card-body">
<span v-if="isGroupBadge">{{ s__('Badges|This group has no badges') }}</span>
<span v-else>{{ s__('Badges|This project has no badges') }}</span>

View file

@ -1,5 +1,5 @@
/* eslint-disable class-methods-use-this */
/* eslint-disable @gitlab/i18n/no-non-i18n-strings */
/* eslint-disable @gitlab/require-i18n-strings */
import { Node } from 'tiptap';
import { defaultMarkdownSerializer } from 'prosemirror-markdown';

View file

@ -1,6 +1,6 @@
import Vue from 'vue';
import Metrics from '~/monitoring/components/embed.vue';
import { createStore } from '~/monitoring/stores';
import EmbedGroup from '~/monitoring/components/embeds/embed_group.vue';
import { createStore } from '~/monitoring/stores/embed_group/';
// TODO: Handle copy-pasting - https://gitlab.com/gitlab-org/gitlab-foss/issues/64369.
export default function renderMetrics(elements) {
@ -8,16 +8,36 @@ export default function renderMetrics(elements) {
return;
}
elements.forEach(element => {
const { dashboardUrl } = element.dataset;
const MetricsComponent = Vue.extend(Metrics);
const EmbedGroupComponent = Vue.extend(EmbedGroup);
const wrapperList = [];
elements.forEach(element => {
let wrapper;
const { previousElementSibling } = element;
const isFirstElementInGroup = !previousElementSibling?.urls;
if (isFirstElementInGroup) {
wrapper = document.createElement('div');
wrapper.urls = [element.dataset.dashboardUrl];
element.parentNode.insertBefore(wrapper, element);
wrapperList.push(wrapper);
} else {
wrapper = previousElementSibling;
wrapper.urls.push(element.dataset.dashboardUrl);
}
// Clean up processed element
element.parentNode.removeChild(element);
});
wrapperList.forEach(wrapper => {
// eslint-disable-next-line no-new
new MetricsComponent({
el: element,
new EmbedGroupComponent({
el: wrapper,
store: createStore(),
propsData: {
dashboardUrl,
urls: wrapper.urls,
},
});
});

View file

@ -43,6 +43,10 @@ export default class BlobFileDropzone {
previewsContainer: '.dropzone-previews',
headers: csrf.headers,
init() {
this.on('processing', function() {
this.options.url = form.attr('action');
});
this.on('addedfile', () => {
toggleLoading(submitButton, submitButtonLoadingIcon, false);
dropzoneMessage.addClass(HIDDEN_CLASS);

View file

@ -1,11 +1,13 @@
<script>
import { initEditorLite } from '~/blob/utils';
import { debounce } from 'lodash';
export default {
props: {
value: {
type: String,
required: true,
required: false,
default: '',
},
fileName: {
type: String,
@ -15,7 +17,6 @@ export default {
},
data() {
return {
content: this.value,
editor: null,
};
},
@ -28,22 +29,18 @@ export default {
this.editor = initEditorLite({
el: this.$refs.editor,
blobPath: this.fileName,
blobContent: this.content,
blobContent: this.value,
});
},
methods: {
triggerFileChange() {
const val = this.editor.getValue();
this.content = val;
this.$emit('input', val);
},
triggerFileChange: debounce(function debouncedFileChange() {
this.$emit('input', this.editor.getValue());
}, 250),
},
};
</script>
<template>
<div class="file-content code">
<pre id="editor" ref="editor" data-editor-loading @focusout="triggerFileChange">{{
content
}}</pre>
<pre id="editor" ref="editor" data-editor-loading @keyup="triggerFileChange">{{ value }}</pre>
</div>
</template>

View file

@ -1,11 +1,11 @@
<script>
import { GlFormInputGroup, GlButton, GlIcon } from '@gitlab/ui';
import { GlFormInputGroup, GlDeprecatedButton, GlIcon } from '@gitlab/ui';
import { __ } from '~/locale';
export default {
components: {
GlFormInputGroup,
GlButton,
GlDeprecatedButton,
GlIcon,
},
props: {
@ -33,9 +33,9 @@ export default {
select-on-click
>
<template #append>
<gl-button new-style data-clipboard-target="#embeddable-text">
<gl-deprecated-button new-style data-clipboard-target="#embeddable-text">
<gl-icon name="copy-to-clipboard" :title="__('Copy')" />
</gl-button>
</gl-deprecated-button>
</template>
</gl-form-input-group>
</template>

View file

@ -1,5 +1,5 @@
<script>
import { GlButton, GlButtonGroup, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { GlDeprecatedButton, GlButtonGroup, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import {
BTN_COPY_CONTENTS_TITLE,
BTN_DOWNLOAD_TITLE,
@ -12,7 +12,7 @@ export default {
components: {
GlIcon,
GlButtonGroup,
GlButton,
GlDeprecatedButton,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -43,7 +43,7 @@ export default {
</script>
<template>
<gl-button-group>
<gl-button
<gl-deprecated-button
v-gl-tooltip.hover
:aria-label="$options.BTN_COPY_CONTENTS_TITLE"
:title="$options.BTN_COPY_CONTENTS_TITLE"
@ -51,8 +51,8 @@ export default {
data-clipboard-target="#blob-code-content"
>
<gl-icon name="copy-to-clipboard" :size="14" />
</gl-button>
<gl-button
</gl-deprecated-button>
<gl-deprecated-button
v-gl-tooltip.hover
:aria-label="$options.BTN_RAW_TITLE"
:title="$options.BTN_RAW_TITLE"
@ -60,8 +60,8 @@ export default {
target="_blank"
>
<gl-icon name="doc-code" :size="14" />
</gl-button>
<gl-button
</gl-deprecated-button>
<gl-deprecated-button
v-gl-tooltip.hover
:aria-label="$options.BTN_DOWNLOAD_TITLE"
:title="$options.BTN_DOWNLOAD_TITLE"
@ -69,6 +69,6 @@ export default {
target="_blank"
>
<gl-icon name="download" :size="14" />
</gl-button>
</gl-deprecated-button>
</gl-button-group>
</template>

View file

@ -1,5 +1,5 @@
<script>
import { GlButton, GlButtonGroup, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { GlDeprecatedButton, GlButtonGroup, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import {
RICH_BLOB_VIEWER,
RICH_BLOB_VIEWER_TITLE,
@ -11,7 +11,7 @@ export default {
components: {
GlIcon,
GlButtonGroup,
GlButton,
GlDeprecatedButton,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -46,7 +46,7 @@ export default {
</script>
<template>
<gl-button-group class="js-blob-viewer-switcher ml-2">
<gl-button
<gl-deprecated-button
v-gl-tooltip.hover
:aria-label="$options.SIMPLE_BLOB_VIEWER_TITLE"
:title="$options.SIMPLE_BLOB_VIEWER_TITLE"
@ -55,8 +55,8 @@ export default {
@click="switchToViewer($options.SIMPLE_BLOB_VIEWER)"
>
<gl-icon name="code" :size="14" />
</gl-button>
<gl-button
</gl-deprecated-button>
<gl-deprecated-button
v-gl-tooltip.hover
:aria-label="$options.RICH_BLOB_VIEWER_TITLE"
:title="$options.RICH_BLOB_VIEWER_TITLE"
@ -65,6 +65,6 @@ export default {
@click="switchToViewer($options.RICH_BLOB_VIEWER)"
>
<gl-icon name="document" :size="14" />
</gl-button>
</gl-deprecated-button>
</gl-button-group>
</template>

View file

@ -3,6 +3,9 @@ import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale';
import Cookies from 'js-cookie';
import { glEmojiTag } from '~/emoji';
import Tracking from '~/tracking';
const trackingMixin = Tracking.mixin();
export default {
beginnerLink:
@ -18,11 +21,14 @@ export default {
},
false,
),
goToTrackValue: 10,
trackEvent: 'click_button',
components: {
GlModal,
GlSprintf,
GlLink,
},
mixins: [trackingMixin],
props: {
goToPipelinesPath: {
type: String,
@ -32,8 +38,26 @@ export default {
type: String,
required: true,
},
humanAccess: {
type: String,
required: true,
},
},
data() {
return {
trackLabel: 'congratulate_first_pipeline',
};
},
computed: {
tracking() {
return {
label: this.trackLabel,
property: this.humanAccess,
};
},
},
mounted() {
this.track();
this.disableModalFromRenderingAgain();
},
methods: {
@ -72,7 +96,17 @@ export default {
</template>
</gl-sprintf>
<template #modal-footer>
<a :href="goToPipelinesPath" class="btn btn-success">{{ __('Go to Pipelines') }}</a>
<a
ref="goto"
:href="goToPipelinesPath"
class="btn btn-success"
:data-track-property="humanAccess"
:data-track-value="$options.goToTrackValue"
:data-track-event="$options.trackEvent"
:data-track-label="trackLabel"
>
{{ __('Go to Pipelines') }}
</a>
</template>
</gl-modal>
</template>

View file

@ -1,7 +1,6 @@
<script>
import { GlPopover, GlSprintf, GlButton, GlIcon } from '@gitlab/ui';
import Cookies from 'js-cookie';
import { parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
import { GlPopover, GlSprintf, GlDeprecatedButton, GlIcon } from '@gitlab/ui';
import { parseBoolean, scrollToElement, setCookie, getCookie } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import { glEmojiTag } from '~/emoji';
import Tracking from '~/tracking';
@ -24,11 +23,13 @@ const popoverStates = {
},
};
export default {
dismissTrackValue: 10,
clickTrackValue: 'click_button',
components: {
GlPopover,
GlSprintf,
GlIcon,
GlButton,
GlDeprecatedButton,
},
mixins: [trackingMixin],
props: {
@ -51,7 +52,7 @@ export default {
},
data() {
return {
popoverDismissed: parseBoolean(Cookies.get(this.dismissKey)),
popoverDismissed: parseBoolean(getCookie(`${this.trackLabel}_${this.dismissKey}`)),
tracking: {
label: this.trackLabel,
property: this.humanAccess,
@ -68,17 +69,27 @@ export default {
emoji() {
return popoverStates[this.trackLabel].emoji || '';
},
dismissCookieName() {
return `${this.trackLabel}_${this.dismissKey}`;
},
commitCookieName() {
return `suggest_gitlab_ci_yml_commit_${this.dismissKey}`;
},
},
mounted() {
if (this.trackLabel === 'suggest_commit_first_project_gitlab_ci_yml' && !this.popoverDismissed)
if (
this.trackLabel === 'suggest_commit_first_project_gitlab_ci_yml' &&
!this.popoverDismissed
) {
scrollToElement(document.querySelector(this.target));
}
this.trackOnShow();
},
methods: {
onDismiss() {
this.popoverDismissed = true;
Cookies.set(this.dismissKey, this.popoverDismissed, { expires: 365 });
setCookie(this.dismissCookieName, this.popoverDismissed);
},
trackOnShow() {
if (!this.popoverDismissed) this.track();
@ -100,9 +111,18 @@ export default {
<template #title>
<span v-html="suggestTitle"></span>
<span class="ml-auto">
<gl-button :aria-label="__('Close')" class="btn-blank" @click="onDismiss">
<gl-deprecated-button
:aria-label="__('Close')"
class="btn-blank"
name="dismiss"
:data-track-property="humanAccess"
:data-track-value="$options.dismissTrackValue"
:data-track-event="$options.clickTrackValue"
:data-track-label="trackLabel"
@click="onDismiss"
>
<gl-icon name="close" aria-hidden="true" />
</gl-button>
</gl-deprecated-button>
</span>
</template>

View file

@ -5,6 +5,8 @@ import NewCommitForm from '../new_commit_form';
import EditBlob from './edit_blob';
import BlobFileDropzone from '../blob/blob_file_dropzone';
import initPopover from '~/blob/suggest_gitlab_ci_yml';
import { setCookie } from '~/lib/utils/common_utils';
import Tracking from '~/tracking';
export default () => {
const editBlobForm = $('.js-edit-blob-form');
@ -60,6 +62,25 @@ export default () => {
}
if (suggestEl) {
const commitButton = document.querySelector('#commit-changes');
initPopover(suggestEl);
if (commitButton) {
const { dismissKey, humanAccess } = suggestEl.dataset;
const commitCookieName = `suggest_gitlab_ci_yml_commit_${dismissKey}`;
const commitTrackLabel = 'suggest_gitlab_ci_yml_commit_changes';
const commitTrackValue = '20';
commitButton.addEventListener('click', () => {
setCookie(commitCookieName, true);
Tracking.event(undefined, 'click_button', {
label: commitTrackLabel,
property: humanAccess,
value: commitTrackValue,
});
});
}
}
};

View file

@ -1,7 +1,7 @@
import $ from 'jquery';
import Sortable from 'sortablejs';
import Vue from 'vue';
import { GlButtonGroup, GlButton, GlLabel, GlTooltip } from '@gitlab/ui';
import { GlButtonGroup, GlDeprecatedButton, GlLabel, GlTooltip } from '@gitlab/ui';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import { s__, __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
@ -16,6 +16,14 @@ import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_
import { ListType } from '../constants';
import { isScopedLabel } from '~/lib/utils/common_utils';
/**
* Please don't edit this file, have a look at:
* ./board_column.vue
* https://gitlab.com/gitlab-org/gitlab/-/issues/212300
*
* This file here will be deleted soon
* @deprecated
*/
export default Vue.extend({
components: {
BoardBlankState,
@ -24,7 +32,7 @@ export default Vue.extend({
Icon,
GlButtonGroup,
IssueCount,
GlButton,
GlDeprecatedButton,
GlLabel,
GlTooltip,
},
@ -36,6 +44,7 @@ export default Vue.extend({
list: {
type: Object,
default: () => ({}),
required: false,
},
disabled: {
type: Boolean,
@ -53,6 +62,13 @@ export default Vue.extend({
type: String,
required: true,
},
// Does not do anything but is used
// to support the API of the new board_column.vue
canAdminList: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
@ -94,7 +110,7 @@ export default Vue.extend({
return this.list.type !== ListType.blank && this.list.type !== ListType.promotion;
},
uniqueKey() {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
// eslint-disable-next-line @gitlab/require-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
},
helpLink() {

View file

@ -2,6 +2,7 @@
/* eslint-disable vue/require-default-prop */
import IssueCardInner from './issue_card_inner.vue';
import eventHub from '../eventhub';
import sidebarEventHub from '~/sidebar/event_hub';
import boardsStore from '../stores/boards_store';
export default {
@ -13,29 +14,36 @@ export default {
list: {
type: Object,
default: () => ({}),
required: false,
},
issue: {
type: Object,
default: () => ({}),
required: false,
},
issueLinkBase: {
type: String,
default: '',
required: false,
},
disabled: {
type: Boolean,
default: false,
required: false,
},
index: {
type: Number,
default: 0,
required: false,
},
rootPath: {
type: String,
default: '',
required: false,
},
groupId: {
type: Number,
required: false,
},
},
data() {
@ -66,6 +74,11 @@ export default {
showIssue(e) {
if (e.target.classList.contains('js-no-trigger')) return;
// If no issues are opened, close all sidebars first
if (!boardsStore.detail?.issue?.id) {
sidebarEventHub.$emit('sidebar.closeAll');
}
// If CMD or CTRL is clicked
const isMultiSelect = this.canMultiSelect && (e.ctrlKey || e.metaKey);

View file

@ -0,0 +1,384 @@
<script>
import $ from 'jquery';
import Sortable from 'sortablejs';
import { GlButtonGroup, GlDeprecatedButton, GlLabel, GlTooltip, GlIcon } from '@gitlab/ui';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import { s__, __, sprintf } from '~/locale';
import Tooltip from '~/vue_shared/directives/tooltip';
import EmptyComponent from '~/vue_shared/components/empty_component';
import AccessorUtilities from '../../lib/utils/accessor';
import BoardBlankState from './board_blank_state.vue';
import BoardDelete from './board_delete';
import BoardList from './board_list.vue';
import IssueCount from './issue_count.vue';
import boardsStore from '../stores/boards_store';
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
import { ListType } from '../constants';
import { isScopedLabel } from '~/lib/utils/common_utils';
export default {
components: {
BoardPromotionState: EmptyComponent,
BoardBlankState,
BoardDelete,
BoardList,
GlButtonGroup,
IssueCount,
GlDeprecatedButton,
GlLabel,
GlTooltip,
GlIcon,
},
directives: {
Tooltip,
},
mixins: [isWipLimitsOn],
props: {
list: {
type: Object,
default: () => ({}),
required: false,
},
disabled: {
type: Boolean,
required: true,
},
issueLinkBase: {
type: String,
required: true,
},
rootPath: {
type: String,
required: true,
},
boardId: {
type: String,
required: true,
},
canAdminList: {
type: Boolean,
required: false,
default: false,
},
groupId: {
type: Number,
required: false,
default: null,
},
},
data() {
return {
detailIssue: boardsStore.detail,
filter: boardsStore.filter,
weightFeatureAvailable: false,
};
},
computed: {
isLoggedIn() {
return Boolean(gon.current_user_id);
},
showListHeaderButton() {
return (
!this.disabled &&
this.list.type !== ListType.closed &&
this.list.type !== ListType.blank &&
this.list.type !== ListType.promotion
);
},
issuesTooltip() {
const { issuesSize } = this.list;
return sprintf(__('%{issuesSize} issues'), { issuesSize });
},
// Only needed to make karma pass.
weightCountToolTip() {}, // eslint-disable-line vue/return-in-computed-property
caretTooltip() {
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
},
isNewIssueShown() {
return this.list.type === ListType.backlog || this.showListHeaderButton;
},
isSettingsShown() {
return (
this.list.type !== ListType.backlog &&
this.showListHeaderButton &&
this.list.isExpanded &&
this.isWipLimitsOn
);
},
showBoardListAndBoardInfo() {
return this.list.type !== ListType.blank && this.list.type !== ListType.promotion;
},
uniqueKey() {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
watch: {
filter: {
handler() {
this.list.page = 1;
this.list.getIssues(true).catch(() => {
// TODO: handle request error
});
},
deep: true,
},
},
mounted() {
const instance = this;
const sortableOptions = getBoardSortableDefaultOptions({
disabled: this.disabled,
group: 'boards',
draggable: '.is-draggable',
handle: '.js-board-handle',
onEnd(e) {
sortableEnd();
const sortable = this;
if (e.newIndex !== undefined && e.oldIndex !== e.newIndex) {
const order = sortable.toArray();
const list = boardsStore.findList('id', parseInt(e.item.dataset.id, 10));
instance.$nextTick(() => {
boardsStore.moveList(list, order);
});
}
},
});
Sortable.create(this.$el.parentNode, sortableOptions);
},
created() {
if (
this.list.isExpandable &&
AccessorUtilities.isLocalStorageAccessSafe() &&
!this.isLoggedIn
) {
const isCollapsed = localStorage.getItem(`${this.uniqueKey}.expanded`) === 'false';
this.list.isExpanded = !isCollapsed;
}
},
methods: {
showScopedLabels(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
showNewIssueForm() {
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
},
toggleExpanded() {
if (this.list.isExpandable) {
this.list.isExpanded = !this.list.isExpanded;
if (AccessorUtilities.isLocalStorageAccessSafe() && !this.isLoggedIn) {
localStorage.setItem(`${this.uniqueKey}.expanded`, this.list.isExpanded);
}
if (this.isLoggedIn) {
this.list.update();
}
// When expanding/collapsing, the tooltip on the caret button sometimes stays open.
// Close all tooltips manually to prevent dangling tooltips.
$('.tooltip').tooltip('hide');
}
},
},
};
</script>
<template>
<div
:class="{
'is-draggable': !list.preset,
'is-expandable': list.isExpandable,
'is-collapsed': !list.isExpanded,
'board-type-assignee': list.type === 'assignee',
}"
:data-id="list.id"
class="board h-100 px-2 align-top ws-normal"
data-qa-selector="board_list"
>
<div class="board-inner d-flex flex-column position-relative h-100 rounded">
<header
:class="{
'has-border': list.label && list.label.color,
'position-relative': list.isExpanded,
'position-absolute position-top-0 position-left-0 w-100 h-100': !list.isExpanded,
}"
:style="{ borderTopColor: list.label && list.label.color ? list.label.color : null }"
class="board-header"
data-qa-selector="board_list_header"
>
<h3
:class="{
'user-can-drag': !disabled && !list.preset,
'border-bottom-0': !list.isExpanded,
}"
class="board-title m-0 d-flex js-board-handle"
>
<div
v-if="list.isExpandable"
v-tooltip=""
:aria-label="caretTooltip"
:title="caretTooltip"
aria-hidden="true"
class="board-title-caret no-drag"
data-placement="bottom"
@click="toggleExpanded"
>
<i
:class="{ 'fa-caret-right': list.isExpanded, 'fa-caret-down': !list.isExpanded }"
class="fa fa-fw"
></i>
</div>
<!-- The following is only true in EE and if it is a milestone -->
<span
v-if="list.type === 'milestone' && list.milestone"
aria-hidden="true"
class="append-right-5 milestone-icon"
>
<gl-icon name="timer" />
</span>
<a
v-if="list.type === 'assignee'"
:href="list.assignee.path"
class="user-avatar-link js-no-trigger"
>
<img
:alt="list.assignee.name"
:src="list.assignee.avatar"
class="avatar s20 has-tooltip"
height="20"
width="20"
/>
</a>
<div class="board-title-text">
<span
v-if="list.type !== 'label'"
:class="{
'has-tooltip': !['backlog', 'closed'].includes(list.type),
'd-block': list.type === 'milestone',
}"
:title="(list.label && list.label.description) || list.title || ''"
class="board-title-main-text block-truncated"
data-container="body"
>
{{ list.title }}
</span>
<span
v-if="list.type === 'assignee'"
:title="(list.assignee && list.assignee.username) || ''"
class="board-title-sub-text prepend-left-5 has-tooltip"
>
@{{ list.assignee.username }}
</span>
<gl-label
v-if="list.type === 'label'"
:background-color="list.label.color"
:description="list.label.description"
:scoped="showScopedLabels(list.label)"
:scoped-labels-documentation-link="helpLink"
:size="!list.isExpanded ? 'sm' : ''"
:title="list.label.title"
tooltip-placement="bottom"
/>
</div>
<board-delete
v-if="canAdminList && !list.preset && list.id"
:list="list"
inline-template="true"
>
<button
:class="{ 'd-none': !list.isExpanded }"
:aria-label="__(`Delete list`)"
class="board-delete no-drag p-0 border-0 has-tooltip float-right"
data-placement="bottom"
title="Delete list"
type="button"
@click.stop="deleteBoard"
>
<i aria-hidden="true" data-hidden="true" class="fa fa-trash"></i>
</button>
</board-delete>
<div
v-if="showBoardListAndBoardInfo"
class="issue-count-badge pr-0 no-drag text-secondary"
>
<span class="d-inline-flex">
<gl-tooltip :target="() => $refs.issueCount" :title="issuesTooltip" />
<span ref="issueCount" class="issue-count-badge-count">
<gl-icon class="mr-1" name="issues" />
<issue-count :issues-size="list.issuesSize" :max-issue-count="list.maxIssueCount" />
</span>
<!-- The following is only true in EE. -->
<template v-if="weightFeatureAvailable">
<gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" />
<span ref="weightTooltip" class="d-inline-flex ml-2">
<gl-icon class="mr-1" name="weight" />
{{ list.totalWeight }}
</span>
</template>
</span>
</div>
<gl-button-group
v-if="isNewIssueShown || isSettingsShown"
class="board-list-button-group pl-2"
>
<gl-deprecated-button
v-if="isNewIssueShown"
ref="newIssueBtn"
:class="{
'd-none': !list.isExpanded,
'rounded-right': isNewIssueShown && !isSettingsShown,
}"
:aria-label="__(`New issue`)"
class="issue-count-badge-add-button no-drag"
type="button"
@click="showNewIssueForm"
>
<i aria-hidden="true" data-hidden="true" class="fa fa-plus"></i>
</gl-deprecated-button>
<gl-tooltip :target="() => $refs.newIssueBtn">{{ __('New Issue') }}</gl-tooltip>
<gl-deprecated-button
v-if="isSettingsShown"
ref="settingsBtn"
:aria-label="__(`List settings`)"
class="no-drag rounded-right"
title="List settings"
type="button"
@click="openSidebarSettings"
>
<gl-icon name="settings" />
</gl-deprecated-button>
<gl-tooltip :target="() => $refs.settingsBtn">{{ __('List settings') }}</gl-tooltip>
</gl-button-group>
</h3>
</header>
<board-list
v-if="showBoardListAndBoardInfo"
ref="board-list"
:disabled="disabled"
:group-id="groupId || null"
:issue-link-base="issueLinkBase"
:issues="list.issues"
:list="list"
:loading="list.loading"
:root-path="rootPath"
/>
<board-blank-state v-if="canAdminList && list.id === 'blank'" />
<!-- Will be only available in EE -->
<board-promotion-state v-if="list.id === 'promotion'" />
</div>
</div>
</template>

View file

@ -7,6 +7,7 @@ export default Vue.extend({
list: {
type: Object,
default: () => ({}),
required: false,
},
},
methods: {

View file

@ -1,6 +1,6 @@
<script>
import $ from 'jquery';
import { GlButton } from '@gitlab/ui';
import { GlDeprecatedButton } from '@gitlab/ui';
import { getMilestone } from 'ee_else_ce/boards/boards_util';
import ListIssue from 'ee_else_ce/boards/models/issue';
import eventHub from '../eventhub';
@ -11,7 +11,7 @@ export default {
name: 'BoardNewIssue',
components: {
ProjectSelect,
GlButton,
GlDeprecatedButton,
},
props: {
groupId: {
@ -120,17 +120,21 @@ export default {
/>
<project-select v-if="groupId" :group-id="groupId" :list="list" />
<div class="clearfix prepend-top-10">
<gl-button
<gl-deprecated-button
ref="submit-button"
:disabled="disabled"
class="float-left"
variant="success"
type="submit"
>{{ __('Submit issue') }}</gl-button
>{{ __('Submit issue') }}</gl-deprecated-button
>
<gl-deprecated-button
class="float-right"
type="button"
variant="default"
@click="cancel"
>{{ __('Cancel') }}</gl-deprecated-button
>
<gl-button class="float-right" type="button" variant="default" @click="cancel">{{
__('Cancel')
}}</gl-button>
</div>
</form>
</div>

View file

@ -34,6 +34,7 @@ export default Vue.extend({
currentUser: {
type: Object,
default: () => ({}),
required: false,
},
},
data() {
@ -102,12 +103,14 @@ export default Vue.extend({
eventHub.$on('sidebar.addAssignee', this.addAssignee);
eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$on('sidebar.saveAssignees', this.saveAssignees);
eventHub.$on('sidebar.closeAll', this.closeSidebar);
},
beforeDestroy() {
eventHub.$off('sidebar.removeAssignee', this.removeAssignee);
eventHub.$off('sidebar.addAssignee', this.addAssignee);
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
eventHub.$off('sidebar.closeAll', this.closeSidebar);
},
mounted() {
new IssuableContext(this.currentUser);

View file

@ -43,6 +43,7 @@ export default {
throttleDuration: {
type: Number,
default: 200,
required: false,
},
boardBaseUrl: {
type: String,

View file

@ -1,5 +1,5 @@
<script>
/* eslint-disable @gitlab/vue-i18n/no-bare-strings */
/* eslint-disable @gitlab/vue-require-i18n-strings */
import { __ } from '~/locale';
import ModalFilters from './filters';
import ModalTabs from './tabs.vue';

View file

@ -1,5 +1,5 @@
<script>
/* eslint-disable @gitlab/vue-i18n/no-bare-strings */
/* eslint-disable @gitlab/vue-require-i18n-strings */
import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';

View file

@ -64,6 +64,7 @@ export default {
this.groupId,
term,
{
search_namespaces: true,
with_issues_enabled: true,
with_shared: false,
include_subgroups: true,

View file

@ -3,7 +3,6 @@ import Vue from 'vue';
import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list';
import Board from 'ee_else_ce/boards/components/board';
import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar';
import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
@ -65,7 +64,15 @@ export default () => {
issueBoardsApp = new Vue({
el: $boardApp,
components: {
Board,
Board: () =>
window?.gon?.features?.sfcIssueBoards
? import('ee_else_ce/boards/components/board_column.vue')
: /**
* Please have a look at, we are moving to the SFC soon:
* https://gitlab.com/gitlab-org/gitlab/-/issues/212300
* @deprecated
*/
import('ee_else_ce/boards/components/board'),
BoardSidebar,
BoardAddIssuesModal,
BoardSettingsSidebar: () =>

View file

@ -1,5 +1,5 @@
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
/* eslint-disable-next-line @gitlab/require-i18n-strings */
throw new Error('Not implemented!');
};

View file

@ -1,7 +1,7 @@
import * as mutationTypes from './mutation_types';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
/* eslint-disable-next-line @gitlab/require-i18n-strings */
throw new Error('Not implemented!');
};

View file

@ -0,0 +1,93 @@
<script>
import {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
GlSearchBoxByType,
GlIcon,
} from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { mapGetters } from 'vuex';
export default {
name: 'CiEnvironmentsDropdown',
components: {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
GlSearchBoxByType,
GlIcon,
},
props: {
value: {
type: String,
required: false,
default: '',
},
},
data() {
return {
searchTerm: this.value || '',
};
},
computed: {
...mapGetters(['joinedEnvironments']),
composedCreateButtonLabel() {
return sprintf(__('Create wildcard: %{searchTerm}'), { searchTerm: this.searchTerm });
},
shouldRenderCreateButton() {
return this.searchTerm && !this.joinedEnvironments.includes(this.searchTerm);
},
filteredResults() {
const lowerCasedSearchTerm = this.searchTerm.toLowerCase();
return this.joinedEnvironments.filter(resultString =>
resultString.toLowerCase().includes(lowerCasedSearchTerm),
);
},
},
watch: {
value(newVal) {
this.searchTerm = newVal;
},
},
methods: {
selectEnvironment(selected) {
this.$emit('selectEnvironment', selected);
this.searchTerm = '';
},
createClicked() {
this.$emit('createClicked', this.searchTerm);
this.searchTerm = '';
},
isSelected(env) {
return this.value === env;
},
},
};
</script>
<template>
<gl-dropdown :text="value">
<gl-search-box-by-type v-model.trim="searchTerm" class="m-2" />
<gl-dropdown-item
v-for="environment in filteredResults"
:key="environment"
@click="selectEnvironment(environment)"
>
<gl-icon
:class="{ invisible: !isSelected(environment) }"
name="mobile-issue-close"
class="vertical-align-middle"
/>
{{ environment }}
</gl-dropdown-item>
<gl-dropdown-item v-if="!filteredResults.length" ref="noMatchingResults">{{
__('No matching results')
}}</gl-dropdown-item>
<template v-if="shouldRenderCreateButton">
<gl-dropdown-divider />
<gl-dropdown-item @click="createClicked">
{{ composedCreateButtonLabel }}
</gl-dropdown-item>
</template>
</gl-dropdown>
</template>

View file

@ -0,0 +1,169 @@
<script>
import { uniqueId } from 'lodash';
import { GlButton, GlFormGroup, GlFormInput } from '@gitlab/ui';
export default {
name: 'CiKeyField',
components: {
GlButton,
GlFormGroup,
GlFormInput,
},
model: {
prop: 'value',
event: 'input',
},
props: {
tokenList: {
type: Array,
required: true,
},
value: {
type: String,
required: true,
},
},
data() {
return {
results: [],
arrowCounter: -1,
userDismissedResults: false,
suggestionsId: uniqueId('token-suggestions-'),
};
},
computed: {
showAutocomplete() {
return this.showSuggestions ? 'off' : 'on';
},
showSuggestions() {
return this.results.length > 0;
},
},
mounted() {
document.addEventListener('click', this.handleClickOutside);
},
destroyed() {
document.removeEventListener('click', this.handleClickOutside);
},
methods: {
closeSuggestions() {
this.results = [];
this.arrowCounter = -1;
},
handleClickOutside(event) {
if (!this.$el.contains(event.target)) {
this.closeSuggestions();
}
},
onArrowDown() {
const newCount = this.arrowCounter + 1;
if (newCount >= this.results.length) {
this.arrowCounter = 0;
return;
}
this.arrowCounter = newCount;
},
onArrowUp() {
const newCount = this.arrowCounter - 1;
if (newCount < 0) {
this.arrowCounter = this.results.length - 1;
return;
}
this.arrowCounter = newCount;
},
onEnter() {
const currentToken = this.results[this.arrowCounter] || this.value;
this.selectToken(currentToken);
},
onEsc() {
if (!this.showSuggestions) {
this.$emit('input', '');
}
this.closeSuggestions();
this.userDismissedResults = true;
},
onEntry(value) {
this.$emit('input', value);
this.userDismissedResults = false;
// short circuit so that we don't false match on empty string
if (value.length < 1) {
this.closeSuggestions();
return;
}
const filteredTokens = this.tokenList.filter(token =>
token.toLowerCase().includes(value.toLowerCase()),
);
if (filteredTokens.length) {
this.openSuggestions(filteredTokens);
} else {
this.closeSuggestions();
}
},
openSuggestions(filteredResults) {
this.results = filteredResults;
},
selectToken(value) {
this.$emit('input', value);
this.closeSuggestions();
this.$emit('key-selected');
},
},
};
</script>
<template>
<div>
<div class="dropdown position-relative" role="combobox" aria-owns="token-suggestions">
<gl-form-group :label="__('Key')" label-for="ci-variable-key">
<gl-form-input
id="ci-variable-key"
:value="value"
type="text"
role="searchbox"
class="form-control pl-2 js-env-input"
:autocomplete="showAutocomplete"
aria-autocomplete="list"
aria-controls="token-suggestions"
aria-haspopup="listbox"
:aria-expanded="showSuggestions"
data-qa-selector="ci_variable_key_field"
@input="onEntry"
@keydown.down="onArrowDown"
@keydown.up="onArrowUp"
@keydown.enter.prevent="onEnter"
@keydown.esc.stop="onEsc"
@keydown.tab="closeSuggestions"
/>
</gl-form-group>
<div
v-show="showSuggestions && !userDismissedResults"
id="ci-variable-dropdown"
class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width"
:class="{ 'd-block': showSuggestions }"
>
<div class="dropdown-content">
<ul :id="suggestionsId">
<li
v-for="(result, i) in results"
:key="i"
role="option"
:class="{ 'gl-bg-gray-100': i === arrowCounter }"
:aria-selected="i === arrowCounter"
>
<gl-button tabindex="-1" class="btn-transparent pl-2" @click="selectToken(result)">{{
result
}}</gl-button>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>

View file

@ -0,0 +1,29 @@
import { __ } from '~/locale';
import { AWS_ACCESS_KEY_ID, AWS_DEFAULT_REGION, AWS_SECRET_ACCESS_KEY } from '../constants';
export const awsTokens = {
[AWS_ACCESS_KEY_ID]: {
name: AWS_ACCESS_KEY_ID,
/* Checks for exactly twenty characters that match key.
Based on greps suggested by Amazon at:
https://aws.amazon.com/blogs/security/a-safer-way-to-distribute-aws-credentials-to-ec2/
*/
validation: val => /^[A-Za-z0-9]{20}$/.test(val),
invalidMessage: __('This variable does not match the expected pattern.'),
},
[AWS_DEFAULT_REGION]: {
name: AWS_DEFAULT_REGION,
},
[AWS_SECRET_ACCESS_KEY]: {
name: AWS_SECRET_ACCESS_KEY,
/* Checks for exactly forty characters that match secret.
Based on greps suggested by Amazon at:
https://aws.amazon.com/blogs/security/a-safer-way-to-distribute-aws-credentials-to-ec2/
*/
validation: val => /^[A-Za-z0-9/+=]{40}$/.test(val),
invalidMessage: __('This variable does not match the expected pattern.'),
},
};
export const awsTokenList = Object.keys(awsTokens);

View file

@ -1,9 +1,6 @@
<script>
import { __ } from '~/locale';
import { mapActions, mapState } from 'vuex';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
import {
GlButton,
GlDeprecatedButton,
GlModal,
GlFormSelect,
GlFormGroup,
@ -13,11 +10,20 @@ import {
GlLink,
GlIcon,
} from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
import { awsTokens, awsTokenList } from './ci_variable_autocomplete_tokens';
import CiKeyField from './ci_key_field.vue';
import CiEnvironmentsDropdown from './ci_environments_dropdown.vue';
export default {
modalId: ADD_CI_VARIABLE_MODAL_ID,
components: {
GlButton,
CiEnvironmentsDropdown,
CiKeyField,
GlDeprecatedButton,
GlModal,
GlFormSelect,
GlFormGroup,
@ -27,6 +33,9 @@ export default {
GlLink,
GlIcon,
},
mixins: [glFeatureFlagsMixin()],
tokens: awsTokens,
tokenList: awsTokenList,
computed: {
...mapState([
'projectId',
@ -36,25 +45,27 @@ export default {
'variableBeingEdited',
'isGroup',
'maskableRegex',
'selectedEnvironment',
]),
canSubmit() {
if (this.variableData.masked && this.maskedState === false) {
return false;
}
return this.variableData.key !== '' && this.variableData.secret_value !== '';
return (
this.variableValidationState &&
this.variableData.key !== '' &&
this.variableData.secret_value !== ''
);
},
canMask() {
const regex = RegExp(this.maskableRegex);
return regex.test(this.variableData.secret_value);
},
displayMaskedError() {
return !this.canMask && this.variableData.masked && this.variableData.secret_value !== '';
return !this.canMask && this.variableData.masked;
},
maskedState() {
if (this.displayMaskedError) {
return false;
}
return null;
return true;
},
variableData() {
return this.variableBeingEdited || this.variable;
@ -62,14 +73,42 @@ export default {
modalActionText() {
return this.variableBeingEdited ? __('Update variable') : __('Add variable');
},
primaryAction() {
return {
text: this.modalActionText,
attributes: { variant: 'success', disabled: !this.canSubmit },
};
},
maskedFeedback() {
return __('This variable can not be masked');
return this.displayMaskedError ? __('This variable can not be masked.') : '';
},
tokenValidationFeedback() {
const tokenSpecificFeedback = this.$options.tokens?.[this.variableData.key]?.invalidMessage;
if (!this.tokenValidationState && tokenSpecificFeedback) {
return tokenSpecificFeedback;
}
return '';
},
tokenValidationState() {
// If the feature flag is off, do not validate. Remove when flag is removed.
if (!this.glFeatures.ciKeyAutocomplete) {
return true;
}
const validator = this.$options.tokens?.[this.variableData.key]?.validation;
if (validator) {
return validator(this.variableData.secret_value);
}
return true;
},
variableValidationFeedback() {
return `${this.tokenValidationFeedback} ${this.maskedFeedback}`;
},
variableValidationState() {
if (
this.variableData.secret_value === '' ||
(this.tokenValidationState && this.maskedState)
) {
return true;
}
return false;
},
},
methods: {
@ -80,27 +119,32 @@ export default {
'displayInputValue',
'clearModal',
'deleteVariable',
'setEnvironmentScope',
'addWildCardScope',
'resetSelectedEnvironment',
'setSelectedEnvironment',
]),
updateOrAddVariable() {
if (this.variableBeingEdited) {
this.updateVariable(this.variableBeingEdited);
} else {
this.addVariable();
}
deleteVarAndClose() {
this.deleteVariable(this.variableBeingEdited);
this.hideModal();
},
hideModal() {
this.$refs.modal.hide();
},
resetModalHandler() {
if (this.variableBeingEdited) {
this.resetEditing();
} else {
this.clearModal();
}
this.resetSelectedEnvironment();
},
hideModal() {
this.$refs.modal.hide();
},
deleteVarAndClose() {
this.deleteVariable(this.variableBeingEdited);
updateOrAddVariable() {
if (this.variableBeingEdited) {
this.updateVariable(this.variableBeingEdited);
} else {
this.addVariable();
}
this.hideModal();
},
},
@ -112,29 +156,39 @@ export default {
ref="modal"
:modal-id="$options.modalId"
:title="modalActionText"
static
lazy
@hidden="resetModalHandler"
>
<form>
<gl-form-group :label="__('Key')" label-for="ci-variable-key">
<ci-key-field
v-if="glFeatures.ciKeyAutocomplete"
v-model="variableData.key"
:token-list="$options.tokenList"
/>
<gl-form-group v-else :label="__('Key')" label-for="ci-variable-key">
<gl-form-input
id="ci-variable-key"
v-model="variableData.key"
data-qa-selector="variable_key"
data-qa-selector="ci_variable_key_field"
/>
</gl-form-group>
<gl-form-group
:label="__('Value')"
label-for="ci-variable-value"
:state="maskedState"
:invalid-feedback="maskedFeedback"
:state="variableValidationState"
:invalid-feedback="variableValidationFeedback"
>
<gl-form-textarea
id="ci-variable-value"
ref="valueField"
v-model="variableData.secret_value"
:state="variableValidationState"
rows="3"
max-rows="6"
data-qa-selector="variable_value"
data-qa-selector="ci_variable_value_field"
/>
</gl-form-group>
@ -158,10 +212,11 @@ export default {
label-for="ci-variable-env"
class="w-50"
>
<gl-form-select
id="ci-variable-env"
v-model="variableData.environment_scope"
:options="environments"
<ci-environments-dropdown
class="w-100"
:value="variableData.environment_scope"
@selectEnvironment="setEnvironmentScope"
@createClicked="addWildCardScope"
/>
</gl-form-group>
</div>
@ -180,7 +235,7 @@ export default {
<gl-form-checkbox
ref="masked-ci-variable"
v-model="variableData.masked"
data-qa-selector="variable_masked"
data-qa-selector="ci_variable_masked_checkbox"
>
{{ __('Mask variable') }}
<gl-link href="/help/ci/variables/README#masked-variables">
@ -203,22 +258,24 @@ export default {
</gl-form-group>
</form>
<template #modal-footer>
<gl-button @click="hideModal">{{ __('Cancel') }}</gl-button>
<gl-button
<gl-deprecated-button @click="hideModal">{{ __('Cancel') }}</gl-deprecated-button>
<gl-deprecated-button
v-if="variableBeingEdited"
ref="deleteCiVariable"
category="secondary"
variant="danger"
data-qa-selector="ci_variable_delete_button"
@click="deleteVarAndClose"
>{{ __('Delete variable') }}</gl-button
>{{ __('Delete variable') }}</gl-deprecated-button
>
<gl-button
<gl-deprecated-button
ref="updateOrAddVariable"
:disabled="!canSubmit"
variant="success"
data-qa-selector="ci_variable_save_button"
@click="updateOrAddVariable"
>{{ modalActionText }}
</gl-button>
</gl-deprecated-button>
</template>
</gl-modal>
</template>

View file

@ -1,12 +1,12 @@
<script>
import { GlPopover, GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { GlPopover, GlIcon, GlDeprecatedButton, GlTooltipDirective } from '@gitlab/ui';
export default {
maxTextLength: 95,
components: {
GlPopover,
GlIcon,
GlButton,
GlDeprecatedButton,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -41,14 +41,14 @@ export default {
<gl-popover :target="target" triggers="hover" placement="top" container="popover-container">
<div class="d-flex justify-content-between position-relative">
<div class="pr-5 w-100 ci-popover-value">{{ displayValue }}</div>
<gl-button
<gl-deprecated-button
v-gl-tooltip
class="btn-transparent btn-clipboard position-absolute position-top-0 position-right-0"
:title="tooltipText"
:data-clipboard-text="value"
>
<gl-icon name="copy-to-clipboard" />
</gl-button>
</gl-deprecated-button>
</div>
</gl-popover>
</div>

View file

@ -1,5 +1,5 @@
<script>
import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
import { GlTable, GlDeprecatedButton, GlModalDirective, GlIcon } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { mapState, mapActions } from 'vuex';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
@ -26,7 +26,6 @@ export default {
{
key: 'value',
label: s__('CiVariables|Value'),
tdClass: 'qa-ci-variable-input-value',
customStyle: { width: '40%' },
},
{
@ -52,7 +51,7 @@ export default {
],
components: {
GlTable,
GlButton,
GlDeprecatedButton,
GlIcon,
CiVariablePopover,
},
@ -89,9 +88,11 @@ export default {
:fields="fields"
:items="variables"
tbody-tr-class="js-ci-variable-row"
data-qa-selector="ci_variable_table_content"
sort-by="key"
sort-direction="asc"
stacked="lg"
table-class="text-secondary"
fixed
show-empty
sort-icon-left
@ -146,13 +147,14 @@ export default {
</div>
</template>
<template #cell(actions)="{ item }">
<gl-button
<gl-deprecated-button
ref="edit-ci-variable"
v-gl-modal-directive="$options.modalId"
data-qa-selector="edit_ci_variable_button"
@click="editVariable(item)"
>
<gl-icon :size="$options.iconSize" name="pencil" />
</gl-button>
</gl-deprecated-button>
</template>
<template #empty>
<p ref="empty-variables" class="text-center empty-variables text-plain">
@ -164,20 +166,20 @@ export default {
class="ci-variable-actions d-flex justify-content-end"
:class="{ 'justify-content-center': !tableIsNotEmpty }"
>
<gl-button
<gl-deprecated-button
v-if="tableIsNotEmpty"
ref="secret-value-reveal-button"
data-qa-selector="reveal_ci_variable_value"
data-qa-selector="reveal_ci_variable_value_button"
class="append-right-8"
@click="toggleValues(!valuesHidden)"
>{{ valuesButtonText }}</gl-button
>{{ valuesButtonText }}</gl-deprecated-button
>
<gl-button
<gl-deprecated-button
ref="add-ci-variable"
v-gl-modal-directive="$options.modalId"
data-qa-selector="add_ci_variable"
data-qa-selector="add_ci_variable_button"
variant="success"
>{{ __('Add Variable') }}</gl-button
>{{ __('Add Variable') }}</gl-deprecated-button
>
</div>
</div>

View file

@ -6,7 +6,7 @@ export const ADD_CI_VARIABLE_MODAL_ID = 'add-ci-variable';
export const displayText = {
variableText: __('Var'),
fileText: __('File'),
allEnvironmentsText: __('All'),
allEnvironmentsText: __('All (default)'),
};
export const types = {
@ -14,3 +14,8 @@ export const types = {
fileType: 'file',
allEnvironmentsType: '*',
};
// AWS TOKEN CONSTANTS
export const AWS_ACCESS_KEY_ID = 'AWS_ACCESS_KEY_ID';
export const AWS_DEFAULT_REGION = 'AWS_DEFAULT_REGION';
export const AWS_SECRET_ACCESS_KEY = 'AWS_SECRET_ACCESS_KEY';

View file

@ -153,3 +153,22 @@ export const fetchEnvironments = ({ dispatch, state }) => {
createFlash(__('There was an error fetching the environments information.'));
});
};
export const setEnvironmentScope = ({ commit, dispatch }, environment) => {
commit(types.SET_ENVIRONMENT_SCOPE, environment);
dispatch('setSelectedEnvironment', environment);
};
export const addWildCardScope = ({ commit, dispatch }, environment) => {
commit(types.ADD_WILD_CARD_SCOPE, environment);
commit(types.SET_ENVIRONMENT_SCOPE, environment);
dispatch('setSelectedEnvironment', environment);
};
export const resetSelectedEnvironment = ({ commit }) => {
commit(types.RESET_SELECTED_ENVIRONMENT);
};
export const setSelectedEnvironment = ({ commit }, environment) => {
commit(types.SET_SELECTED_ENVIRONMENT, environment);
};

View file

@ -0,0 +1,9 @@
/* eslint-disable import/prefer-default-export */
// Disabling import/prefer-default-export can be
// removed once a second getter is added to this file
import { uniq } from 'lodash';
export const joinedEnvironments = state => {
const scopesFromVariables = (state.variables || []).map(variable => variable.environment_scope);
return uniq(state.environments.concat(scopesFromVariables)).sort();
};

View file

@ -1,6 +1,7 @@
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
import state from './state';
@ -10,6 +11,7 @@ export default (initialState = {}) =>
new Vuex.Store({
actions,
mutations,
getters,
state: {
...state(),
...initialState,

View file

@ -20,3 +20,7 @@ export const RECEIVE_UPDATE_VARIABLE_ERROR = 'RECEIVE_UPDATE_VARIABLE_ERROR';
export const REQUEST_ENVIRONMENTS = 'REQUEST_ENVIRONMENTS';
export const RECEIVE_ENVIRONMENTS_SUCCESS = 'RECEIVE_ENVIRONMENTS_SUCCESS';
export const SET_ENVIRONMENT_SCOPE = 'SET_ENVIRONMENT_SCOPE';
export const ADD_WILD_CARD_SCOPE = 'ADD_WILD_CARD_SCOPE';
export const RESET_SELECTED_ENVIRONMENT = 'RESET_SELECTED_ENVIRONMENT';
export const SET_SELECTED_ENVIRONMENT = 'SET_SELECTED_ENVIRONMENT';

View file

@ -83,4 +83,25 @@ export default {
state.variableBeingEdited = null;
state.showInputValue = false;
},
[types.SET_ENVIRONMENT_SCOPE](state, environment) {
if (state.variableBeingEdited) {
state.variableBeingEdited.environment_scope = environment;
} else {
state.variable.environment_scope = environment;
}
},
[types.ADD_WILD_CARD_SCOPE](state, environment) {
state.environments.push(environment);
state.environments.sort();
},
[types.RESET_SELECTED_ENVIRONMENT](state) {
state.selectedEnvironment = '';
},
[types.SET_SELECTED_ENVIRONMENT](state, environment) {
state.selectedEnvironment = environment;
},
};

View file

@ -21,4 +21,5 @@ export default () => ({
environments: [],
typeOptions: [displayText.variableText, displayText.fileText],
variableBeingEdited: null,
selectedEnvironment: '',
});

View file

@ -8,13 +8,20 @@ import Flash from '../flash';
import Poll from '../lib/utils/poll';
import initSettingsPanels from '../settings_panels';
import eventHub from './event_hub';
import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX, CROSSPLANE } from './constants';
import {
APPLICATION_STATUS,
INGRESS,
INGRESS_DOMAIN_SUFFIX,
CROSSPLANE,
KNATIVE,
} from './constants';
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';
import Applications from './components/applications.vue';
import RemoveClusterConfirmation from './components/remove_cluster_confirmation.vue';
import setupToggleButtons from '../toggle_buttons';
import initProjectSelectDropdown from '~/project_select';
import initServerlessSurveyBanner from '~/serverless/survey_banner';
const Environments = () => import('ee_component/clusters/components/environments.vue');
@ -252,11 +259,12 @@ export default class Clusters {
eventHub.$on('installApplication', this.installApplication);
eventHub.$on('updateApplication', data => this.updateApplication(data));
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
eventHub.$on('setKnativeDomain', data => this.setKnativeDomain(data));
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
eventHub.$on('setIngressModSecurityEnabled', data => this.setIngressModSecurityEnabled(data));
eventHub.$on('resetIngressModSecurityEnabled', id => this.resetIngressModSecurityEnabled(id));
eventHub.$on('setIngressModSecurityMode', data => this.setIngressModSecurityMode(data));
eventHub.$on('resetIngressModSecurityChanges', id => this.resetIngressModSecurityChanges(id));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
@ -267,11 +275,12 @@ export default class Clusters {
eventHub.$off('installApplication', this.installApplication);
eventHub.$off('updateApplication', this.updateApplication);
eventHub.$off('saveKnativeDomain');
eventHub.$off('setKnativeHostname');
eventHub.$off('setKnativeDomain');
eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
eventHub.$off('setIngressModSecurityEnabled');
eventHub.$off('resetIngressModSecurityEnabled');
eventHub.$off('setIngressModSecurityMode');
eventHub.$off('resetIngressModSecurityChanges');
}
initPolling(method, successCallback, errorCallback) {
@ -324,6 +333,10 @@ export default class Clusters {
this.store.state.applications[INGRESS],
);
}
if (this.store.state.applications[KNATIVE]?.status === APPLICATION_STATUS.INSTALLED) {
initServerlessSurveyBanner();
}
}
showToken() {
@ -508,10 +521,10 @@ export default class Clusters {
});
}
setKnativeHostname(data) {
const appId = data.id;
this.store.updateAppProperty(appId, 'isEditingHostName', true);
this.store.updateAppProperty(appId, 'hostname', data.hostname);
setKnativeDomain({ id: appId, domain, domainId }) {
this.store.updateAppProperty(appId, 'isEditingDomain', true);
this.store.updateAppProperty(appId, 'hostname', domain);
this.store.updateAppProperty(appId, 'pagesDomain', domainId ? { id: domainId, domain } : null);
}
setCrossplaneProviderStack(data) {
@ -525,8 +538,14 @@ export default class Clusters {
this.store.updateAppProperty(id, 'modsecurity_enabled', modSecurityEnabled);
}
resetIngressModSecurityEnabled(id) {
setIngressModSecurityMode({ id, modSecurityMode }) {
this.store.updateAppProperty(id, 'isEditingModSecurityMode', true);
this.store.updateAppProperty(id, 'modsecurity_mode', modSecurityMode);
}
resetIngressModSecurityChanges(id) {
this.store.updateAppProperty(id, 'isEditingModSecurityEnabled', false);
this.store.updateAppProperty(id, 'isEditingModSecurityMode', false);
}
destroy() {

View file

@ -1,6 +1,6 @@
<script>
/* eslint-disable vue/require-default-prop */
/* eslint-disable @gitlab/vue-i18n/no-bare-strings */
/* eslint-disable @gitlab/vue-require-i18n-strings */
import { GlLink, GlModalDirective } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale';
import eventHub from '../event_hub';
@ -95,6 +95,7 @@ export default {
updateable: {
type: Boolean,
default: true,
required: false,
},
updateSuccessful: {
type: Boolean,

View file

@ -1,5 +1,5 @@
<script>
import _ from 'underscore';
import { escape as esc } from 'lodash';
import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg';
import { GlLoadingIcon } from '@gitlab/ui';
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
@ -107,8 +107,12 @@ export default {
isProjectCluster() {
return this.type === CLUSTER_TYPE.PROJECT;
},
managedAppsLocalTillerEnabled() {
return Boolean(gon.features?.managedAppsLocalTiller);
},
helmInstalled() {
return (
this.managedAppsLocalTillerEnabled ||
this.applications.helm.status === APPLICATION_STATUS.INSTALLED ||
this.applications.helm.status === APPLICATION_STATUS.UPDATED
);
@ -130,7 +134,7 @@ export default {
},
ingressDescription() {
return sprintf(
_.escape(
esc(
s__(
`ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}.`,
),
@ -138,14 +142,14 @@ export default {
{
pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb"
target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|pricing'))}</a>`,
${esc(s__('ClusterIntegration|pricing'))}</a>`,
},
false,
);
},
certManagerDescription() {
return sprintf(
_.escape(
esc(
s__(
`ClusterIntegration|Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates.
Installing Cert-Manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates
@ -155,14 +159,14 @@ export default {
{
letsEncrypt: `<a href="https://letsencrypt.org/"
target="_blank" rel="noopener noreferrer">
${_.escape(s__("ClusterIntegration|Let's Encrypt"))}</a>`,
${esc(s__("ClusterIntegration|Let's Encrypt"))}</a>`,
},
false,
);
},
crossplaneDescription() {
return sprintf(
_.escape(
esc(
s__(
`ClusterIntegration|Crossplane enables declarative provisioning of managed services from your cloud of choice using %{kubectl} or %{gitlabIntegrationLink}.
Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on.`,
@ -171,7 +175,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
{
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ee/user/clusters/applications.html#crossplane"
target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|Gitlab Integration'))}</a>`,
${esc(s__('ClusterIntegration|Gitlab Integration'))}</a>`,
kubectl: `<code>kubectl</code>`,
},
false,
@ -180,7 +184,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
prometheusDescription() {
return sprintf(
_.escape(
esc(
s__(
`ClusterIntegration|Prometheus is an open-source monitoring system
with %{gitlabIntegrationLink} to monitor deployed applications.`,
@ -189,7 +193,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
{
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html"
target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`,
${esc(s__('ClusterIntegration|GitLab Integration'))}</a>`,
},
false,
);
@ -215,11 +219,11 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
installedVia() {
if (this.cloudRun) {
return sprintf(
_.escape(s__(`ClusterIntegration|installed via %{installed_via}`)),
esc(s__(`ClusterIntegration|installed via %{installed_via}`)),
{
installed_via: `<a href="${
this.cloudRunHelpPath
}" target="_blank" rel="noopener noreferrer">${_.escape(
}" target="_blank" rel="noopener noreferrer">${esc(
s__('ClusterIntegration|Cloud Run'),
)}</a>`,
},
@ -236,16 +240,20 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
this.helmInstallIllustration = helmInstallIllustration;
},
methods: {
saveKnativeDomain(hostname) {
saveKnativeDomain() {
eventHub.$emit('saveKnativeDomain', {
id: 'knative',
params: { hostname },
params: {
hostname: this.applications.knative.hostname,
pages_domain_id: this.applications.knative.pagesDomain?.id,
},
});
},
setKnativeHostname(hostname) {
eventHub.$emit('setKnativeHostname', {
setKnativeDomain({ domainId, domain }) {
eventHub.$emit('setKnativeDomain', {
id: 'knative',
hostname,
domainId,
domain,
});
},
setCrossplaneProviderStack(stack) {
@ -270,6 +278,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
<div class="cluster-application-list prepend-top-10">
<application-row
v-if="!managedAppsLocalTillerEnabled"
id="helm"
:logo-url="helmLogo"
:title="applications.helm.title"
@ -313,6 +322,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
:install-failed="applications.ingress.installFailed"
:install-application-request-params="{
modsecurity_enabled: applications.ingress.modsecurity_enabled,
modsecurity_mode: applications.ingress.modsecurity_mode,
}"
:uninstallable="applications.ingress.uninstallable"
:uninstall-successful="applications.ingress.uninstallSuccessful"
@ -585,7 +595,10 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
:request-reason="applications.knative.requestReason"
:installed="applications.knative.installed"
:install-failed="applications.knative.installFailed"
:install-application-request-params="{ hostname: applications.knative.hostname }"
:install-application-request-params="{
hostname: applications.knative.hostname,
pages_domain_id: applications.knative.pagesDomain && applications.knative.pagesDomain.id,
}"
:installed-via="installedVia"
:uninstallable="applications.knative.uninstallable"
:uninstall-successful="applications.knative.uninstallSuccessful"
@ -622,7 +635,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
:knative="knative"
:ingress-dns-help-path="ingressDnsHelpPath"
@save="saveKnativeDomain"
@set="setKnativeHostname"
@set="setKnativeDomain"
/>
</div>
</application-row>

View file

@ -1,8 +1,17 @@
<script>
import _ from 'lodash';
import { __ } from '../../locale';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
import { GlAlert, GlSprintf, GlLink, GlToggle, GlButton } from '@gitlab/ui';
import { escape as esc } from 'lodash';
import { s__, __ } from '../../locale';
import { APPLICATION_STATUS, INGRESS, LOGGING_MODE, BLOCKING_MODE } from '~/clusters/constants';
import {
GlAlert,
GlSprintf,
GlLink,
GlToggle,
GlDeprecatedButton,
GlDropdown,
GlDropdownItem,
GlIcon,
} from '@gitlab/ui';
import eventHub from '~/clusters/event_hub';
import modSecurityLogo from 'images/cluster_app_logos/modsecurity.png';
@ -16,7 +25,10 @@ export default {
GlSprintf,
GlLink,
GlToggle,
GlButton,
GlDeprecatedButton,
GlDropdown,
GlDropdownItem,
GlIcon,
},
props: {
ingress: {
@ -28,10 +40,23 @@ export default {
required: false,
default: '',
},
modes: {
type: Object,
required: false,
default: () => ({
[LOGGING_MODE]: {
name: s__('ClusterIntegration|Logging mode'),
},
[BLOCKING_MODE]: {
name: s__('ClusterIntegration|Blocking mode'),
},
}),
},
},
data: () => ({
modSecurityLogo,
hasValueChanged: false,
initialValue: null,
initialMode: null,
}),
computed: {
modSecurityEnabled: {
@ -39,25 +64,39 @@ export default {
return this.ingress.modsecurity_enabled;
},
set(isEnabled) {
if (this.initialValue === null) {
this.initialValue = this.ingress.modsecurity_enabled;
}
eventHub.$emit('setIngressModSecurityEnabled', {
id: INGRESS,
modSecurityEnabled: isEnabled,
});
if (this.hasValueChanged) {
this.resetStatus();
} else {
this.hasValueChanged = true;
}
},
},
hasValueChanged() {
return this.modSecurityEnabledChanged || this.modSecurityModeChanged;
},
modSecurityEnabledChanged() {
return this.initialValue !== null && this.initialValue !== this.ingress.modsecurity_enabled;
},
modSecurityModeChanged() {
return (
this.ingress.modsecurity_enabled &&
this.initialMode !== null &&
this.initialMode !== this.ingress.modsecurity_mode
);
},
ingressModSecurityDescription() {
return _.escape(this.ingressModSecurityHelpPath);
return esc(this.ingressModSecurityHelpPath);
},
saving() {
return [UPDATING].includes(this.ingress.status);
},
saveButtonDisabled() {
return [UNINSTALLING, UPDATING, INSTALLING].includes(this.ingress.status);
return (
[UNINSTALLING, UPDATING, INSTALLING].includes(this.ingress.status) ||
this.ingress.updateAvailable
);
},
saveButtonLabel() {
return this.saving ? __('Saving') : __('Save changes');
@ -69,22 +108,45 @@ export default {
* neither getting installed nor updated.
*/
showButtons() {
return (
this.saving || (this.hasValueChanged && [INSTALLED, UPDATED].includes(this.ingress.status))
);
return this.saving || this.valuesChangedByUser;
},
modSecurityModeName() {
return this.modes[this.ingress.modsecurity_mode].name;
},
valuesChangedByUser() {
return this.hasValueChanged && [INSTALLED, UPDATED].includes(this.ingress.status);
},
},
methods: {
updateApplication() {
eventHub.$emit('updateApplication', {
id: INGRESS,
params: { modsecurity_enabled: this.ingress.modsecurity_enabled },
params: {
modsecurity_enabled: this.ingress.modsecurity_enabled,
modsecurity_mode: this.ingress.modsecurity_mode,
},
});
this.resetStatus();
},
resetStatus() {
eventHub.$emit('resetIngressModSecurityEnabled', INGRESS);
this.hasValueChanged = false;
if (this.initialMode !== null) {
this.ingress.modsecurity_mode = this.initialMode;
}
if (this.initialValue !== null) {
this.ingress.modsecurity_enabled = this.initialValue;
}
this.initialValue = null;
this.initialMode = null;
eventHub.$emit('resetIngressModSecurityChanges', INGRESS);
},
selectMode(modeKey) {
if (this.initialMode === null) {
this.initialMode = this.ingress.modsecurity_mode;
}
eventHub.$emit('setIngressModSecurityMode', {
id: INGRESS,
modSecurityMode: modeKey,
});
},
},
};
@ -144,18 +206,46 @@ export default {
label-position="right"
/>
</div>
<div v-if="showButtons">
<gl-button
<div
v-if="ingress.modsecurity_enabled"
class="gl-responsive-table-row-layout mt-3"
role="row"
>
<div class="table-section section-wrap" role="gridcell">
<strong>
{{ s__('ClusterIntegration|Global default') }}
<gl-icon name="earth" class="align-text-bottom" />
</strong>
<div class="form-group">
<p class="form-text text-muted">
<strong>
{{
s__(
'ClusterIntegration|Set the global mode for the WAF in this cluster. This can be overridden at the environmental level.',
)
}}
</strong>
</p>
</div>
<gl-dropdown :text="modSecurityModeName" :disabled="saveButtonDisabled">
<gl-dropdown-item v-for="(mode, key) in modes" :key="key" @click="selectMode(key)">
{{ mode.name }}
</gl-dropdown-item>
</gl-dropdown>
</div>
</div>
<div v-if="showButtons" class="mt-3">
<gl-deprecated-button
class="btn-success inline mr-1"
:loading="saving"
:disabled="saveButtonDisabled"
@click="updateApplication"
>
{{ saveButtonLabel }}
</gl-button>
<gl-button :disabled="saveButtonDisabled" @click="resetStatus">
</gl-deprecated-button>
<gl-deprecated-button :disabled="saveButtonDisabled" @click="resetStatus">
{{ __('Cancel') }}
</gl-button>
</gl-deprecated-button>
</div>
</div>
</div>

View file

@ -1,5 +1,12 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import {
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlLoadingIcon,
GlSearchBoxByType,
GlSprintf,
} from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import { __, s__ } from '~/locale';
@ -13,6 +20,11 @@ export default {
LoadingButton,
ClipboardButton,
GlLoadingIcon,
GlDropdown,
GlDropdownDivider,
GlDropdownItem,
GlSearchBoxByType,
GlSprintf,
},
props: {
knative: {
@ -22,8 +34,14 @@ export default {
ingressDnsHelpPath: {
type: String,
default: '',
required: false,
},
},
data() {
return {
searchQuery: '',
};
},
computed: {
saveButtonDisabled() {
return [UNINSTALLING, UPDATING].includes(this.knative.status);
@ -48,9 +66,22 @@ export default {
return this.knative.hostname;
},
set(hostname) {
this.$emit('set', hostname);
this.selectCustomDomain(hostname);
},
},
domainDropdownText() {
return this.knativeHostname || s__('ClusterIntegration|Select existing domain or use new');
},
availableDomains() {
return this.knative.availableDomains || [];
},
filteredDomains() {
const query = this.searchQuery.toLowerCase();
return this.availableDomains.filter(({ domain }) => domain.toLowerCase().includes(query));
},
showDomainsDropdown() {
return this.availableDomains.length > 0;
},
},
watch: {
knativeUpdateSuccessful(updateSuccessful) {
@ -59,6 +90,14 @@ export default {
}
},
},
methods: {
selectDomain({ id, domain }) {
this.$emit('set', { domain, domainId: id });
},
selectCustomDomain(domain) {
this.$emit('set', { domain, domainId: null });
},
},
};
</script>
@ -71,22 +110,55 @@ export default {
{{ s__('ClusterIntegration|Something went wrong while updating Knative domain name.') }}
</div>
<template>
<div
:class="{ 'col-md-6': knativeInstalled, 'col-12': !knativeInstalled }"
class="form-group col-sm-12 mb-0"
<div
:class="{ 'col-md-6': knativeInstalled, 'col-12': !knativeInstalled }"
class="form-group col-sm-12 mb-0"
>
<label for="knative-domainname">
<strong>{{ s__('ClusterIntegration|Knative Domain Name:') }}</strong>
</label>
<gl-dropdown
v-if="showDomainsDropdown"
:text="domainDropdownText"
toggle-class="dropdown-menu-toggle"
class="w-100 mb-2"
>
<label for="knative-domainname">
<strong>{{ s__('ClusterIntegration|Knative Domain Name:') }}</strong>
</label>
<input
id="knative-domainname"
v-model="knativeHostname"
type="text"
class="form-control js-knative-domainname"
<gl-search-box-by-type
v-model.trim="searchQuery"
:placeholder="s__('ClusterIntegration|Search domains')"
class="m-2"
/>
</div>
</template>
<gl-dropdown-item
v-for="domain in filteredDomains"
:key="domain.id"
@click="selectDomain(domain)"
>
<span class="ml-1">{{ domain.domain }}</span>
</gl-dropdown-item>
<template v-if="searchQuery">
<gl-dropdown-divider />
<gl-dropdown-item key="custom-domain" @click="selectCustomDomain(searchQuery)">
<span class="ml-1">
<gl-sprintf :message="s__('ClusterIntegration|Use %{query}')">
<template #query>
<code>{{ searchQuery }}</code>
</template>
</gl-sprintf>
</span>
</gl-dropdown-item>
</template>
</gl-dropdown>
<input
v-else
id="knative-domainname"
v-model="knativeHostname"
type="text"
class="form-control js-knative-domainname"
/>
</div>
<template v-if="knativeInstalled">
<div class="form-group col-sm-12 col-md-6 pl-md-0 mb-0 mt-3 mt-md-0">
<label for="knative-endpoint">
@ -143,7 +215,7 @@ export default {
:loading="saving"
:disabled="saveButtonDisabled"
:label="saveButtonLabel"
@click="$emit('save', knativeHostname)"
@click="$emit('save')"
/>
</template>
</div>

View file

@ -1,7 +1,7 @@
<script>
import _ from 'underscore';
import { escape as esc } from 'lodash';
import SplitButton from '~/vue_shared/components/split_button.vue';
import { GlModal, GlButton, GlFormInput } from '@gitlab/ui';
import { GlModal, GlDeprecatedButton, GlFormInput } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
@ -27,7 +27,7 @@ export default {
components: {
SplitButton,
GlModal,
GlButton,
GlDeprecatedButton,
GlFormInput,
},
props: {
@ -82,7 +82,7 @@ export default {
)
: s__('ClusterIntegration|To remove your integration, type %{clusterName} to confirm:'),
{
clusterName: `<code>${_.escape(this.clusterName)}</code>`,
clusterName: `<code>${esc(this.clusterName)}</code>`,
},
false,
);
@ -148,19 +148,24 @@ export default {
}}</span>
</template>
<template slot="modal-footer">
<gl-button variant="secondary" @click="handleCancel">{{ s__('Cancel') }}</gl-button>
<gl-deprecated-button variant="secondary" @click="handleCancel">{{
s__('Cancel')
}}</gl-deprecated-button>
<template v-if="confirmCleanup">
<gl-button :disabled="!canSubmit" variant="warning" @click="handleSubmit">{{
<gl-deprecated-button :disabled="!canSubmit" variant="warning" @click="handleSubmit">{{
s__('ClusterIntegration|Remove integration')
}}</gl-button>
<gl-button :disabled="!canSubmit" variant="danger" @click="handleSubmit(true)">{{
s__('ClusterIntegration|Remove integration and resources')
}}</gl-button>
}}</gl-deprecated-button>
<gl-deprecated-button
:disabled="!canSubmit"
variant="danger"
@click="handleSubmit(true)"
>{{ s__('ClusterIntegration|Remove integration and resources') }}</gl-deprecated-button
>
</template>
<template v-else>
<gl-button :disabled="!canSubmit" variant="danger" @click="handleSubmit">{{
<gl-deprecated-button :disabled="!canSubmit" variant="danger" @click="handleSubmit">{{
s__('ClusterIntegration|Remove integration')
}}</gl-button>
}}</gl-deprecated-button>
</template>
</template>
</gl-modal>

View file

@ -66,3 +66,6 @@ export const APPLICATIONS = [
];
export const INGRESS_DOMAIN_SUFFIX = '.nip.io';
export const LOGGING_MODE = 'logging';
export const BLOCKING_MODE = 'blocking';

View file

@ -191,7 +191,8 @@ const applicationStateMachine = {
* @param {*} event
*/
const transitionApplicationState = (application, event) => {
const newState = applicationStateMachine[application.status].on[event];
const stateMachine = applicationStateMachine[application.status];
const newState = stateMachine !== undefined ? stateMachine.on[event] : false;
return newState
? {

View file

@ -53,10 +53,13 @@ export default class ClusterStore {
...applicationInitialState,
title: s__('ClusterIntegration|Ingress'),
modsecurity_enabled: false,
modsecurity_mode: null,
externalIp: null,
externalHostname: null,
isEditingModSecurityEnabled: false,
isEditingModSecurityMode: false,
updateFailed: false,
updateAvailable: false,
},
cert_manager: {
...applicationInitialState,
@ -90,7 +93,7 @@ export default class ClusterStore {
...applicationInitialState,
title: s__('ClusterIntegration|Knative'),
hostname: null,
isEditingHostName: false,
isEditingDomain: false,
externalIp: null,
externalHostname: null,
updateSuccessful: false,
@ -211,9 +214,13 @@ export default class ClusterStore {
if (appId === INGRESS) {
this.state.applications.ingress.externalIp = serverAppEntry.external_ip;
this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname;
this.state.applications.ingress.updateAvailable = updateAvailable;
if (!this.state.applications.ingress.isEditingModSecurityEnabled) {
this.state.applications.ingress.modsecurity_enabled = serverAppEntry.modsecurity_enabled;
}
if (!this.state.applications.ingress.isEditingModSecurityMode) {
this.state.applications.ingress.modsecurity_mode = serverAppEntry.modsecurity_mode;
}
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
this.state.applications.cert_manager.email || serverAppEntry.email;
@ -227,7 +234,12 @@ export default class ClusterStore {
'jupyter',
);
} else if (appId === KNATIVE) {
if (!this.state.applications.knative.isEditingHostName) {
if (serverAppEntry.available_domains) {
this.state.applications.knative.availableDomains = serverAppEntry.available_domains;
}
if (!this.state.applications.knative.isEditingDomain) {
this.state.applications.knative.pagesDomain =
serverAppEntry.pages_domain || this.state.applications.knative.pagesDomain;
this.state.applications.knative.hostname =
serverAppEntry.hostname || this.state.applications.knative.hostname;
}

View file

@ -1,33 +1,37 @@
<script>
import { mapActions, mapState } from 'vuex';
import Popover from './popover.vue';
import eventHub from '../../notes/event_hub';
export default {
components: {
Popover,
},
computed: {
...mapState(['currentDefinition', 'currentDefinitionPosition']),
...mapState(['currentDefinition', 'currentDefinitionPosition', 'definitionPathPrefix']),
},
mounted() {
this.blobViewer = document.querySelector('.blob-viewer');
this.body = document.body;
eventHub.$on('showBlobInteractionZones', this.showBlobInteractionZones);
this.addGlobalEventListeners();
this.fetchData();
},
beforeDestroy() {
eventHub.$off('showBlobInteractionZones', this.showBlobInteractionZones);
this.removeGlobalEventListeners();
},
methods: {
...mapActions(['fetchData', 'showDefinition']),
...mapActions(['fetchData', 'showDefinition', 'showBlobInteractionZones']),
addGlobalEventListeners() {
if (this.blobViewer) {
this.blobViewer.addEventListener('click', this.showDefinition);
if (this.body) {
this.body.addEventListener('click', this.showDefinition);
}
},
removeGlobalEventListeners() {
if (this.blobViewer) {
this.blobViewer.removeEventListener('click', this.showDefinition);
if (this.body) {
this.body.removeEventListener('click', this.showDefinition);
}
},
},
@ -39,5 +43,6 @@ export default {
v-if="currentDefinition"
:position="currentDefinitionPosition"
:data="currentDefinition"
:definition-path-prefix="definitionPathPrefix"
/>
</template>

View file

@ -1,9 +1,9 @@
<script>
import { GlButton } from '@gitlab/ui';
import { GlDeprecatedButton } from '@gitlab/ui';
export default {
components: {
GlButton,
GlDeprecatedButton,
},
props: {
position: {
@ -14,6 +14,10 @@ export default {
type: Object,
required: true,
},
definitionPathPrefix: {
type: String,
required: true,
},
},
data() {
return {
@ -27,6 +31,11 @@ export default {
top: `${this.position.y + this.position.height}px`,
};
},
definitionPath() {
return (
this.data.definition_path && `${this.definitionPathPrefix}/${this.data.definition_path}`
);
},
},
watch: {
position: {
@ -67,10 +76,10 @@ export default {
{{ hover.value }}
</p>
</div>
<div v-if="data.definition_url" class="popover-body">
<gl-button :href="data.definition_url" target="_blank" class="w-100" variant="default">
<div v-if="definitionPath" class="popover-body">
<gl-deprecated-button :href="definitionPath" target="_blank" class="w-100" variant="default">
{{ __('Go to definition') }}
</gl-button>
</gl-deprecated-button>
</div>
</div>
</template>

View file

@ -5,10 +5,10 @@ import App from './components/app.vue';
Vue.use(Vuex);
export default () => {
export default initialData => {
const el = document.getElementById('js-code-navigation');
store.dispatch('setInitialData', el.dataset);
store.dispatch('setInitialData', initialData);
return new Vue({
el,

View file

@ -1,4 +1,4 @@
import api from '~/api';
import axios from '~/lib/utils/axios_utils';
import * as types from './mutation_types';
import { getCurrentHoverElement, setCurrentHoverElement, addInteractionClass } from '../utils';
@ -12,21 +12,25 @@ export default {
fetchData({ commit, dispatch, state }) {
commit(types.REQUEST_DATA);
api
.lsifData(state.projectPath, state.commitId, [state.blobPath])
.then(({ data }) => {
const dataForPath = data[state.blobPath];
const normalizedData = dataForPath.reduce((acc, d) => {
if (d.hover) {
acc[`${d.start_line}:${d.start_char}`] = d;
addInteractionClass(d);
}
return acc;
}, {});
state.blobs.forEach(({ path, codeNavigationPath }) => {
axios
.get(codeNavigationPath)
.then(({ data }) => {
const normalizedData = data.reduce((acc, d) => {
if (d.hover) {
acc[`${d.start_line}:${d.start_char}`] = d;
addInteractionClass(path, d);
}
return acc;
}, {});
commit(types.REQUEST_DATA_SUCCESS, normalizedData);
})
.catch(() => dispatch('requestDataError'));
commit(types.REQUEST_DATA_SUCCESS, { path, normalizedData });
})
.catch(() => dispatch('requestDataError'));
});
},
showBlobInteractionZones({ state }, path) {
Object.values(state.data[path]).forEach(d => addInteractionClass(path, d));
},
showDefinition({ commit, state }, { target: el }) {
let definition;
@ -40,15 +44,28 @@ export default {
getCurrentHoverElement().classList.remove('hll');
}
if (el.classList.contains('js-code-navigation') && !isCurrentElementPopoverOpen) {
const blobEl = el.closest('[data-path]');
if (!blobEl) {
commit(types.SET_CURRENT_DEFINITION, { definition, position });
return;
}
const data = state.data[blobEl.dataset.path];
if (!data) return;
if (el.closest('.js-code-navigation') && !isCurrentElementPopoverOpen) {
const { lineIndex, charIndex } = el.dataset;
const { x, y } = el.getBoundingClientRect();
position = {
x: el.offsetLeft,
y: el.offsetTop,
x: x || 0,
y: y + window.scrollY || 0,
height: el.offsetHeight,
};
definition = state.data[`${lineIndex}:${charIndex}`];
definition = data[`${lineIndex}:${charIndex}`];
el.classList.add('hll');

View file

@ -1,17 +1,16 @@
import * as types from './mutation_types';
export default {
[types.SET_INITIAL_DATA](state, { projectPath, commitId, blobPath }) {
state.projectPath = projectPath;
state.commitId = commitId;
state.blobPath = blobPath;
[types.SET_INITIAL_DATA](state, { blobs, definitionPathPrefix }) {
state.blobs = blobs;
state.definitionPathPrefix = definitionPathPrefix;
},
[types.REQUEST_DATA](state) {
state.loading = true;
},
[types.REQUEST_DATA_SUCCESS](state, data) {
[types.REQUEST_DATA_SUCCESS](state, { path, normalizedData }) {
state.loading = false;
state.data = data;
state.data = { ...state.data, [path]: normalizedData };
},
[types.REQUEST_DATA_ERROR](state) {
state.loading = false;

View file

@ -1,7 +1,5 @@
export default () => ({
projectPath: null,
commitId: null,
blobPath: null,
blobs: [],
loading: false,
data: null,
currentDefinition: null,

View file

@ -3,18 +3,25 @@ export const cachedData = new Map();
export const getCurrentHoverElement = () => cachedData.get('current');
export const setCurrentHoverElement = el => cachedData.set('current', el);
export const addInteractionClass = d => {
let charCount = 0;
const line = document.getElementById(`LC${d.start_line + 1}`);
const el = [...line.childNodes].find(({ textContent }) => {
if (charCount === d.start_char) return true;
charCount += textContent.length;
return false;
});
export const addInteractionClass = (path, d) => {
const lineNumber = d.start_line + 1;
const lines = document
.querySelector(`[data-path="${path}"]`)
.querySelectorAll(`.blob-content #LC${lineNumber}, .line_content:not(.old) #LC${lineNumber}`);
if (!lines?.length) return;
if (el) {
el.setAttribute('data-char-index', d.start_char);
el.setAttribute('data-line-index', d.start_line);
el.classList.add('cursor-pointer', 'code-navigation', 'js-code-navigation');
}
lines.forEach(line => {
let charCount = 0;
const el = [...line.childNodes].find(({ textContent }) => {
if (charCount === d.start_char) return true;
charCount += textContent.length;
return false;
});
if (el) {
el.setAttribute('data-char-index', d.start_char);
el.setAttribute('data-line-index', d.start_line);
el.classList.add('cursor-pointer', 'code-navigation', 'js-code-navigation');
}
});
};

View file

@ -1,5 +1,5 @@
<script>
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { GlDeprecatedButton, GlLoadingIcon } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import PipelinesService from '~/pipelines/services/pipelines_service';
import PipelineStore from '~/pipelines/stores/pipelines_store';
@ -12,7 +12,7 @@ import CIPaginationMixin from '~/vue_shared/mixins/ci_pagination_api_mixin';
export default {
components: {
TablePagination,
GlButton,
GlDeprecatedButton,
GlLoadingIcon,
},
mixins: [pipelinesMixin, CIPaginationMixin],
@ -156,7 +156,7 @@ export default {
<gl-loading-icon
v-if="isLoading"
:label="s__('Pipelines|Loading Pipelines')"
:size="3"
size="lg"
class="prepend-top-20"
/>
@ -171,7 +171,7 @@ export default {
<div v-else-if="shouldRenderTable" class="table-holder">
<div v-if="canRenderPipelineButton" class="nav justify-content-end">
<gl-button
<gl-deprecated-button
v-if="canRenderPipelineButton"
variant="success"
class="js-run-mr-pipeline prepend-top-10 btn-wide-on-xs"
@ -180,7 +180,7 @@ export default {
>
<gl-loading-icon v-if="state.isRunningMergeRequestPipeline" inline />
{{ s__('Pipelines|Run Pipeline') }}
</gl-button>
</gl-deprecated-button>
</div>
<pipelines-table-component

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