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

gitlab Debian release 12.9.2-1
This commit is contained in:
Pirate Praveen 2020-04-10 20:08:32 +05:30
commit 298f1c9f45
3894 changed files with 111111 additions and 50512 deletions

15
.editorconfig Normal file
View file

@ -0,0 +1,15 @@
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{js,json,vue,scss,rb,haml,yml}]
indent_size = 2
[*.{js,json,vue,scss,rb,haml,yml,md}]
indent_style = space
charset = utf-8

4
.gitignore vendored
View file

@ -30,6 +30,7 @@ eslint-report.html
/app/assets/javascripts/locale/**/app.js
/backups/*
/config/aws.yml
/config/cable.yml
/config/database*.yml
/config/gitlab.yml
/config/gitlab_ci.yml
@ -42,6 +43,7 @@ eslint-report.html
/config/redis.shared_state.yml
/config/unicorn.rb
/config/puma.rb
/config/puma_actioncable.rb
/config/secrets.yml
/config/sidekiq.yml
/config/registry.key
@ -54,7 +56,7 @@ eslint-report.html
/dump.rdb
/jsconfig.json
/log/*.log*
/node_modules/
/node_modules
/nohup.out
/public/assets/
/public/uploads.*

View file

@ -3,6 +3,7 @@ image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.1
stages:
- sync
- prepare
- fixtures
- test
- post-test
- review-prepare
@ -11,6 +12,29 @@ stages:
- post-qa
- pages
# always use `gitlab-org` runners
default:
tags:
- gitlab-org
workflow:
rules:
# If `$FORCE_GITLAB_CI` is set, create a pipeline.
- if: '$FORCE_GITLAB_CI'
# For merge requests, create a pipeline.
- if: '$CI_MERGE_REQUEST_IID'
# For `master` branch, create a pipeline (this includes on schedules, pushes, merges, etc.).
- if: '$CI_COMMIT_BRANCH == "master"'
# For tags, create a pipeline.
- if: '$CI_COMMIT_TAG'
# If `$GITLAB_INTERNAL` isn't set, don't create a pipeline.
- if: '$GITLAB_INTERNAL == null'
when: never
# For stable, auto-deploy, and security branches, create a pipeline.
- if: '$CI_COMMIT_BRANCH =~ /^[\d-]+-stable(-ee)?$/'
- if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
- if: '$CI_COMMIT_BRANCH =~ /^security\//'
variables:
RAILS_ENV: "test"
NODE_ENV: "test"

View file

@ -8,19 +8,29 @@
# Technical writing team are the default reviewers for all markdown docs
*.md @gl-docsteam
/doc/ @gl-docsteam
# Frontend maintainers should see everything in `app/assets/`
app/assets/ @gitlab-org/maintainers/frontend
*.scss @annabeldunstone @gitlab-org/maintainers/frontend
/scripts/frontend/ @gitlab-org/maintainers/frontend
*.js @gitlab-org/maintainers/frontend
/app/assets/ @gitlab-org/maintainers/frontend
/ee/app/assets/ @gitlab-org/maintainers/frontend
/spec/javascripts/ @gitlab-org/maintainers/frontend
/ee/spec/javascripts/ @gitlab-org/maintainers/frontend
/spec/frontend/ @gitlab-org/maintainers/frontend
/ee/spec/frontend/ @gitlab-org/maintainers/frontend
# Database maintainers should review changes in `db/`
db/ @gitlab-org/maintainers/database
lib/gitlab/background_migration/ @gitlab-org/maintainers/database
lib/gitlab/database/ @gitlab-org/maintainers/database
lib/gitlab/sql/ @gitlab-org/maintainers/database
lib/gitlab/github_import/ @gitlab-org/maintainers/database
/db/ @gitlab-org/maintainers/database
/ee/db/ @gitlab-org/maintainers/database
/lib/gitlab/background_migration/ @gitlab-org/maintainers/database
/ee/lib/ee/gitlab/background_migration/ @gitlab-org/maintainers/database
/lib/gitlab/database/ @gitlab-org/maintainers/database
/ee/lib/gitlab/database/ @gitlab-org/maintainers/database
/lib/gitlab/sql/ @gitlab-org/maintainers/database
/lib/gitlab/github_import/ @gitlab-org/maintainers/database
/app/finders/ @gitlab-org/maintainers/database
/ee/app/finders/ @gitlab-org/maintainers/database
# Feature specific owners
/ee/lib/gitlab/code_owners/ @reprazent
@ -38,11 +48,20 @@ lib/gitlab/github_import/ @gitlab-org/maintainers/database
# Engineering Productivity owned files
/.gitlab-ci.yml @gl-quality/eng-prod
/.gitlab/ci/ @gl-quality/eng-prod
/.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam
/.gitlab/ci/releases.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/delivery
/.gitlab/CODEOWNERS @gl-quality/eng-prod
Dangerfile @gl-quality/eng-prod
/danger/ @gl-quality/eng-prod
/lib/gitlab/danger/ @gl-quality/eng-prod
/scripts/ @gl-quality/eng-prod
/scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend
.editorconfig @gl-quality/eng-prod
# Delivery owner files
/.gitlab/ci/releases.gitlab-ci.yml @gitlab-org/delivery
# Telemetry owner files
/ee/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry
/ee/lib/ee/gitlab/usage_data.rb @gitlab-org/growth/telemetry
/lib/gitlab/grafana_embed_usage_data.rb @gitlab-org/growth/telemetry
/lib/gitlab/usage_data.rb @gitlab-org/growth/telemetry
/lib/gitlab/cycle_analytics/usage_data.rb @gitlab-org/growth/telemetry
/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry

View file

@ -1,13 +1,11 @@
.run-dev-fixtures:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-before_script
- .use-pg9
stage: test
needs: ["setup-test-env"]
dependencies: ["setup-test-env"]
variables:
FIXTURE_PATH: "db/fixtures/development"
SEED_CYCLE_ANALYTICS: "true"

View file

@ -1,6 +1,5 @@
.review-docs:
extends:
- .default-tags
- .default-retry
- .docs:rules:review-docs
allow_failure: true
@ -16,6 +15,7 @@
name: review-docs/$DOCS_GITLAB_REPO_SUFFIX-$CI_MERGE_REQUEST_IID
# DOCS_REVIEW_APPS_DOMAIN and DOCS_GITLAB_REPO_SUFFIX are CI variables
# Discussion: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/14236/diffs#note_40140693
auto_stop_in: 2 weeks
url: http://docs-preview-$DOCS_GITLAB_REPO_SUFFIX-$CI_MERGE_REQUEST_IID.$DOCS_REVIEW_APPS_DOMAIN/$DOCS_GITLAB_REPO_SUFFIX
on_stop: review-docs-cleanup
before_script:
@ -41,18 +41,17 @@ review-docs-cleanup:
docs lint:
extends:
- .default-tags
- .default-retry
- .docs:rules:docs-lint
image: "registry.gitlab.com/gitlab-org/gitlab-docs:docs-lint"
image: "registry.gitlab.com/gitlab-org/gitlab-docs:lint"
stage: test
dependencies: []
needs: []
script:
- scripts/lint-doc.sh
# Lint Markdown
- markdownlint --config .markdownlint.json 'doc/**/*.md'
# Prepare docs for build
- mv doc/ /tmp/gitlab-docs/content/$DOCS_GITLAB_REPO_SUFFIX
# The path must be 'ee/' because we have hardcoded links relying on it
# https://gitlab.com/gitlab-org/gitlab-docs/-/blob/887850752fc0e72856da6632db132f005ba77f16/content/index.erb#L44-63
- mv doc/ /tmp/gitlab-docs/content/ee
- cd /tmp/gitlab-docs
# Build HTML from Markdown
- bundle exec nanoc
@ -63,7 +62,6 @@ docs lint:
graphql-reference-verify:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-before_script

View file

@ -6,10 +6,10 @@
- tmp/cache/assets/sprockets
- tmp/cache/babel-loader
- tmp/cache/vue-loader
- tmp/cache/webpack-dlls
.gitlab:assets:compile-metadata:
extends:
- .default-tags
- .default-retry
- .default-before_script
- .assets-compile-cache
@ -63,7 +63,6 @@ gitlab:assets:compile pull-cache:
.compile-assets-metadata:
extends:
- .default-tags
- .default-retry
- .default-before_script
- .assets-compile-cache
@ -78,6 +77,7 @@ gitlab:assets:compile pull-cache:
SETUP_DB: "false"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
WEBPACK_VENDOR_DLL: "true"
cache:
key: "assets-compile:v9"
artifacts:
@ -118,15 +118,52 @@ compile-assets pull-cache as-if-foss:
policy: pull
key: "assets-compile:v9:foss"
.frontend-job-base:
.frontend-fixtures-base:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-before_script
- .use-pg9
stage: fixtures
needs:
- job: "setup-test-env"
artifacts: true
- job: "compile-assets pull-cache"
artifacts: true
script:
- date
- scripts/gitaly-test-spawn
- date
- bundle exec rake frontend:fixtures
artifacts:
name: frontend-fixtures
expire_in: 31d
when: always
paths:
- node_modules
- public/assets
- tmp/tests/frontend/
frontend-fixtures:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs
frontend-fixtures-as-if-foss:
extends:
- .frontend-fixtures-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
.frontend-job-base:
extends:
- .default-retry
- .default-cache
- .default-before_script
variables:
USE_BUNDLE_INSTALL: "false"
SETUP_DB: "false"
stage: test
needs: ["setup-test-env", "compile-assets pull-cache"]
.karma-base:
extends: .frontend-job-base
@ -136,14 +173,13 @@ compile-assets pull-cache as-if-foss:
script:
- export BABEL_ENV=coverage CHROME_LOG_FILE=chrome_debug.log
- date
- scripts/gitaly-test-spawn
- date
- bundle exec rake karma
- yarn karma
karma:
extends:
- .karma-base
- .frontend:rules:default-frontend-jobs
needs: ["frontend-fixtures"]
coverage: '/^Statements *: (\d+\.\d+%)/'
artifacts:
name: coverage-javascript
@ -161,25 +197,24 @@ karma-as-if-foss:
- .karma-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
.jest-base:
extends: .frontend-job-base
script:
- scripts/gitaly-test-spawn
- date
- bundle exec rake frontend:fixtures
- date
- yarn jest --ci --coverage
- yarn jest --ci --coverage --testSequencer ./scripts/frontend/parallel_ci_sequencer.js
cache:
key: jest
paths:
- tmp/jest/jest/
- tmp/cache/jest/
policy: pull-push
jest:
extends:
- .jest-base
- .frontend:rules:default-frontend-jobs
needs: ["frontend-fixtures"]
artifacts:
name: coverage-frontend
expire_in: 31d
@ -190,18 +225,38 @@ jest:
- tmp/tests/frontend/
reports:
junit: junit_jest.xml
parallel: 2
jest-as-if-foss:
extends:
- .jest-base
- .frontend:rules:default-frontend-jobs-as-if-foss
- .as-if-foss
needs: ["frontend-fixtures-as-if-foss"]
cache:
policy: pull
coverage-frontend:
extends:
- .default-retry
- .frontend:rules:default-frontend-jobs
needs: ["jest"]
stage: post-test
before_script:
- yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline
script:
- yarn node scripts/frontend/merge_coverage_frontend.js
artifacts:
name: coverage-frontend
expire_in: 31d
paths:
- coverage-frontend/
cache:
paths:
- .yarn-cache/
.qa-frontend-node:
extends:
- .default-tags
- .default-retry
- .default-cache
- .frontend:rules:qa-frontend-node
@ -218,10 +273,6 @@ jest-as-if-foss:
- date
- yarn run webpack-prod
qa-frontend-node:8:
extends: .qa-frontend-node
image: node:carbon
qa-frontend-node:10:
extends: .qa-frontend-node
image: node:dubnium
@ -233,7 +284,6 @@ qa-frontend-node:latest:
webpack-dev-server:
extends:
- .default-tags
- .default-retry
- .default-cache
- .frontend:rules:default-frontend-jobs

View file

@ -1,7 +1,3 @@
.default-tags:
tags:
- gitlab-org
.default-retry:
retry:
max: 2 # This is confusing but this means "3 runs at max".
@ -50,6 +46,15 @@
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.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"
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
.use-pg9-ee:
services:
- name: postgres:9.6.17
@ -69,6 +74,16 @@
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.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"
services:
- name: postgres:11.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
- name: elasticsearch:6.4.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.as-if-foss:
variables:
FOSS_ONLY: '1'

View file

@ -1,6 +1,5 @@
.only-code-memory-job-base:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-before_script
@ -8,6 +7,10 @@
memory-static:
extends: .only-code-memory-job-base
stage: test
needs:
- job: setup-test-env
artifacts: true
variables:
SETUP_DB: "false"
script:
@ -36,6 +39,12 @@ memory-on-boot:
extends:
- .only-code-memory-job-base
- .use-pg10
stage: test
needs:
- job: setup-test-env
artifacts: true
- job: compile-assets pull-cache
artifacts: true
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
@ -44,8 +53,7 @@ memory-on-boot:
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
script:
# Both bootsnap and derailed monkey-patch Kernel#require, which leads to circular dependency
- ENABLE_BOOTSNAP=false PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> 'tmp/memory_on_boot.txt'
- PATH_TO_HIT="/users/sign_in" CUT_OFF=0.3 bundle exec derailed exec perf:mem >> 'tmp/memory_on_boot.txt'
- scripts/generate-memory-metrics-on-boot tmp/memory_on_boot.txt >> 'tmp/memory_on_boot_metrics.txt'
artifacts:
paths:

View file

@ -1,11 +1,10 @@
pages:
extends:
- .default-tags
- .default-retry
- .default-cache
- .pages:rules
stage: pages
dependencies: ["coverage", "karma", "gitlab:assets:compile pull-cache"]
dependencies: ["rspec:coverage", "karma", "gitlab:assets:compile pull-cache"]
script:
- mv public/ .public/
- mkdir public/

View file

@ -1,9 +1,8 @@
.qa-job-base:
extends:
- .default-tags
- .default-retry
stage: test
dependencies: []
needs: []
cache:
key: "qa-framework-jobs:v1"
paths:

View file

@ -7,7 +7,6 @@
.rails-job-base:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-before_script
@ -202,7 +201,7 @@ gitlab:setup:
paths:
- log/development.log
coverage:
rspec:coverage:
extends:
- .rails-job-base
- .rails:rules:ee-and-foss
@ -238,6 +237,12 @@ rspec quarantine pg9:
- .rails:rules:master-refs-code-backstage
- .use-pg10
rspec migration pg10:
extends:
- .rspec-base-pg10
- .rspec-base-migration
parallel: 2
rspec unit pg10:
extends: .rspec-base-pg10
parallel: 20
@ -252,6 +257,34 @@ rspec system pg10:
# master-only jobs #
####################
############################
# nightly master-only jobs #
.rspec-base-pg11:
extends:
- .rspec-base
- .rails:rules:nightly-master-refs-code-backstage
- .use-pg11
rspec migration pg11:
extends:
- .rspec-base-pg11
- .rspec-base-migration
parallel: 2
rspec unit pg11:
extends: .rspec-base-pg11
parallel: 20
rspec integration pg11:
extends: .rspec-base-pg11
parallel: 8
rspec system pg11:
extends: .rspec-base-pg11
parallel: 24
# nightly master-only jobs #
############################
#########################
# ee + master-only jobs #
rspec-ee quarantine pg9:

View file

@ -12,6 +12,7 @@ code_quality:
- .default-retry
- .reports:rules:code_quality
stage: test
needs: []
image: docker:stable
allow_failure: true
services:
@ -19,7 +20,7 @@ code_quality:
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/security-products/codequality:0.85.6"
CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/ci-cd/codequality:0.85.9"
script:
- |
if ! docker info &>/dev/null; then
@ -39,7 +40,6 @@ code_quality:
paths:
- gl-code-quality-report.json # GitLab-specific
expire_in: 1 week # GitLab-specific
dependencies: []
# We need to duplicate this job's definition because it seems it's impossible to
# override an included `only.refs`.
@ -52,7 +52,7 @@ sast:
- .reports:rules:sast
stage: test
allow_failure: true
dependencies: [] # GitLab-specific
needs: []
artifacts:
paths:
- gl-sast-report.json # GitLab-specific
@ -90,6 +90,7 @@ dependency_scanning:
- .default-retry
- .reports:rules:dependency_scanning
stage: test
needs: []
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
@ -148,7 +149,6 @@ dependency_scanning:
reports:
dependency_scanning: gl-dependency-scanning-report.json
expire_in: 1 week # GitLab-specific
dependencies: []
# We need to duplicate this job's definition because it seems it's impossible to
# override an included `only.refs`.
@ -168,6 +168,7 @@ dast:
# DAST_USERNAME: "root"
# DAST_USERNAME_FIELD: "user[login]"
# DAST_PASSWORD_FIELD: "user[passowrd]"
DAST_VERSION: 1
allow_failure: true
script:
- 'export DAST_WEBSITE="${DAST_WEBSITE:-$(cat environment_url.txt)}"'

View file

@ -1,6 +1,5 @@
.review-docker:
extends:
- .default-tags
- .default-retry
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine-ruby-2.6
services:
@ -29,7 +28,6 @@ build-qa-image:
review-cleanup:
extends:
- .default-tags
- .default-retry
- .review:rules:review-cleanup
stage: prepare
@ -46,7 +44,6 @@ review-cleanup:
review-build-cng:
extends:
- .default-tags
- .default-retry
- .review:rules:mr-and-schedule
image: ruby:2.6-alpine
@ -63,18 +60,18 @@ review-build-cng:
.review-workflow-base:
extends:
- .default-tags
- .default-retry
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
variables:
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
GITLAB_HELM_CHART_REF: "v2.5.1"
GITLAB_HELM_CHART_REF: "v2.6.8"
GITLAB_EDITION: "ce"
environment:
name: review/${CI_COMMIT_REF_NAME}
url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
on_stop: review-stop
auto_stop_in: 48 hours
review-deploy:
extends:
@ -84,6 +81,7 @@ review-deploy:
needs:
- job: review-build-cng
artifacts: false
resource_group: "review/${CI_COMMIT_REF_NAME}"
allow_failure: true
before_script:
- '[[ ! -d "ee/" ]] || export GITLAB_EDITION="ee"'
@ -215,7 +213,6 @@ review-performance:
parallel-spec-reports:
extends:
- .default-tags
- .review:rules:mr-only-manual
image: ruby:2.6-alpine
stage: post-qa
@ -242,15 +239,14 @@ parallel-spec-reports:
danger-review:
extends:
- .default-tags
- .default-retry
- .default-cache
- .review:rules:danger
image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
stage: test
dependencies: []
needs: []
script:
- git version
- node --version
- yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline
- danger --fail-on-errors=true
- danger --fail-on-errors=true --verbose

View file

@ -22,6 +22,9 @@
.if-merge-request: &if-merge-request
if: '$CI_MERGE_REQUEST_IID'
.if-nightly-master-schedule: &if-nightly-master-schedule
if: '$NIGHTLY && $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule"'
.if-dot-com-gitlab-org-schedule: &if-dot-com-gitlab-org-schedule
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org" && $CI_PIPELINE_SOURCE == "schedule"'
@ -77,6 +80,9 @@
- "{,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/**/*"
@ -267,6 +273,9 @@
rules:
- <<: *if-master-refs
when: on_success
- <<: *if-merge-request
changes: *frontend-dependency-patterns
when: on_success
################
# Memory rules #
@ -343,6 +352,12 @@
changes: *code-backstage-patterns
when: on_success
.rails:rules:nightly-master-refs-code-backstage:
rules:
- <<: *if-nightly-master-schedule
changes: *code-backstage-patterns
when: on_success
.rails:rules:ee-only:
rules:
- <<: *if-not-ee
@ -484,13 +499,6 @@
changes: *code-backstage-patterns
when: on_success
.test-metadata:rules:flaky-examples-check:
rules:
- <<: *if-merge-request
changes: *code-backstage-patterns
when: on_success
##############
# YAML rules #
##############

View file

@ -2,7 +2,6 @@
# rubygems.org in the future.
cache gems:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-before_script
@ -21,14 +20,14 @@ cache gems:
.minimal-job:
extends:
- .default-tags
- .default-retry
dependencies: []
needs: []
gitlab_git_test:
extends:
- .minimal-job
- .setup:rules:gitlab_git_test
stage: test
script:
- spec/support/prepare-gitlab-git-test-for-commit --check-for-changes
@ -36,5 +35,6 @@ no_ee_check:
extends:
- .minimal-job
- .setup:rules:no_ee_check
stage: test
script:
- scripts/no-ee-check

View file

@ -37,22 +37,3 @@ update-tests-metadata:
- retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document
- source scripts/rspec_helpers.sh
- update_tests_metadata
flaky-examples-check:
extends:
- .default-tags
- .default-retry
- .test-metadata:rules:flaky-examples-check
image: ruby:2.6-alpine
stage: post-test
variables:
NEW_FLAKY_SPECS_REPORT: rspec_flaky/report-new.json
allow_failure: true
artifacts:
expire_in: 30d
paths:
- rspec_flaky/
script:
- '[[ -f $NEW_FLAKY_SPECS_REPORT ]] || echo "{}" > ${NEW_FLAKY_SPECS_REPORT}'
- scripts/merge-reports ${NEW_FLAKY_SPECS_REPORT} rspec_flaky/new_*_*.json
- scripts/flaky_examples/detect-new-flaky-examples $NEW_FLAKY_SPECS_REPORT

View file

@ -2,11 +2,11 @@
# This uses rules from project root `.yamllint`.
lint-ci-gitlab:
extends:
- .default-tags
- .default-retry
- .yaml:rules
image: sdesbure/yamllint:latest
dependencies: []
stage: test
needs: []
variables:
LINT_PATHS: .gitlab-ci.yml .gitlab/ci lib/gitlab/ci/templates changelogs
script:

View file

@ -15,6 +15,8 @@
* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sidney-systems-administrator)
* [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/ -->
@ -57,6 +59,10 @@ See the test engineering planning process and reach out to your counterpart Soft
<!-- Which leads to: in which enterprise tier should this feature go? See https://about.gitlab.com/handbook/product/pricing/#four-tiers -->
### Is this a cross-stage feature?
<!-- Communicate if this change will affect multiple Stage Groups or product areas. We recommend always start with the assumption that a feature request will have an impact into another Group. Loop in the most relevant PM and Product Designer from that Group to provide strategic support to help align the Group's broader plan and vision, as well as to avoid UX and technical debt. https://about.gitlab.com/handbook/product/#cross-stage-features -->
### Links / references
/label ~feature

View file

@ -1,6 +1,6 @@
## Problem Statement
<!-- What is the problem we hope to validate and solve? -->
<!-- What is the problem we hope to validate? Reference how to write a real customer problem statement at https://productcoalition.com/how-to-write-a-good-customer-problem-statement-a815f80189ba for guidance. -->
## Reach

View file

@ -40,7 +40,10 @@ Attach the screenshot and HTML snapshot of the page from the job's artifacts:
/due in 2 weeks
<!-- Base labels. -->
/label ~Quality ~QA ~bug ~S1
/label ~Quality ~QA ~test
<!-- Test failure type label, please use just one.-->
/label ~"failure::broken-test" ~"failure::flaky-test" ~"failure::stale-test" ~"failure::test-environment" ~"failure::investigating"
<!--
Choose the stage that appears in the test path, e.g. ~"devops::create" for

View file

@ -4,6 +4,10 @@
Set the title to: `Security Release: 12.2.X, 12.1.X, and 12.0.X`
-->
:warning: **Only Release Managers and members of the AppSec team can edit the description of this issue**
-------
## Releases tasks
- https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/release-manager.md
@ -12,31 +16,17 @@ Set the title to: `Security Release: 12.2.X, 12.1.X, and 12.0.X`
## Version issues:
* 12.2.X: {release task link}
* 12.1.X: {release task link}
* 12.0.X: {release task link}
12.2.X, 12.1.X, 12.0.X: {release task link}
## Issues in GitLab Security
* {https://gitlab.com/gitlab-org/security/gitlab/issues/ link}
To include your issue and merge requests in this Security Release, please mark
your security issues as related to this release tracking issue. You can do this
in the "Linked issues" section below this issue description.
| Version | MR |
|---------|----|
| 12.2 | {https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests link} |
| 12.1 | {https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests link} |
| 12.0 | {https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests link} |
| master | {https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests link} |
## Issues in Omnibus-GitLab
* {https://gitlab.com/gitlab-org/security/gitlab/issues/ link}
| Version | MR |
|---------|----|
| 12.2 | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
| 12.1 | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
| 12.0 | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
| master | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
:warning: If your security issues are not marked as related to this release
tracking issue, their merge requests may not be included in the security
release.
## QA
{QA issue link}
@ -49,5 +39,5 @@ GitLab.com: {https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ link}
## Email notification
{https://gitlab.com/gitlab-com/marketing/general/issues/ link}
/label ~security
/label ~security ~"upcoming security release"
/confidential

View file

@ -9,9 +9,12 @@
<!-- Outline the tasks with issues that you need evaluate as a part of the implementation issue -->
- [ ] Add task
- [ ] Add task
- [ ] Add task
- [ ] Determine feasibility of the feature
- [ ] Create issue for implementation or update existing implementation issue description with implementation proposal
- [ ] Set weight on implementation issue
- [ ] If weight is greater than 5, break issue into smaller issues
- [ ] Add task
- [ ] Add task
### Risks and Implementation Considerations

View file

@ -1,5 +1,6 @@
<!-- Follow the documentation workflow https://docs.gitlab.com/ee/development/documentation/workflow.html -->
<!-- Additional information is located at https://docs.gitlab.com/ee/development/documentation/ -->
<!-- To find the designated Tech Writer for the stage/group, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers -->
<!-- Mention "documentation" or "docs" in the MR title -->
<!-- For changing documentation location use the "Change documentation location" template -->

View file

@ -12,7 +12,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
## Developer checklist
- [ ] **Make sure this merge request mentions the [GitLab Security] issue it belongs to (i.e. `Related to <issue_id>`).**
- [ ] **On "Related issues" section, write down the [GitLab Security] issue it belongs to (i.e. `Related to <issue_id>`).**
- [ ] Merge request targets `master`, or `X-Y-stable` for backports.
- [ ] Milestone is set for the version this merge request applies to. A closed milestone can be assigned via [quick actions].
- [ ] Title of this merge request is the same as for all backports.

View file

@ -1,13 +1,13 @@
# This configuration was generated by
# `haml-lint --auto-gen-config`
# on 2019-05-07 19:04:08 +0100 using Haml-Lint version 0.30.0.
# on 2020-03-04 13:16:29 +0100 using Haml-Lint version 0.34.0.
# The point is for the user to remove these configuration records
# one by one as the lints are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of Haml-Lint, may require this file to be generated again.
linters:
# Offense count: 2075
# Offense count: 1646
NoPlainNodes:
enabled: true
exclude:
@ -19,13 +19,11 @@ linters:
- 'app/views/admin/application_settings/_gitaly.html.haml'
- 'app/views/admin/application_settings/_influx.html.haml'
- 'app/views/admin/application_settings/_ip_limits.html.haml'
- 'app/views/admin/application_settings/_logging.html.haml'
- 'app/views/admin/application_settings/_performance.html.haml'
- 'app/views/admin/application_settings/_plantuml.html.haml'
- 'app/views/admin/application_settings/_prometheus.html.haml'
- 'app/views/admin/application_settings/_realtime.html.haml'
- 'app/views/admin/application_settings/_repository_check.html.haml'
- 'app/views/admin/application_settings/_repository_storage.html.haml'
- 'app/views/admin/application_settings/_signin.html.haml'
- 'app/views/admin/application_settings/_signup.html.haml'
- 'app/views/admin/application_settings/_spam.html.haml'
@ -42,13 +40,11 @@ linters:
- 'app/views/admin/broadcast_messages/index.html.haml'
- 'app/views/admin/dashboard/index.html.haml'
- 'app/views/admin/deploy_keys/new.html.haml'
- 'app/views/admin/groups/show.html.haml'
- 'app/views/admin/health_check/show.html.haml'
- 'app/views/admin/hook_logs/_index.html.haml'
- 'app/views/admin/hook_logs/show.html.haml'
- 'app/views/admin/hooks/_form.html.haml'
- 'app/views/admin/hooks/edit.html.haml'
- 'app/views/admin/hooks/index.html.haml'
- 'app/views/admin/labels/_form.html.haml'
- 'app/views/admin/logs/show.html.haml'
- 'app/views/admin/projects/_projects.html.haml'
@ -78,8 +74,6 @@ linters:
- 'app/views/dashboard/milestones/index.html.haml'
- 'app/views/dashboard/projects/_blank_state_admin_welcome.html.haml'
- 'app/views/dashboard/projects/_blank_state_welcome.html.haml'
- 'app/views/dashboard/projects/_zero_authorized_projects.html.haml'
- 'app/views/dashboard/snippets/index.html.haml'
- 'app/views/dashboard/todos/_todo.html.haml'
- 'app/views/dashboard/todos/index.html.haml'
- 'app/views/devise/confirmations/almost_there.haml'
@ -97,12 +91,10 @@ linters:
- 'app/views/devise/sessions/two_factor.html.haml'
- 'app/views/devise/shared/_omniauth_box.html.haml'
- 'app/views/devise/shared/_sign_in_link.html.haml'
- 'app/views/devise/shared/_signup_box.html.haml'
- 'app/views/devise/shared/_tabs_normal.html.haml'
- 'app/views/discussions/_discussion.html.haml'
- 'app/views/discussions/_headline.html.haml'
- 'app/views/discussions/_notes.html.haml'
- 'app/views/discussions/_resolve_all.html.haml'
- 'app/views/doorkeeper/applications/_delete_form.html.haml'
- 'app/views/doorkeeper/authorized_applications/_delete_form.html.haml'
- 'app/views/errors/encoding.html.haml'
@ -114,8 +106,6 @@ linters:
- 'app/views/events/event/_push.html.haml'
- 'app/views/groups/_create_chat_team.html.haml'
- 'app/views/groups/_group_admin_settings.html.haml'
- 'app/views/groups/group_members/_new_group_member.html.haml'
- 'app/views/groups/group_members/index.html.haml'
- 'app/views/groups/labels/edit.html.haml'
- 'app/views/groups/labels/new.html.haml'
- 'app/views/groups/milestones/edit.html.haml'
@ -130,29 +120,25 @@ linters:
- 'app/views/help/instance_configuration.html.haml'
- 'app/views/help/instance_configuration/_gitlab_ci.html.haml'
- 'app/views/help/instance_configuration/_gitlab_pages.html.haml'
- 'app/views/help/instance_configuration/_ssh_info.html.haml'
- 'app/views/help/ui.html.haml'
- 'app/views/import/bitbucket/status.html.haml'
- 'app/views/import/bitbucket_server/status.html.haml'
- 'app/views/instance_statistics/cohorts/_cohorts_table.html.haml'
- 'app/views/instance_statistics/cohorts/_usage_ping.html.haml'
- 'app/views/invites/show.html.haml'
- 'app/views/layouts/_mailer.html.haml'
- 'app/views/layouts/header/_default.html.haml'
- 'app/views/layouts/header/_new_dropdown.haml'
- 'app/views/layouts/mailer/devise.html.haml'
- 'app/views/layouts/nav/sidebar/_profile.html.haml'
- 'app/views/layouts/notify.html.haml'
- 'app/views/notify/_failed_builds.html.haml'
- 'app/views/notify/_reassigned_issuable_email.html.haml'
- 'app/views/notify/_removal_notification.html.haml'
- 'app/views/notify/_successful_pipeline.html.haml'
- 'app/views/notify/autodevops_disabled_email.html.haml'
- 'app/views/notify/changed_milestone_email.html.haml'
- 'app/views/notify/import_issues_csv_email.html.haml'
- 'app/views/notify/issue_moved_email.html.haml'
- 'app/views/notify/member_access_denied_email.html.haml'
- 'app/views/notify/member_invite_accepted_email.html.haml'
- 'app/views/notify/member_invite_declined_email.html.haml'
- 'app/views/notify/member_invited_email.html.haml'
- 'app/views/notify/new_gpg_key_email.html.haml'
- 'app/views/notify/new_mention_in_issue_email.html.haml'
@ -163,7 +149,6 @@ linters:
- 'app/views/notify/pages_domain_verification_failed_email.html.haml'
- 'app/views/notify/pages_domain_verification_succeeded_email.html.haml'
- 'app/views/notify/pipeline_failed_email.html.haml'
- 'app/views/notify/pipeline_success_email.html.haml'
- 'app/views/notify/project_was_exported_email.html.haml'
- 'app/views/notify/project_was_moved_email.html.haml'
- 'app/views/notify/project_was_not_exported_email.html.haml'
@ -172,31 +157,11 @@ linters:
- 'app/views/notify/removed_milestone_issue_email.html.haml'
- 'app/views/notify/removed_milestone_merge_request_email.html.haml'
- 'app/views/notify/repository_push_email.html.haml'
- 'app/views/peek/views/_gc.html.haml'
- 'app/views/peek/views/_redis.html.haml'
- 'app/views/peek/views/_sidekiq.html.haml'
- 'app/views/profiles/_event_table.html.haml'
- 'app/views/profiles/active_sessions/_active_session.html.haml'
- 'app/views/profiles/active_sessions/index.html.haml'
- 'app/views/profiles/audit_log.html.haml'
- 'app/views/profiles/chat_names/_chat_name.html.haml'
- 'app/views/profiles/chat_names/index.html.haml'
- 'app/views/profiles/chat_names/new.html.haml'
- 'app/views/profiles/emails/index.html.haml'
- 'app/views/profiles/gpg_keys/_key.html.haml'
- 'app/views/profiles/gpg_keys/index.html.haml'
- 'app/views/profiles/keys/_key.html.haml'
- 'app/views/profiles/keys/_key_details.html.haml'
- 'app/views/profiles/keys/index.html.haml'
- 'app/views/profiles/notifications/show.html.haml'
- 'app/views/profiles/passwords/edit.html.haml'
- 'app/views/profiles/personal_access_tokens/index.html.haml'
- 'app/views/profiles/preferences/show.html.haml'
- 'app/views/profiles/show.html.haml'
- 'app/views/profiles/two_factor_auths/_codes.html.haml'
- 'app/views/profiles/two_factor_auths/codes.html.haml'
- 'app/views/profiles/two_factor_auths/create.html.haml'
- 'app/views/profiles/two_factor_auths/show.html.haml'
- 'app/views/projects/_bitbucket_import_modal.html.haml'
- 'app/views/projects/_customize_workflow.html.haml'
- 'app/views/projects/_deletion_failed.html.haml'
@ -206,10 +171,8 @@ linters:
- 'app/views/projects/_import_project_pane.html.haml'
- 'app/views/projects/_issuable_by_email.html.haml'
- 'app/views/projects/_md_preview.html.haml'
- 'app/views/projects/_new_project_fields.html.haml'
- 'app/views/projects/_readme.html.haml'
- 'app/views/projects/artifacts/_artifact.html.haml'
- 'app/views/projects/artifacts/_search_bar.html.haml'
- 'app/views/projects/artifacts/_tree_file.html.haml'
- 'app/views/projects/artifacts/browse.html.haml'
- 'app/views/projects/blame/_age_map_legend.html.haml'
@ -219,7 +182,6 @@ linters:
- 'app/views/projects/blob/_new_dir.html.haml'
- 'app/views/projects/blob/_remove.html.haml'
- 'app/views/projects/blob/_render_error.html.haml'
- 'app/views/projects/blob/_template_selectors.html.haml'
- 'app/views/projects/blob/_upload.html.haml'
- 'app/views/projects/blob/edit.html.haml'
- 'app/views/projects/blob/new.html.haml'
@ -244,8 +206,6 @@ linters:
- 'app/views/projects/deploy_keys/_form.html.haml'
- 'app/views/projects/deploy_keys/_index.html.haml'
- 'app/views/projects/deploy_keys/edit.html.haml'
- 'app/views/projects/deploy_tokens/_revoke_modal.html.haml'
- 'app/views/projects/deploy_tokens/_table.html.haml'
- 'app/views/projects/deployments/_deployment.html.haml'
- 'app/views/projects/diffs/_file_header.html.haml'
- 'app/views/projects/diffs/_replaced_image_diff.html.haml'
@ -254,10 +214,8 @@ linters:
- 'app/views/projects/environments/show.html.haml'
- 'app/views/projects/forks/error.html.haml'
- 'app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml'
- 'app/views/projects/graphs/charts.html.haml'
- 'app/views/projects/hook_logs/_index.html.haml'
- 'app/views/projects/hook_logs/show.html.haml'
- 'app/views/projects/hooks/_index.html.haml'
- 'app/views/projects/hooks/edit.html.haml'
- 'app/views/projects/imports/new.html.haml'
- 'app/views/projects/imports/show.html.haml'
@ -294,20 +252,9 @@ linters:
- 'app/views/projects/merge_requests/widget/open/_error.html.haml'
- 'app/views/projects/mirrors/_regenerate_public_ssh_key_confirm_modal.html.haml'
- 'app/views/projects/mirrors/_ssh_host_keys.html.haml'
- 'app/views/projects/new.html.haml'
- 'app/views/projects/no_repo.html.haml'
- 'app/views/projects/pages/_access.html.haml'
- 'app/views/projects/pages/_destroy.haml'
- 'app/views/projects/pages/_https_only.html.haml'
- 'app/views/projects/pages/_list.html.haml'
- 'app/views/projects/pages/_no_domains.html.haml'
- 'app/views/projects/pages/_use.html.haml'
- 'app/views/projects/pages/show.html.haml'
- 'app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml'
- 'app/views/projects/pipelines/_info.html.haml'
- 'app/views/projects/pipelines/charts/_pipelines.haml'
- 'app/views/projects/protected_branches/shared/_branches_list.html.haml'
- 'app/views/projects/protected_branches/shared/_create_protected_branch.html.haml'
- 'app/views/projects/protected_branches/shared/_dropdown.html.haml'
- 'app/views/projects/protected_branches/shared/_index.html.haml'
- 'app/views/projects/protected_branches/shared/_matching_branch.html.haml'
@ -325,7 +272,6 @@ linters:
- 'app/views/projects/runners/_shared_runners.html.haml'
- 'app/views/projects/runners/edit.html.haml'
- 'app/views/projects/services/_form.html.haml'
- 'app/views/projects/services/_index.html.haml'
- 'app/views/projects/services/mattermost_slash_commands/_detailed_help.html.haml'
- 'app/views/projects/services/mattermost_slash_commands/_help.html.haml'
- 'app/views/projects/services/prometheus/_metrics.html.haml'
@ -338,43 +284,35 @@ linters:
- 'app/views/projects/tags/releases/edit.html.haml'
- 'app/views/projects/tree/_tree_row.html.haml'
- 'app/views/projects/tree/_truncated_notice_tree_row.html.haml'
- 'app/views/projects/triggers/_content.html.haml'
- 'app/views/projects/triggers/_form.html.haml'
- 'app/views/projects/triggers/_index.html.haml'
- 'app/views/projects/triggers/_trigger.html.haml'
- 'app/views/projects/triggers/edit.html.haml'
- 'app/views/projects/wikis/_new.html.haml'
- 'app/views/projects/wikis/_pages_wiki_page.html.haml'
- 'app/views/projects/wikis/edit.html.haml'
- 'app/views/projects/wikis/history.html.haml'
- 'app/views/repository_check_mailer/notify.html.haml'
- 'app/views/search/_form.html.haml'
- 'app/views/search/results/_issue.html.haml'
- 'app/views/search/results/_note.html.haml'
- 'app/views/search/results/_snippet_blob.html.haml'
- 'app/views/search/results/_snippet_title.html.haml'
- 'app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml'
- 'app/views/shared/_commit_message_container.html.haml'
- 'app/views/shared/_confirm_modal.html.haml'
- 'app/views/shared/_confirm_fork_modal.html.haml'
- 'app/views/shared/_confirm_modal.html.haml'
- 'app/views/shared/_delete_label_modal.html.haml'
- 'app/views/shared/_group_form.html.haml'
- 'app/views/shared/_group_tips.html.haml'
- 'app/views/shared/_milestone_expired.html.haml'
- 'app/views/shared/_no_password.html.haml'
- 'app/views/shared/_no_ssh.html.haml'
- 'app/views/shared/_outdated_browser.html.haml'
- 'app/views/shared/_personal_access_tokens_created_container.html.haml'
- 'app/views/shared/_personal_access_tokens_table.html.haml'
- 'app/views/shared/_ping_consent.html.haml'
- 'app/views/shared/_project_limit.html.haml'
- 'app/views/shared/_service_settings.html.haml'
- 'app/views/shared/boards/components/_board.html.haml'
- 'app/views/shared/boards/components/_sidebar.html.haml'
- 'app/views/shared/boards/components/sidebar/_due_date.html.haml'
- 'app/views/shared/boards/components/sidebar/_labels.html.haml'
- 'app/views/shared/boards/components/sidebar/_milestone.html.haml'
- 'app/views/shared/empty_states/_priority_labels.html.haml'
- 'app/views/shared/deploy_tokens/_revoke_modal.html.haml'
- 'app/views/shared/hook_logs/_content.html.haml'
- 'app/views/shared/issuable/_assignees.html.haml'
- 'app/views/shared/issuable/_board_create_list_dropdown.html.haml'
@ -399,57 +337,42 @@ linters:
- 'app/views/shared/notifications/_button.html.haml'
- 'app/views/shared/notifications/_custom_notifications.html.haml'
- 'app/views/shared/notifications/_new_button.html.haml'
- 'app/views/shared/notifications/_notification_dropdown.html.haml'
- 'app/views/shared/plugins/_index.html.haml'
- 'app/views/shared/projects/_dropdown.html.haml'
- 'app/views/shared/projects/_list.html.haml'
- 'app/views/shared/projects/_project.html.haml'
- 'app/views/shared/runners/_runner_description.html.haml'
- 'app/views/shared/runners/show.html.haml'
- 'app/views/shared/snippets/_embed.html.haml'
- 'app/views/shared/snippets/_header.html.haml'
- 'app/views/shared/snippets/_snippet.html.haml'
- 'app/views/shared/tokens/_scopes_list.html.haml'
- 'app/views/shared/web_hooks/_form.html.haml'
- 'app/views/shared/web_hooks/_hook.html.haml'
- 'app/views/shared/web_hooks/_test_button.html.haml'
- 'app/views/u2f/_authenticate.html.haml'
- 'app/views/u2f/_register.html.haml'
- 'app/views/users/_deletion_guidance.html.haml'
- 'ee/app/views/admin/_namespace_plan_info.html.haml'
- 'ee/app/views/admin/application_settings/_templates.html.haml'
- 'ee/app/views/admin/audit_logs/index.html.haml'
- 'ee/app/views/admin/dashboard/stats.html.haml'
- 'ee/app/views/admin/emails/show.html.haml'
- 'ee/app/views/admin/geo/nodes/edit.html.haml'
- 'ee/app/views/admin/geo/nodes/new.html.haml'
- 'ee/app/views/admin/geo/projects/_registry_failed.html.haml'
- 'ee/app/views/admin/geo/projects/_registry_never.html.haml'
- 'ee/app/views/admin/licenses/_breakdown.html.haml'
- 'ee/app/views/admin/licenses/_upload_trial_license.html.haml'
- 'ee/app/views/admin/licenses/missing.html.haml'
- 'ee/app/views/admin/licenses/new.html.haml'
- 'ee/app/views/admin/licenses/show.html.haml'
- 'ee/app/views/admin/monitoring/ee/_nav.html.haml'
- 'ee/app/views/admin/projects/_shared_runner_status.html.haml'
- 'ee/app/views/admin/push_rules/show.html.haml'
- 'ee/app/views/admin/users/_auditor_access_level_radio.html.haml'
- 'ee/app/views/admin/users/_auditor_user_badge.html.haml'
- 'ee/app/views/admin/users/_limits.html.haml'
- 'ee/app/views/admin/users/_user_detail_note.html.haml'
- 'ee/app/views/dashboard/projects/_blank_state_ee_trial.html.haml'
- 'ee/app/views/errors/kerberos_denied.html.haml'
- 'ee/app/views/groups/analytics/show.html.haml'
- 'ee/app/views/groups/audit_events/index.html.haml'
- 'ee/app/views/groups/ee/_settings_nav.html.haml'
- 'ee/app/views/groups/epics/_epic.html.haml'
- 'ee/app/views/groups/group_members/_ldap_sync.html.haml'
- 'ee/app/views/groups/group_members/_sync_button.html.haml'
- 'ee/app/views/groups/hooks/edit.html.haml'
- 'ee/app/views/groups/hooks/index.html.haml'
- 'ee/app/views/groups/ldap_group_links/index.html.haml'
- 'ee/app/views/groups/pipeline_quota/index.html.haml'
- 'ee/app/views/jira_connect/subscriptions/index.html.haml'
- 'ee/app/views/layouts/jira_connect.html.haml'
- 'ee/app/views/layouts/nav/ee/_epic_link.html.haml'
- 'ee/app/views/layouts/nav/ee/admin/_new_monitoring_sidebar.html.haml'
- 'ee/app/views/layouts/service_desk.html.haml'
- 'ee/app/views/ldap_group_links/_form.html.haml'
@ -465,26 +388,22 @@ linters:
- 'ee/app/views/notify/epic_status_changed_email.html.haml'
- 'ee/app/views/notify/issues_csv_email.html.haml'
- 'ee/app/views/notify/new_review_email.html.haml'
- 'ee/app/views/notify/prometheus_alert_fired_email.html.haml'
- 'ee/app/views/notify/send_admin_notification.html.haml'
- 'ee/app/views/notify/send_unsubscribed_notification.html.haml'
- 'ee/app/views/notify/unapproved_merge_request_email.html.haml'
- 'ee/app/views/oauth/geo_auth/error.html.haml'
- 'ee/app/views/profiles/pipeline_quota/index.haml'
- 'ee/app/views/projects/audit_events/index.html.haml'
- 'ee/app/views/projects/blob/_owners.html.haml'
- 'ee/app/views/projects/commits/_mirror_status.html.haml'
- 'ee/app/views/projects/feature_flags/_configure_feature_flags_modal.html.haml'
- 'ee/app/views/projects/issues/_issue_weight.html.haml'
- 'ee/app/views/projects/issues/_related_issues.html.haml'
- 'ee/app/views/projects/issues/export_csv/_modal.html.haml'
- 'ee/app/views/projects/jobs/_shared_runner_limit_warning.html.haml'
- 'ee/app/views/projects/merge_requests/_approvals_count.html.haml'
- 'ee/app/views/projects/merge_requests/widget/open/_geo.html.haml'
- 'ee/app/views/projects/mirrors/_mirrored_repositories_count.html.haml'
- 'ee/app/views/projects/protected_branches/_update_protected_branch.html.haml'
- 'ee/app/views/projects/protected_branches/ee/_create_protected_branch.html.haml'
- 'ee/app/views/projects/protected_branches/ee/_dropdown.html.haml'
- 'ee/app/views/projects/protected_branches/ee/_fallback_update_protected_branch.html.haml'
- 'ee/app/views/projects/protected_tags/_protected_tag_extra_create_access_levels.haml'
- 'ee/app/views/projects/protected_tags/ee/_create_protected_tag.html.haml'
- 'ee/app/views/projects/push_rules/_index.html.haml'
@ -494,22 +413,16 @@ linters:
- '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'
- 'ee/app/views/shared/_shared_runners_minutes_limit.html.haml'
- 'ee/app/views/shared/audit_events/_event_table.html.haml'
- 'ee/app/views/shared/boards/components/_list_weight.html.haml'
- 'ee/app/views/shared/boards/components/sidebar/_epic.html.haml'
- 'ee/app/views/shared/ee/_import_form.html.haml'
- 'ee/app/views/shared/epic/_search_bar.html.haml'
- 'ee/app/views/shared/issuable/_approvals.html.haml'
- 'ee/app/views/shared/issuable/_board_create_list_dropdown.html.haml'
- 'ee/app/views/shared/issuable/_filter_weight.html.haml'
- 'ee/app/views/shared/issuable/_sidebar_item_epic.haml'
- 'ee/app/views/shared/members/ee/_ldap_tag.html.haml'
- 'ee/app/views/shared/members/ee/_override_member_buttons.html.haml'
- 'ee/app/views/shared/members/ee/_sso_badge.html.haml'
- 'ee/app/views/shared/milestones/_burndown.html.haml'
- 'ee/app/views/shared/milestones/_weight.html.haml'
- 'ee/app/views/shared/promotions/_promote_audit_events.html.haml'
- 'ee/app/views/shared/promotions/_promote_burndown_charts.html.haml'
- 'ee/app/views/shared/promotions/_promote_csv_export.html.haml'
- 'ee/app/views/shared/promotions/_promote_issue_weights.html.haml'
@ -517,7 +430,3 @@ linters:
- 'ee/app/views/shared/promotions/_promote_servicedesk.html.haml'
- 'ee/app/views/shared/push_rules/_form.html.haml'
- 'ee/app/views/unsubscribes/show.html.haml'
- 'ee/app/views/admin/users/_auditor_access_level_radio.html.haml'
- 'ee/app/views/admin/users/_auditor_user_badge.html.haml'
- 'ee/app/views/projects/protected_branches/_update_protected_branch.html.haml'
- 'ee/app/views/analytics/cycle_analytics/show.html.haml'

View file

@ -123,5 +123,6 @@
"YouTrack"
],
"code_blocks": false
}
},
"code-fence-style": false
}

View file

@ -331,25 +331,19 @@ RSpec/MissingExampleGroupArgument:
RSpec/UnspecifiedException:
Enabled: false
# Work in progress. See https://gitlab.com/gitlab-org/gitlab/issues/196163
RSpec/HaveGitlabHttpStatus:
Enabled: true
Exclude:
- 'spec/support/matchers/have_gitlab_http_status.rb'
Include:
- 'spec/support/**/*'
- 'ee/spec/support/**/*'
- 'spec/features/**/*'
- 'ee/spec/features/**/*'
- 'spec/controllers/**/*'
- 'ee/spec/controllers/**/*'
- 'spec/requests/*.rb'
- 'ee/spec/requests/*.rb'
- 'spec/requests/api/*/**/*.rb'
- 'ee/spec/requests/api/*/**/*.rb'
- 'spec/**/*'
- 'ee/spec/**/*'
Style/MultilineWhenThen:
Enabled: false
Style/FloatDivision:
Enabled: false
Cop/BanCatchThrow:
Enabled: true

View file

@ -1,40 +1,9 @@
# Vale configuration file, taken from https://errata-ai.github.io/vale/config/
# Vale configuration file.
#
# For more information, see https://errata-ai.gitbook.io/vale/getting-started/configuration.
# The relative path to the folder containing linting rules (styles)
# -----------------------------------------------------------------
StylesPath = doc/.linting/vale/styles
# Minimum alert level
# -------------------
# The minimum alert level to display (suggestion, warning, or error).
# If integrated into CI, builds fail by default on error-level alerts,
# unless you execute Vale with the --no-exit flag
StylesPath = doc/.vale
MinAlertLevel = suggestion
# Should Vale parse any file formats other than .md files as Markdown?
# --------------------------------------------------------------------
[formats]
mdx = md
# What file types should Vale test?
# ----------------------------------
[*.md]
# Styles to load
# --------------
# What styles, located in the StylesPath folder, should Vale load?
# Vale also currently includes write-good, proselint, joblint, and vale
BasedOnStyles = gitlab
# Enabling or disabling specific rules in a style
# -----------------------------------------------
# To disable a rule in an enabled style, use the following format:
# {style}.{filename} = NO
# To enable a single rule in a disabled style, use the following format:
# vale.Editorializing = YES
# Altering the severity of a rule in a style
# ------------------------------------------
# To change the reporting level (suggestion, warning, error) of a rule,
# use the following format: {style}.{filename} = {level}
# vale.Hedging = error

View file

@ -1,5 +1,157 @@
Please view this file on the master branch, on stable branches it's out of date.
## 12.9.2 (2020-03-31)
### Fixed (4 changes)
- Fix direct access to individual design on deprecated issue route. !27650
- Fix error when viewing events from design notes on project activity page. !27840
- Allow Seat Link to be disabled through configuration or admin toggle. !28015
- Allow active_users param to be optional for SyncSeatLinkRequestWorker#perform. !28241
### Changed (1 change)
- Send active users for each day in seat link POST request. !27481
## 12.9.1 (2020-03-26)
### Security (1 change)
- Add NPM package versions SemVer validation.
## 12.9.0 (2020-03-22)
### Removed (1 change)
- Remove Security Dashboard button from project page. !24781
### Fixed (20 changes)
- Ensure User IDs are Removed When Moving to All Users Strategy. !25190
- Fix dismissed vulnerability comment updates. !25328
- Generate illustrations URLs in the backend. !25375
- Check user role for each of namespaces while deciding if it is using a paid seat in it. !25437
- Change MR approval popover to use hover trigger. !25494
- Change copy of Welcome screen. !25526
- Fix weight not showing in linked issues. !25718
- Scope name uniqueness of approval rules to type. !25875
- Pass correct Time object to Value Stream Management DataCollector. !25885
- API for Namespace members has correct boolean if user using paid seat. !25988
- Set the default SSL cert path environment variables when invoking indexer. !26036
- Add pagination to Instance Level Security Dashboard project selector. !26138
- Exclude creation event from audit changes. !26140
- Resolve Usage Ping timeouts on security_products_usage for large hosts. !26296
- Fix SCIM API not creating new users. !26514
- BulkIndexer change _routing -> routing to support ES 7. !26639
- Ensure design events are correctly visible. !26708
- Add support for async search within Epics dropdown. !26980
- Fix configuration issue for License Compliance. !27016
- Geo - Fix ambiguous reference column while loading migrated local files to clean up. !27252
### Changed (22 changes, 1 of them is from the community)
- Add auto_renew attribute to gitlab_subscriptions and expose via api. !24896 (jejacks0n)
- Remove admin user count from Users statistics. !25337
- Improve error messages in the Dependency List. !25369
- Do not automatically add approvers as participants of each merge request. !25546
- Improve loading UX in the License Management list. !25620
- Improve audit log & events header layout. !25821
- Adjust the MR approval rule to use a smaller modal. !25955
- Resolve Code review start time doesnt account for approvals. !25997
- Prevent 'Invite group' for groups outside a group-managed account group. !26081
- Improve language when MR Security Report is outdated. !26150
- Prevent projects from being shared outside a group with managed accounts. !26163
- Prevent projects from being shared outside a group with managed accounts for forked projects. !26186
- Extract time range information to be reused. !26431
- Restrict inviting outside group to a project forked from a group with enforced SSO. !26456
- Add severity badge to security reports. !26715
- Consider MR reassign as code review start. !26891
- Add application limits for Ci::PipelineSchedule. !27004
- Update license compliance policy classifications text in the UI. !27058
- Remove approval metric recalculation when approval was removed. !27071
- Move Value Stream Analytics to the group level. !27304
- Remove release tag filter from Issues Analytics. !208903
- Change dependency & license scan pipeline text message.
### Performance (4 changes)
- Optimize service desk issue counters query performance in usage data. !26445
- Preload database records in bulk indexing. !26754
- Geo: Make the LFS registry the SSOT to optimize query performance. !27154
- Remove feature flag elastic_bulk_incremental_updates. !27293
### Added (58 changes, 9 of them are from the community)
- Audit number of required approvals changes. !7531
- Add weight and progress information in Roadmap Epic bars. !18957
- Adds the ability to dismiss multiple vulnerabilities. !21480
- Add code quality tab to pipeline view. !21527
- Add milestones to roadmap. !22748
- Add Threat Monitoring page. !22911
- Add epic in filtered search. !22958
- Add deep links for cycle analytics. !23493
- Allow existing users to transfer their account to SAML Group managed accounts. !24329
- Use snowplow for user error reporting in trial. !24430
- Time period for usage activity by stage in usage data. !24431
- Adds sorting to group packages api. !24432
- Export Board Milestones in Group Export. !24606
- Add possibility to search for epics by partial iids using GraphQL. !24673
- Add restrict outer forks functionality for group SAML. !24698
- Release Jira connect feature. !24818
- Adds project_path sorting to group package API. !24833
- Add webhook limits for groups. !25129
- Default design images to 100% of viewport width. !25327
- Add npm_package_requests_forwarding to application settings. !25346
- Add bot count to Users statistics in Admin Area. !25355
- Adding CODEOWNER approval metrics for merge requests. !25386
- Separate 4 ee entities into own class file. !25400 (Rajendra Kadam)
- Improve zoom reset after resizing window in Design Management. !25406
- Introduce Project level issues analytics. !25417
- Add tooltip to guests count in Users Statistics. !25433
- Separate epic, epic_issue, linked epic ee entities into own class file. !25592 (Rajendra Kadam)
- Add API endpoints for Group Activity Analytics. !25606
- Add GraphQL mutation to set the epic of an issue. !25628
- Add merge request metrics first approved at timestamp. !25681
- Separate ee entities into own class file. !25709 (Rajendra Kadam)
- Embed cluster health metrics in GitLab-flavored Markdown. !25739
- Always show the Design Tab on Issues but indicate user if feature is not available. !25796
- Status page settings. !25820
- Audit user blocked via API. !25872
- Create a vulnerability-list component. !25927
- Audit project MR approval permission changes. !25959
- Add health status data to the epic GraphQL endpoint. !25989
- Display correct approval rules based on target branch in Edit MR form. !26053
- Activate triggering webhooks from a subgroup feature. !26070
- Separate vulnerabilities, feature flags, dependency entities into own class files. !26088 (Rajendra Kadam)
- Support drag-and-drop design uploads in Design Management. !26139
- Add distinct user count of secure stage items to usage ping. !26194
- Support drag-and-drop on existing designs in Design Management. !26446
- Add worker to sync paid seats info daily in the background. !26467
- Separate project alias, build info, package entities into own class files. !26493 (Rajendra Kadam)
- Support async loading & search of projects within Epics Tree. !26661
- Add code review reassign metric structure. !26664
- Separate npm, subs and geo entities into own class file. !26674 (Rajendra Kadam)
- Separate geo_node and license entities into own class file. !26685 (Rajendra Kadam)
- Separate merge request approval entities into own class files. !26860 (Rajendra Kadam)
- Include snippet description as part of snippet title search (advanced search, Elasticsearch reindexing required). !26870
- Add Roadmap GraphQL and unfiltered epic aggregates. !26919
- Add NPM package requests forwarding to npmjs.org. !26932
- Separate approval entities into own class files. !27336 (Rajendra Kadam)
- View description changes history of issues, epics and merge requests. !27445
- Enable license compliance policies tab for on prem installs.
- Add a bulk processor for ES incremental updates.
### Other (5 changes, 1 of them is from the community)
- Refactor WeightSelect to use gitlab-ui dropdown. !20770
- Update to new severity badge design. !25489
- Rename close_{at,by} to dismissed_{at,by} in Vulnerabilities. !25609 (Michał Zając)
- Style MR approval popover link to be consistent text size. !25657
- Allow users to be marked as service users. !202680
## 12.8.7 (2020-03-16)
### Fixed (1 change)
@ -17,11 +169,11 @@ Please view this file on the master branch, on stable branches it's out of date.
## 12.8.4
- Unreleased due to tagging failure.
- No changes.
## 12.8.3
- Unreleased due to tagging failure.
- No changes.
## 12.8.2

View file

@ -2,19 +2,38 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 12.8.8 (2020-03-26)
## 12.9.2 (2020-03-31)
### Security (17 changes)
- No changes.
### Fixed (5 changes)
- Redact notes in moved confidential issues.
- Ensure import by URL works after a failed import. !27546
- Fix issue/MR state not being preserved when importing a project using Project Import/Export. !27816
- Leave upload Content-Type unchaged. !27864
- Disable archive rate limit by default. !28264
- Fix rake gitlab:setup failing on new installs. !28270
### Changed (1 change)
- Rename feature on the FE and locale.
### Performance (1 change)
- Index issues on sent_notifications table. !27034
## 12.9.1 (2020-03-26)
### Security (16 changes)
- Add permission check for pipeline status of MR.
- Ignore empty remote_id params from Workhorse accelerated uploads.
- External user can not create personal snippet through API.
- Prevent malicious entry for group name.
- Restrict mirroring changes to admins only when mirroring is disabled.
- Reject all container registry requests from blocked users.
- Deny localhost requests on fogbugz importer.
- Change GitHub service integration token input to password.
- Add permission check for pipeline status of MR.
- Redact notes in moved confidential issues.
- Fix UploadRewriter Path Traversal vulnerability.
- Block hotlinking to repository archives.
- Restrict access to project pipeline metrics reports.
@ -24,6 +43,462 @@ entry.
- Prevent updating trigger by other maintainers.
- Fix XSS vulnerability in `admin/email` "Recipient Group" dropdown.
### Fixed (1 change)
- Fix updating the authorized_keys file. !27798
## 12.9.0 (2020-03-22)
### Security (1 change)
- Update Puma to 4.3.3. !27232
### Removed (3 changes)
- Remove staging from commit workflow in the Web IDE. !26151
- Remove and deprecate snippet content search. !26359
- Remove "Analytics" suffix from the sidebar menu items. !26415
### Fixed (117 changes, 19 of them are from the community)
- Set all NULL `lock_version` values to 0 for issuables. !18418
- Support finding namespace by ID or path on fork API. !20603 (leoleoasd)
- Fixes caret position after pasting an image 15011. !21382 (Carolina Carvalhosa)
- Use of sha instead of ref when creating a new ref on deployment creation. !23170
- Fix logic to determine project export state and add regeneration_in_progress state. !23664
- Create child pipelines dynamically using content from artifact as CI configuration. !23790
- Handle Gitaly failure when fetching license. !24310
- Fix error details layout and alignment for mobile view. !24390
- Added the multiSelect option to stop event propagation when clicking on the dropdown. !24611 (Gwen_)
- Activate Prometheus integration service for newly created project if this project has access to shared Prometheus application. !24676
- Fix Jump to next unresolved thread. !24728
- Require a logged in user to accept or decline a term. !24771
- Fix quick actions executing in multiline inline code when placed on its own line. !24933 (Pavlo Dudchenko)
- Fix timezones for popovers. !24942
- Prevent "Select project to create merge request" button from overflowing out of the viewport on mobile. !25195
- Add validation for updated_at parameter in update Issue API. !25201 (Filip Stybel)
- Elasticsearch: when index is absent warn users and disable index button. !25254
- Fix pipeline details page initialisation on invalid pipeline. !25302 (Fabio Huser)
- Fix bug with sidebar not expanding at certain resolutions. !25313 (Lee Tickett)
- Rescue elasticsearch server error in pod logs. !25367
- Fix project setting approval input in non-sequential order. !25391
- Add responsivity to cluster environments table. !25501
- Board issue due dates appear grey for closed past-due issues. !25507 (rachelfox)
- Fix self monitoring project link. !25516
- Don't track MR deployment multiple times. !25537
- Fix an issue with Group Import members with Owner access level being imported with Maintainer access level. Owner access level is now preserved. !25595
- Allow 0 to be set for pages maximum size per project/group to indicate unlimited size. !25677
- Fix variable passthrough in the SAST CI/CD template when using DinD. !25697
- Drop bridge if downstream pipeline has errors. !25706
- Clean stale background migration jobs. !25707
- Inject CSP values when repository static objects external caching is enabled. !25711
- Fix bug deleting internal project snippets by project maintainer. !25792
- Fix Insights displaying JSON on back navigation. !25801
- Don't show issue as blocked on the issue board if blocking issue is closed. !25817
- Return 503 to the Runner when the object storage is unavailable. !25822
- Ensure temp export data is removed if Group/Project export failed. !25828
- Fix Kubernetes namespace resolution for new DeploymentCluster records. !25853
- Fix links to exposed artifacts in MRs from forks. !25868 (Daniel Stone)
- Keep needs association on the retried build. !25888
- Remove unreachable link from embded dashboard context menu. !25892
- Fix issue importer so it matches issue export format. !25896
- Fix snippet blob viewers for rich and plain data. !25945
- Fix White syntax highlighting theme in Monaco to closely match the Pygments theme. !25966
- Markup tips for Markdown shown while editing wiki pages in other formats. !25974
- Fix code search pagination on a custom branch. !25984
- Fix Snippet content incorrectly caching. !25985
- Fix 500 error caused by Kubernetes logs not being encoded in UTF-8. !25999
- Fix "Add an epic" form. !26003
- Ensure weight changes no longer render duplicate system notes. !26014
- Geo: Show secondary-only setting on only on secondaries. !26029
- Fixes project import failures when user is not part of any groups. !26038
- Fix ImportFailure when restore ci_pipelines:external_pull_request relation. !26041
- Code Review Analytics: Fix review time display. !26057
- Allow to fork to the same namespace and different path via API call. !26062
- Change back internal api return code. !26063
- Create approval todos on update. !26077
- Fix issues missing on epic's page after project import. !26099
- Fix scoped labels rendering in To-Do List. !26146
- Fix 500 Error when using Gitea Importer. !26166
- Fix dev vulnerabilities seeder. !26169
- Use uncached SQL queries for Geo long-running workers. !26187
- Fix infinite spinner on error detail page. !26188
- Generate proper link for Pipeline tab. !26193
- Issue Analytics: Fix svg illustration path for empty state. !26219
- Fix dashboards dropdown if custom dashboard is broken. !26228
- Refresh widget after canceling "Merge When Pipeline Succeeds". !26232
- Fix package file finder for conan packages with a conan_package_reference filter. !26240
- Fixed bug where processing NuGet packages are returned from the Packages API. !26270
- Fix bug committing snippet content when creating the snippet. !26287
- Fix error messages for dashboard clonning process. !26290
- Fix saving preferences with unrelated changes when gitaly timeouts became invalid. !26292
- Allow creating default branch in snippet repositories. !26294
- Container expiration policy settings hide form on API error. !26303
- Prevent unauthorized users to lock an issue from the collapsed sidebar. !26324 (Gilang Gumilar)
- Mark existing LFS object for upload for forks. !26344
- Fix scoped labels rendering in emails. !26347
- Fix issues with non-ASCII plain text files being incorrectly uploaded as binary in the Web IDE. !26360
- Polyfill fetch for Internet Explorer 11. !26366
- Fix avg_cycle_analytics uncaught error and optimize query. !26381
- Fix reversed pipeline order on Project Import. !26390
- Display GitLab issues created via Sentry global integration. !26418
- Fix MergeToRefService raises Gitlab::Git::CommandError. !26465
- Render special references for releases. !26554
- Show git error message updating snippet. !26570
- Support Rails 6 `insert_all!`. !26595
- Fix evidence SHA clipboard hover text. !26608 (Gilang Gumilar)
- Prevent editing weight to scroll to the top. !26613 (Gilang Gumilar)
- Fix spinner in Create MR dropdown. !26679
- Added a padding-right to items in subgroup list. !26791
- Prevent default overwrite for theme and color ID in user API. !26792 (Fabio Huser)
- Fix user registration when smartcard authentication is enabled. !26800
- Correctly send notification on pipeline retry. !26803 (Jacopo Beschi @jacopo-beschi)
- Default to generating blob links for missing paths. !26817
- Fix Mermaid flowchart width. !26848 (julien MILLAU)
- Ensure valid mount point is used by attachments on notes. !26849
- Validate that users selects at least two subnets in EKS Form. !26936
- Fix embeds so that a chart appears only once. !26997
- Fix capybara screenshots path name for rails configuration. !27002
- Fix access to logs when multiple pods exist. !27008
- Fix installation of GitLab-managed crossplane chart. !27040
- Fix bug displaying snippet update error. !27082
- Fix WikiPage#title_changed for paths with spaces. !27087
- Fix backend validation of numeric emoji names. !27101
- Reorder exported relations by primary_key when using Project Export. !27117
- Ensure freshness of settings with project creation. !27156
- Fix bug setting hook env with personal snippets. !27235
- Fix Conan package download_urls and snapshot to return files based on requested conan_package_reference. !27250
- Fixes stop_review job upon expired artifacts from previous stages. !27258 (Jack Lei)
- Fix duplicate labels when moving projects within the same ancestor group. !27261
- Fix project moved message after git operation. !27341
- Fix submodule links to gist.github.com. !27346
- Fix remove special chars from snippet url_to_repo. !27390
- Validate actor against CODEOWNERS entries.
- Fix: tableflip quick action is interpreted even if inside code block. (Pavlo Dudchenko)
- Fix an error with concat method.
- Improved selection of multiple cards. (Gwen_)
- Resolves the disappearance of a ticket when it was moved from the closed list. (Gwen_)
### Deprecated (1 change)
- Remove state column from issues and merge_requests. !25561
### Changed (81 changes, 18 of them are from the community)
- Remove kubernetes workaround in container scanning. !21188
- New styles for scoped labels. !21377
- Update labels in Vue with GlLabel component. !21465
- Update Web IDE clientside preview bundler to use GitLab managed server. !21520
- Allow default time window on grafana embeds. !21884
- Default to first valid panel in unspecified Grafana embeds. !21932
- Correctly style scoped labels in sidebar after updating. !22071
- Add id and image_v432x230 columns to design_management_designs_versions. !22860
- Decouple Webhooks from Integrations within Project > Settings. !23136
- Sort closed issues on issue boards using time of closing. !23442 (briankabiro)
- Differentiate between errors and failures in xUnit result. !23476
- Add 'shard' label for 'job_queue_duration_seconds' metric. !23536
- Migrate mentions for design notes to design_user_mentions DB table. !23704
- Migrate mentions for commit notes to commit_user_mentions DB table. !23859
- Update files when snippet is updated. !23993
- Move issues routes under /-/ scope. !24791
- Migrated the sidebar label select dropdown title component spinner to utilize GlLoadingIcon. !24914 (Raihan Kabir)
- Migrated from .fa-spinner to .spinner in 'app/assets/javascripts/notes.js. !24916 (Raihan Kabir (gitlab/rk4bir))
- Migrated from .fa-spinner to .spinner in app/assets/javascripts/create_merge_request_dropdown.js. !24917 (Raihan Kabir (gitlab/rk4bir))
- Migrated from .fa-spinner to .spinner in app/assets/javascripts/sidebar/components/assignees/assignee_title.vue. !24919 (rk4bir)
- Replace underscore with lodash for ./app/assets/javascripts/deploy_keys. !24965 (Jacopo Beschi @jacopo-beschi)
- Replace underscore with lodash for ./app/assets/javascripts/badges. !24966 (Jacopo Beschi @jacopo-beschi)
- Add commits limit text at graphs page. !24990
- Migrated from .fa-spinner to .spinner in app/assets/javascripts/blob/template_selector.js. !25045 (Raihan Kabir (gitlab/rk4bir))
- Update iOS (Swift) project template logo. !25049
- Sessionless and API endpoints bypass session for admin mode. !25056 (Diego Louzán)
- New loading spinner for attachemnt uploads via discussion boxes. !25057 (Philip Jonas)
- Hide the private commit email in Notification email list. !25099 (briankabiro)
- Replace underscore with lodash in /app/assets/javascripts/blob/. !25113 (rkpattnaik780)
- Allow access to /version API endpoint with read_user scope. !25211
- Use only the first line of the commit message on chat service notification. !25224 (Takuya Noguchi)
- Include invalid directories in wiki title message. !25376
- Replace avatar and favicon upload type consistency validation with content whitelist validation. !25401
- Showing only "Next" button for snippet explore page. !25404
- Moved Deploy Keys from Repository to CI/CD settings. !25444
- Move pod logs to core. !25455
- Improve error messages of failed migrations. !25457
- Hides the "Allowed to fail" tag on jobs that are successful. !25458
- Disable CSRF protection on logout endpoint. !25521 (Diego Louzán)
- Ensure all errors are logged in Group Import. !25619
- Tweak wiki page title handling. !25647
- Add refresh dashboard button. !25716
- Disable draggable behavior on the epic tree chevron (collapse/expand) button. !25729
- Rate limit archive endpoint by user. !25750
- Improve audit log header layout. !25821
- Migrate mentions for merge requests to DB table. !25826
- Align git returned error codes. !25936
- Split cluster info page into tabs. !25940
- Remove visibility check from epic descendant counts. !25975
- Use colon to tokenize input in filtered search. !26072
- Add link to dependency proxy docs on the dependency proxy page. !26092
- Remove Puma notices from AdminArea banner. !26137
- Add airgap support to Dependency Scanning template. !26145
- 27880 Make release notes optional and do not delete release when they are removed. !26231 (Pavlo Dudchenko)
- Limit notification-type broadcast display to web interface. !26236 (Aleksandrs Ļedovskis)
- Update renewal banner link for clearer instructions. !26240
- Special handling for the rich viewer on specific file types. !26260
- Rename pod logs to logs. !26313
- Ensure checksums match when updating repository storage. !26334
- Bump Auto Deploy image to v0.12.1. !26336
- Use cert-manager 0.10 instead of 0.9 for new chart installations. !26345
- Use y-axis format configuration in column charts. !26356
- Add Prometheus metrics for Gitaly and database time in background jobs. !26384
- Batch processing LFS objects downloads. !26434
- Add edit custom metric link to metrics dashboard. !26511
- Remove unused file_type column from packages_package_files. !26527
- Enable client-side GRPC keepalive for Gitaly. !26536
- Use ReplicateRepository when moving repo storage. !26550
- Add functionality to render individual mermaids. !26564
- Sync snippet after Git action. !26565
- In single-file editor set syntax highlighting theme according to user's preference. !26606
- Introduce a feature flag for Notifications for when pipelines are fixed. !26682 (Jacopo Beschi @jacopo-beschi)
- Replace checkbox by toggle for ModSecurity on Cluster App Page. !26720
- Change capybara screenshots files names taken on tests failures. !26788
- Update cluster-applications image to v0.11 with a runner bugfix, updated cert-manager, and vault as a new app. !26842
- Store first commit's authored_date for value stream calculation on merge. !26885
- Group repository contributors by email instead of name. !26899 (Hilco van der Wilk)
- Move authorized_keys operations into their own Sidekiq queue. !26913
- Upgrade Elastic Stack helm chart to 1.9.0. !27011
- Enable customizable_cycle_analytics feature flag by default. !27418
- Deemphasized styles for inline code blocks.
### Performance (41 changes, 1 of them is from the community)
- Cache milestone issue counters and make them independent of user permissions. !21554
- Persist expanded environment name in ci build metadata. !22374
- Diffs load each view style separately, on demand. !24821
- Project repositories are no longer cloned by default when running DAST. !25320
- Enable Workhorse upload acceleration for Project Import API. !25361
- Add API pagination for deployed merge requests. !25733
- Upgrade to Bootsnap 1.4.6. !25844
- Improve performance of Repository#merged_branch_names. !26005
- Fix N+1 in Group milestone view. !26051
- Project Snippets API endpoints check feature status. !26064
- Memoize loading of CI variables. !26147
- Refactor workhorse passthrough URL checker. !26157 (Takuya Noguchi)
- Project Snippets GraphQL resolver checks feature status. !26158
- Improved MR toggle file performance by hiding instead of removing. !26181
- Use Workhorse acceleration for Project Import file upload via UI. !26278
- Improve SnippetsFinder performance with disabled project snippets. !26295
- Add trigram index on snippet description. !26341
- Optimize todos counters in usage data. !26442
- Optimize event counters query performance in usage data. !26444
- Ensure RepositoryLinkFilter handles Gitaly failures gracefully. !26531
- Fix N+1 queries for PipelinesController#index.json. !26643
- Optimize Project related count with slack service. !26686
- Optimize Project counters with respository enabled counter. !26698
- Optimize Deployment related counters. !26757
- Optimize ci_pipelines counters in usage data. !26774
- Improve performance of the "has this commit been reverted?" check. !26784
- Optimize Project counters with pipelines enabled counter. !26802
- Optimize notes counters in usage data. !26871
- Optimize clusters counters query performance in usage data. !26887
- Enable Workhorse upload acceleration for Project Import uploads via API. !26914
- Use process-wide memory cache for feature flags. !26935
- Optimize services usage counters using batch counters. !26973
- Optimize Project related count service desk enabled. !27115
- Swap to UNLINK for Redis set cache. !27116
- Optimize members counters query performance in usage data. !27197
- Use batch counters instead of approximate counters in usage data. !27218
- Enable Redis cache key compression. !27254
- Move feature flag list into process cache. !27511
- Remove duplicate authorization refresh for group members on project creation.
- Optimize project representation in large imports.
- Replace several temporary indexes with a single one to save time when running mentions migration.
### Added (115 changes, 16 of them are from the community)
- Notifications for when pipelines are fixed. !16951 (Jacopo Beschi @jacopo-beschi)
- Backport API support to move between repository storages/shards. !18721 (Ben Bodenmiller)
- Add ability to trigger pipelines when project is rebuilt. !20063
- Add user dismiss option to broadcast messages. !20665 (Fabio Huser)
- Show notices in Admin area when detected any of these cases: Puma, multi-threaded Puma, multi-threaded Puma + Rugged. !21403
- Update git workflows and routes to allow snippets. !21739
- Add Cobertura XML coverage visualization to merge request diff view. !21791 (Fabio Huser)
- Add 2FA support to admin mode feature. !22281 (Diego Louzán)
- GraphQL: Add Board type. !22497 (Alexander Koval)
- Add/update services to delete snippets repositories. !22672
- Render single snippet blob in repository. !23848
- Commit file when snippet is created. !23953
- Addition of the Group Deploy Token interface. !24102
- Allow multiple Slack channels for notifications. !24132
- Import/Export snippet repositories. !24150
- Add custom validator for validating file path. !24223 (Rajendra Kadam)
- Add a bulk processor for elasticsearch incremental updates. !24298
- Send alert emails for generic incident alerts. !24414
- Introduce default branch protection at the group level. !24426
- Add "New release" button to Releases page. !24516
- Nudge users to select a gitlab-ci.yml template. !24622
- Allow enabling/disabling modsecurity from UI. !24747
- Add possibility to track milestone changes on issues and merge requests. !24780
- Allow group/project board to be queried by ID via GraphQL. !24825
- Add functionality to revoke a X509Certificate and update related X509CommitSignatures. !24889 (Roger Meier)
- Update file content of an existing custom dashboard. !25024
- Add deploy tokens instance API endpoint. !25066
- Add support for alert-based metric embeds in GFM. !25075
- Add restrictions for signup email addresses. !25122
- Add accessibility scanning CI template. !25144
- Expose `plan` and `trial` to `/users/:id` endpoint. !25151
- Add "Job Title" field in user settings and display on profile. !25155
- Add endpoint for listing all deploy tokens for a project. !25186
- Add api endpoint for listing deploy tokens for a group. !25219
- Add API endpoint for deleting project deploy tokens. !25220
- Add API endpoint for deleting group deploy tokens. !25222
- Allow users to get Merge Trains entries via Public API. !25229
- Added CI_MERGE_REQUEST_CHANGED_PAGE_* to Predefined Variables reference. !25256
- Add missing arguments to UpdateIssue mutation. !25268
- Add api endpoint to create deploy tokens. !25270
- Automatically include embedded metrics for GitLab alert incidents. !25277
- Allow to create masked variable from group variables API. !25283 (Emmanuel CARRE)
- Add migration to create self monitoring project environment. !25289
- Add deploy and re-deploy buttons to deployments. !25427
- Replaced ACE with Monaco editor for Snippets. !25465
- Add support for user Job Title. !25483
- Add name_regex_keep param to container registry bulk delete API endpoint. !25484
- Add Project template for Gatsby. !25486
- Add filepath to ReleaseLink. !25512
- Added Drop older active deployments project setting. !25520
- Add filepath to release links API. !25533
- Adds new activity panel to package details page. !25534
- Add filepath redirect url. !25541
- Add version column to operations_feature_flags table. !25552
- Filter commits by author. !25597
- Add api endpoint for creating group deploy tokens. !25629
- Expose assets filepath URL on UI. !25635
- Update moved service desk issues notifications. !25640
- Allow chart descriptions for Insights. !25686
- Allow to disable inheritance of default job settings. !25690
- Support more query variables in custom dashboards per project. !25732
- All image diffs (except for renamed files) show the image file size in the diff. !25734
- Optional custom icon in the OmniAuth login labels. !25744 (Tobias Wawryniuk, Luca Leonardo Scorcia)
- Add avatar upload support for create and update group APIs. !25751 (Rajendra Kadam)
- Add properties to the dashboard definition to customize y-axis format. !25785
- Empty state for Code Review Analytics. !25793
- Search issues in GraphQL API by milestone title and assignees. !25794
- Add package_type as a filter option to the packages list API endpoint. !25816
- Add support for configuring remote mirrors via API. !25825 (Rajendra Kadam)
- Display base label in versions drop down. !25834
- Create table & setup operations endpoint for Status Page Settings. !25863
- Update Ingress chart version to 1.29.7. !25949
- Include snippet description as part of snippet title search (basic search). !25961
- Add admin API endpoint to delete Sidekiq jobs matching metadata. !25998
- Add documentation for create remote mirrors API. !26012 (Rajendra Kadam)
- Update charts documentation and common_metrics.yml to enable data formatting. !26048
- Allow issues/merge_requests as an issuable_type in Insights configuration. !26061
- Add migration for Requirement model. !26097
- Create scim_identities table in preparation for newer SCIM features in the future. !26124
- Add web_url attribute to API response for Commits. !26173
- Filter sentry error list by status (unresolved/ignored/resolved). !26205
- Add grape custom validator for sha params. !26220 (Rajendra Kadam)
- Update cluster-applications to v0.9.0. !26242
- Support DotEnv Variables through report type artifact. !26247
- More logs entries are loaded when logs are scrolled to the top. !26254
- Introduce db table to store users statistics. !26261
- Add title to Analytics sidebar menus. !26265
- Added package_name as filter parameter to packages API. !26291
- Added tracking to merge request jump to next thread buttons. !26319 (Martin Hobert)
- Introduce optional expiry date for SSH Keys. !26351
- Show cluster status (FE). !26368
- Add CI template to deploy to ECS. !26371
- Make hostname configurable for smartcard authentication. !26411
- Filter rules by target_branch in approval_settings. !26439
- Add CRUD for Instance-Level Integrations. !26454
- Add vars to allow air-gapped usage of Retire.js (Dependency Scanning). !26463
- Upgrade Pages to 1.17.0. !26478
- Add dedicated Release page for viewing a single Release. !26502
- Allow selecting all queues with sidekiq-cluster. !26594
- Enable feature Dynamic Child Pipeline creation via artifact. !26648
- Generate JSON-formatted a11y CI artifacts. !26687
- Add anchor tags to related issues and related merge requests. !26756 (Gilang Gumilar)
- Added Blob Description Edit component in Vue. !26762
- Added Edit Visibility Vue compoenent for Snippet. !26799
- Add package_type as a filter option to the group packages list API endpoint. !26833
- Update UI for project and group settings CI variables. !26901
- Track merge request cherry-picks. !26907
- Introduce database table for user highest roles. !26987
- Add ability to whitelist ports. !27025
- Add issue summary to Release blocks on the Releases page. !27032
- Support sidekiq-cluster supervision through bin/background_jobs. !27042
- Adds crossplane as CI/CD Managed App. !27374
- Update UI for project and group settings CI variables. !27411
- Add remote mirrors API.
- Add changed pages dropdown to visual review modal.
### Other (66 changes, 22 of them are from the community)
- Make design_management_versions.created_at not null. !20182 (Lee Tickett)
- Drop forked_project_links table. !20771 (Lee Tickett)
- Moves refreshData from issue model to board store. !21409 (nuwe1)
- Use DNT: 1 as an experiment opt-out mechanism. !22100
- Include full path to an upload in api response. !23500 (briankabiro)
- Update Ruby version in official CI templates. !23585 (Takuya Noguchi)
- Schedule worker to migrate security job artifacts to security scans. !24125
- Move namespace of Secure Sidekiq queues. !24340
- Remove spinner from app/views/projects/notes. !25015 (nuwe1)
- Migrate .fa-spinner to .spinner for ee/app/views/shared/members. !25019 (nuwe1)
- Migrate .fa-spinner to .spinner for app/views/ide. !25022 (nuwe1)
- Remove spinner from app/views/award_emoji. !25032 (nuwe1)
- Remove .fa-spinner from app/views/projects/forks. !25034 (nuwe1)
- Remove .fa-spinner from app/views/snippets/notes. !25036 (nuwe1)
- Migrate .fa-spinner to .spinner for app/views/help. !25037 (nuwe1)
- Replaced underscore with lodash for app/assets/javascripts/lib. !25042 (Shubham Pandey)
- Remove unused loading spinner from badge_settings partial. !25044 (nuwe1)
- Migrate .fa-spinner to .spinner for app/views/projects/find_file. !25051 (nuwe1)
- Migrate .fa-spinner to .spinner for app/assets/javascripts/notes/components/discussion_resolve_button.vue. !25055 (nuwe1)
- Change OmniAuth log format to JSON. !25086
- migrate fa spinner for notification_dropdown.js. !25141 (minghuan)
- Use new loading spinner in Todos dashboard buttons. !25142 (Tsegaselassie Tadesse)
- Refuse to start web server without a working ActiveRecord connection. !25160
- Simplifying colors in the Web IDE. !25304
- Clean up conditional `col-` classes in `nav_dropdown_button.vue`. !25312
- Only load usage ping cron schedule for Sidekiq. !25325
- Update rouge to v3.16.0. !25334 (Konrad Borowski)
- Update project's permission settings description to reflect actual permissions. !25523
- Use clearer error message for pages deploy job when the SHA is outdated. !25659
- Add index on LOWER(domain) for pages_domains. !25664
- Remove repository_storage column from snippets. !25699
- Add instance column to services table. !25714
- Update GitLab Runner Helm Chart to 0.14.0. !25749
- Update loader for various project views. !25755 (Phellipe K Ribeiro)
- Clarify private visibility for projects. !25852
- Do not parse undefined severity and confidence from reports. !25884
- Remove special chars from previous and next items in pagination. !25891
- Update Auto DevOps deployment template's auto-deploy-image to v0.10.0 (updates the included glibc). !25920
- Update DAST auto-deploy-image to v0.10.0. !25922
- Optimize storage usage for newly created ES indices. !25992
- Replace undefined severity with unknown severity for occurrences. !26085
- Replace undefined severity with unknown severity for vulnerabilities. !26305
- Remove unused Snippets#content_types method. !26306
- Change tooltip text for pipeline on last commit widget. !26315
- Resolve Change link-icons on security configuration page to follow design system. !26340
- Put System Metrics chart group first in default dashboard. !26355
- Validates only one service template per type. !26380
- update table layout for error tracking list on medium view ports. !26479
- Validate absence of project_id if service is a template. !26563
- Move sidekiq-cluster script to Core. !26703
- Update GitLab's codeclimate to 0.85.9. !26712 (Eddie Stubbington)
- Bump minimum node version to v10.13.0. !26831
- Remove promoted notes temporary index. !26896
- Update Project Import API rate limit. !26903
- Backfill LfsObjectsProject records of forks. !26964
- Add migration for creating open_project_tracker_data table. !26966
- Fixed SSH warning style. !26992
- Use new codequality docker image from ci-cd group. !27098
- Add tooltip to modification icon in the file tree. !27158
- Upgrade Gitaly gem and fix UserSquash RPC usage. !27372
- Replace issue-external icon with external-link. !208827
- Add keep_divergent_refs to remote_mirrors table.
- Replace issue-duplicate icon with duplicate icon.
- Add confidential attribute to notes table.
- Replace content_viewer_spec setTimeouts with semantic actions / events. (Oregand)
- Improvement in token reference.
## 12.8.7 (2020-03-16)
@ -64,11 +539,49 @@ entry.
## 12.8.4
- Unreleased due to tagging failure.
### Fixed (8 changes)
- Fix Group Import API file upload when object storage is disabled. !25715
- Fix Web IDE fork modal showing no text. !25842
- Fixed regression when URL was encoded in a loop. !25849
- Fixed repository browsing for folders with non-ascii characters. !25877
- Fix search for Sentry error list. !26129
- Send credentials with GraphQL fetch requests. !26386
- Show CI status in project dashboards. !26403
- Rescue invalid URLs during badge retrieval in asset proxy. !26524
### Performance (2 changes)
- Disable Marginalia line backtrace in production. !26199
- Remove unnecessary Redis deletes for broadcast messages. !26541
### Other (1 change, 1 of them is from the community)
- Fix fixtures for Error Tracking Web UI. !26233 (Takuya Noguchi)
## 12.8.3
- Unreleased due to tagging failure.
### Fixed (8 changes)
- Fix Group Import API file upload when object storage is disabled. !25715
- Fix Web IDE fork modal showing no text. !25842
- Fixed regression when URL was encoded in a loop. !25849
- Fixed repository browsing for folders with non-ascii characters. !25877
- Fix search for Sentry error list. !26129
- Send credentials with GraphQL fetch requests. !26386
- Show CI status in project dashboards. !26403
- Rescue invalid URLs during badge retrieval in asset proxy. !26524
### Performance (2 changes)
- Disable Marginalia line backtrace in production. !26199
- Remove unnecessary Redis deletes for broadcast messages. !26541
### Other (1 change, 1 of them is from the community)
- Fix fixtures for Error Tracking Web UI. !26233 (Takuya Noguchi)
## 12.8.2

View file

@ -1 +1 @@
12.8.8
12.9.2

View file

@ -1 +1 @@
1.16.0
1.17.0

View file

@ -1 +1 @@
11.0.0
12.0.0

View file

@ -1 +1 @@
8.21.1
8.25.1

31
Gemfile
View file

@ -2,7 +2,7 @@ source 'https://rubygems.org'
gem 'rails', '6.0.2'
gem 'bootsnap', '~> 1.4'
gem 'bootsnap', '~> 1.4.6'
# Improves copy-on-write performance for MRI
gem 'nakayoshi_fork', '~> 0.0.4'
@ -58,7 +58,7 @@ gem 'akismet', '~> 3.0'
gem 'invisible_captcha', '~> 0.12.1'
# Two-factor authentication
gem 'devise-two-factor', '~> 3.0.0'
gem 'devise-two-factor', '~> 3.1.0'
gem 'rqrcode-rails3', '~> 0.1.7'
gem 'attr_encrypted', '~> 3.1.0'
gem 'u2f', '~> 0.2.1'
@ -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.12'
gem 'graphql', '~> 1.9.19'
# 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.15.0'
gem 'rouge', '~> 3.17.0'
gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.5'
@ -159,7 +159,7 @@ gem 'escape_utils', '~> 1.1'
gem 'icalendar'
# Diffs
gem 'diffy', '~> 3.1.0'
gem 'diffy', '~> 3.3'
gem 'diff_match_patch', '~> 0.1.0'
# Application server
@ -171,7 +171,7 @@ group :unicorn do
end
group :puma do
gem 'gitlab-puma', '~> 4.3.1.gitlab.2', require: false
gem 'gitlab-puma', '~> 4.3.3.gitlab.2', require: false
gem 'gitlab-puma_worker_killer', '~> 0.1.1.gitlab.1', require: false
gem 'rack-timeout', require: false
end
@ -237,7 +237,7 @@ gem 'atlassian-jwt', '~> 0.2.0'
gem 'flowdock', '~> 0.7'
# Slack integration
gem 'slack-notifier', '~> 1.5.1'
gem 'slack-messenger', '~> 2.3.3'
# Hangouts Chat integration
gem 'hangouts-chat', '~> 0.0.5'
@ -301,7 +301,7 @@ gem 'sentry-raven', '~> 2.9'
gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '0.9.1'
gem 'gitlab-labkit', '0.11.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 'derailed_benchmarks', require: false
gem 'gitlab-derailed_benchmarks', require: false
# Metrics
group :metrics do
@ -355,7 +355,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.7.0'
gem 'factory_bot_rails', '~> 5.1.0'
gem 'rspec-rails', '~> 4.0.0.beta3'
gem 'rspec-rails', '~> 4.0.0.beta4'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.11.0'
@ -374,8 +374,8 @@ group :development, :test do
gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.34.0', require: false
gem 'simplecov', '~> 0.16.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
gem 'simplecov', '~> 0.18.5', require: false
gem 'bundler-audit', '~> 0.6.1', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false
@ -383,7 +383,7 @@ group :development, :test do
gem 'simple_po_parser', '~> 1.1.2', require: false
gem 'timecop', '~> 0.8.0'
gem 'timecop', '~> 0.9.1'
gem 'png_quantizator', '~> 0.2.1', require: false
@ -419,7 +419,8 @@ end
gem 'octokit', '~> 4.15'
gem 'mail_room', '~> 0.10.0'
# https://gitlab.com/gitlab-org/gitlab/issues/207207
gem 'gitlab-mail_room', '~> 0.0.3', require: 'mail_room'
gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
@ -455,7 +456,7 @@ group :ed25519 do
end
# Gitaly GRPC protocol definitions
gem 'gitaly', '~> 1.86.0'
gem 'gitaly', '~> 12.9.0.pre.rc4'
gem 'grpc', '~> 1.24.0'

View file

@ -123,7 +123,7 @@ GEM
binding_ninja (0.2.3)
binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
bootsnap (1.4.5)
bootsnap (1.4.6)
msgpack (~> 1.0)
bootstrap_form (4.2.0)
actionpack (>= 5.0)
@ -134,8 +134,8 @@ GEM
bullet (6.0.2)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
bundler-audit (0.5.0)
bundler (~> 1.2)
bundler-audit (0.6.1)
bundler (>= 1.2.0, < 3)
thor (~> 0.18)
byebug (9.1.0)
capybara (3.22.0)
@ -211,15 +211,6 @@ GEM
declarative-option (0.1.0)
default_value_for (3.3.0)
activerecord (>= 3.2.0, < 6.1)
derailed_benchmarks (1.4.2)
benchmark-ips (~> 2)
get_process_mem (~> 0)
heapy (~> 0)
memory_profiler (~> 0)
rack (>= 1)
rake (> 10, < 14)
ruby-statistics (>= 2.1)
thor (~> 0.19)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
device_detector (1.0.0)
@ -229,18 +220,18 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise-two-factor (3.0.0)
activesupport
devise-two-factor (3.1.0)
activesupport (< 6.1)
attr_encrypted (>= 1.3, < 4, != 2)
devise (~> 4.0)
railties
railties (< 6.1)
rotp (~> 2.0)
diff-lcs (1.3)
diff_match_patch (0.1.0)
diffy (3.1.0)
diffy (3.3.0)
discordrb-webhooks-blackst0ne (3.3.0)
rest-client (~> 2.0)
docile (1.3.1)
docile (1.3.2)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.0.2)
@ -375,12 +366,21 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
git (1.5.0)
gitaly (1.86.0)
gitaly (12.9.0.pre.rc4)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
gitlab-labkit (0.9.1)
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)
actionpack (>= 5.0.0, < 6.1.0)
activesupport (>= 5.0.0, < 6.1.0)
grpc (~> 1.19)
@ -388,9 +388,10 @@ GEM
opentracing (~> 0.4)
redis (> 3.0.0, < 5.0.0)
gitlab-license (1.0.0)
gitlab-mail_room (0.0.3)
gitlab-markup (1.7.0)
gitlab-net-dns (0.9.1)
gitlab-puma (4.3.1.gitlab.2)
gitlab-puma (4.3.3.gitlab.2)
nio4r (~> 2.0)
gitlab-puma_worker_killer (0.1.1.gitlab.1)
get_process_mem (~> 0.2)
@ -433,7 +434,7 @@ GEM
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
gpgme (2.0.19)
gpgme (2.0.20)
mini_portile2 (~> 2.3)
grape (1.1.0)
activesupport
@ -455,7 +456,7 @@ GEM
graphiql-rails (1.4.10)
railties
sprockets-rails
graphql (1.9.12)
graphql (1.9.19)
graphql-docs (1.6.0)
commonmarker (~> 0.16)
escape_utils (~> 1.2)
@ -616,7 +617,6 @@ GEM
lumberjack (1.0.13)
mail (2.7.1)
mini_mime (>= 0.1.1)
mail_room (0.10.0)
marcel (0.3.3)
mimemagic (~> 0.3.2)
marginalia (1.8.0)
@ -889,41 +889,41 @@ GEM
retriable (3.1.2)
rinku (2.0.0)
rotp (2.1.2)
rouge (3.15.0)
rouge (3.17.0)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
rspec (3.8.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-core (3.8.2)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.4)
rspec (3.9.0)
rspec-core (~> 3.9.0)
rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.9.0)
rspec-core (3.9.1)
rspec-support (~> 3.9.1)
rspec-expectations (3.9.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.1)
rspec-support (~> 3.9.0)
rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-support (~> 3.9.0)
rspec-parameterized (0.4.2)
binding_ninja (>= 0.2.3)
parser
proc_to_ast
rspec (>= 2.13, < 4)
unparser
rspec-rails (4.0.0.beta3)
rspec-rails (4.0.0.beta4)
actionpack (>= 4.2)
activesupport (>= 4.2)
railties (>= 4.2)
rspec-core (~> 3.8)
rspec-expectations (~> 3.8)
rspec-mocks (~> 3.8)
rspec-support (~> 3.8)
rspec-core (~> 3.9)
rspec-expectations (~> 3.9)
rspec-mocks (~> 3.9)
rspec-support (~> 3.9)
rspec-retry (0.6.1)
rspec-core (> 3.3)
rspec-set (0.1.3)
rspec-support (3.8.2)
rspec-support (3.9.2)
rspec_junit_formatter (0.4.1)
rspec-core (>= 2, < 4, != 2.12.0)
rspec_profiling (0.0.5)
@ -955,7 +955,7 @@ GEM
ruby-progressbar (1.10.1)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
ruby-statistics (2.1.1)
ruby-statistics (2.1.2)
ruby_dep (1.5.0)
ruby_parser (3.13.1)
sexp_processor (~> 4.9)
@ -1015,13 +1015,12 @@ GEM
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simple_po_parser (1.1.2)
simplecov (0.16.1)
simplecov (0.18.5)
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
simplecov-html (~> 0.11)
simplecov-html (0.12.2)
sixarm_ruby_unaccent (1.2.0)
slack-notifier (1.5.1)
slack-messenger (2.3.3)
snowplow-tracker (0.6.1)
contracts (~> 0.7, <= 0.11)
spring (2.0.2)
@ -1065,7 +1064,7 @@ GEM
thread_safe (0.3.6)
thrift (0.11.0.0)
tilt (2.0.10)
timecop (0.8.1)
timecop (0.9.1)
timfel-krb5-auth (0.8.3)
toml (0.2.0)
parslet (~> 1.8.0)
@ -1171,12 +1170,12 @@ DEPENDENCIES
benchmark-memory (~> 0.1)
better_errors (~> 2.5.0)
binding_of_caller (~> 0.8.0)
bootsnap (~> 1.4)
bootsnap (~> 1.4.6)
bootstrap_form (~> 4.2.0)
brakeman (~> 4.2)
browser (~> 2.5)
bullet (~> 6.0.2)
bundler-audit (~> 0.5.0)
bundler-audit (~> 0.6.1)
capybara (~> 3.22.0)
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
@ -1190,12 +1189,11 @@ 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.0.0)
devise-two-factor (~> 3.1.0)
diff_match_patch (~> 0.1.0)
diffy (~> 3.1.0)
diffy (~> 3.3)
discordrb-webhooks-blackst0ne (~> 3.3)
doorkeeper (~> 5.0.2)
doorkeeper-openid_connect (~> 1.6.3)
@ -1230,14 +1228,16 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly (~> 1.86.0)
gitaly (~> 12.9.0.pre.rc4)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-labkit (= 0.9.1)
gitlab-derailed_benchmarks
gitlab-labkit (= 0.11.0)
gitlab-license (~> 1.0)
gitlab-mail_room (~> 0.0.3)
gitlab-markup (~> 1.7.0)
gitlab-net-dns (~> 0.9.1)
gitlab-puma (~> 4.3.1.gitlab.2)
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)
@ -1252,7 +1252,7 @@ DEPENDENCIES
grape-path-helpers (~> 1.2)
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.9.12)
graphql (~> 1.9.19)
graphql-docs (~> 1.6.0)
grpc (~> 1.24.0)
gssapi
@ -1284,7 +1284,6 @@ DEPENDENCIES
loofah (~> 2.2)
lru_redux
mail (= 2.7.1)
mail_room (~> 0.10.0)
marginalia (~> 1.8.0)
memory_profiler (~> 0.9)
method_source (~> 0.8)
@ -1347,10 +1346,10 @@ DEPENDENCIES
request_store (~> 1.3)
responders (~> 3.0)
retriable (~> 3.1.2)
rouge (~> 3.15.0)
rouge (~> 3.17.0)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 4.0.0.beta3)
rspec-rails (~> 4.0.0.beta4)
rspec-retry (~> 0.6.1)
rspec-set (~> 0.1.3)
rspec_junit_formatter
@ -1375,8 +1374,8 @@ DEPENDENCIES
sidekiq (~> 5.2.7)
sidekiq-cron (~> 1.0)
simple_po_parser (~> 1.1.2)
simplecov (~> 0.16.1)
slack-notifier (~> 1.5.1)
simplecov (~> 0.18.5)
slack-messenger (~> 2.3.3)
snowplow-tracker (~> 0.6.1)
spring (~> 2.0.0)
spring-commands-rspec (~> 1.0.4)
@ -1387,7 +1386,7 @@ DEPENDENCIES
sys-filesystem (~> 1.1.6)
test-prof (~> 0.10.0)
thin (~> 1.7.0)
timecop (~> 0.8.0)
timecop (~> 0.9.1)
toml-rb (~> 1.0.0)
truncato (~> 0.7.11)
u2f (~> 0.2.1)

View file

@ -1 +1 @@
12.8.8
12.9.2

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -1,5 +1,3 @@
import $ from 'jquery';
import _ from 'underscore';
import axios from './lib/utils/axios_utils';
import { joinPaths } from './lib/utils/url_utility';
import flash from '~/flash';
@ -47,6 +45,7 @@ const Api = {
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',
group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
@ -69,7 +68,7 @@ const Api = {
},
// Return groups list. Filtered by query
groups(query, options, callback = $.noop) {
groups(query, options, callback = () => {}) {
const url = Api.buildUrl(Api.groupsPath);
return axios
.get(url, {
@ -107,7 +106,7 @@ const Api = {
},
// Return projects list. Filtered by query
projects(query, options, callback = _.noop) {
projects(query, options, callback = () => {}) {
const url = Api.buildUrl(Api.projectsPath);
const defaults = {
search: query,
@ -475,12 +474,17 @@ const Api = {
return axios.get(url);
},
lsifData(projectPath, commitId, path) {
lsifData(projectPath, commitId, paths) {
const url = Api.buildUrl(this.lsifPath)
.replace(':id', encodeURIComponent(projectPath))
.replace(':commit_id', commitId);
return axios.get(url, { params: { path } });
return axios.get(url, { params: { paths } });
},
environments(id) {
const url = Api.buildUrl(this.environmentsPath).replace(':id', encodeURIComponent(id));
return axios.get(url);
},
buildUrl(url) {

View file

@ -1,5 +1,5 @@
<script>
import _ from 'underscore';
import { escape, debounce } from 'lodash';
import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon, GlFormInput, GlFormGroup } from '@gitlab/ui';
import createFlash from '~/flash';
@ -54,7 +54,7 @@ export default {
s__('Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}'),
{
docsLinkEnd: '</a>',
docsLinkStart: `<a href="${_.escape(this.docsUrl)}">`,
docsLinkStart: `<a href="${escape(this.docsUrl)}">`,
placeholders,
},
false,
@ -118,7 +118,7 @@ export default {
},
methods: {
...mapActions(['addBadge', 'renderBadge', 'saveBadge', 'stopEditing', 'updateBadgeInForm']),
debouncedPreview: _.debounce(function preview() {
debouncedPreview: debounce(function preview() {
this.renderBadge();
}, badgePreviewDelayInMilliseconds),
onCancel() {

View file

@ -1,6 +1,7 @@
import flash from '~/flash';
import $ from 'jquery';
import { sprintf, __ } from '../../locale';
import { __, sprintf } from '~/locale';
import { once } from 'lodash';
// Renders diagrams and flowcharts from text using Mermaid in any element with the
// `js-render-mermaid` class.
@ -18,14 +19,10 @@ import { sprintf, __ } from '../../locale';
// This is an arbitrary number; Can be iterated upon when suitable.
const MAX_CHAR_LIMIT = 5000;
let mermaidModule = {};
function renderMermaids($els) {
if (!$els.length) return;
// A diagram may have been truncated in search results which will cause errors, so abort the render.
if (document.querySelector('body').dataset.page === 'search:show') return;
import(/* webpackChunkName: 'mermaid' */ 'mermaid')
function importMermaidModule() {
return import(/* webpackChunkName: 'mermaid' */ 'mermaid')
.then(mermaid => {
mermaid.initialize({
// mermaid core options
@ -35,68 +32,133 @@ function renderMermaids($els) {
// mermaidAPI options
theme: 'neutral',
flowchart: {
useMaxWidth: true,
htmlLabels: false,
},
securityLevel: 'strict',
});
mermaidModule = mermaid;
return mermaid;
})
.catch(err => {
flash(sprintf(__("Can't load mermaid module: %{err}"), { err }));
// eslint-disable-next-line no-console
console.error(err);
});
}
function fixElementSource(el) {
// Mermaid doesn't like `<br />` tags, so collapse all like tags into `<br>`, which is parsed correctly.
const source = el.textContent.replace(/<br\s*\/>/g, '<br>');
// Remove any extra spans added by the backend syntax highlighting.
Object.assign(el, { textContent: source });
return { source };
}
function renderMermaidEl(el) {
mermaidModule.init(undefined, el, id => {
const source = el.textContent;
const svg = document.getElementById(id);
// As of https://github.com/knsv/mermaid/commit/57b780a0d,
// Mermaid will make two init callbacks:one to initialize the
// flow charts, and another to initialize the Gannt charts.
// Guard against an error caused by double initialization.
if (svg.classList.contains('mermaid')) {
return;
}
svg.classList.add('mermaid');
// pre > code > svg
svg.closest('pre').replaceWith(svg);
// We need to add the original source into the DOM to allow Copy-as-GFM
// to access it.
const sourceEl = document.createElement('text');
sourceEl.classList.add('source');
sourceEl.setAttribute('display', 'none');
sourceEl.textContent = source;
svg.appendChild(sourceEl);
});
}
function renderMermaids($els) {
if (!$els.length) return;
// A diagram may have been truncated in search results which will cause errors, so abort the render.
if (document.querySelector('body').dataset.page === 'search:show') return;
importMermaidModule()
.then(() => {
let renderedChars = 0;
$els.each((i, el) => {
// Mermaid doesn't like `<br />` tags, so collapse all like tags into `<br>`, which is parsed correctly.
const source = el.textContent.replace(/<br\s*\/>/g, '<br>');
const { source } = fixElementSource(el);
/**
* Restrict the rendering to a certain amount of character to
* prevent mermaidjs from hanging up the entire thread and
* causing a DoS.
*/
if ((source && source.length > MAX_CHAR_LIMIT) || renderedChars > MAX_CHAR_LIMIT) {
el.textContent = sprintf(
__(
'Cannot render the image. Maximum character count (%{charLimit}) has been exceeded.',
),
{ charLimit: MAX_CHAR_LIMIT },
);
const html = `
<div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert">
<div>
<div class="display-flex">
<div>${__(
'Warning: Displaying this diagram might cause performance issues on this page.',
)}</div>
<div class="gl-alert-actions">
<button class="js-lazy-render-mermaid btn gl-alert-action btn-warning btn-md new-gl-button">Display</button>
</div>
</div>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
`;
const $parent = $(el).parent();
if (!$parent.hasClass('lazy-alert-shown')) {
$parent.after(html);
$parent.addClass('lazy-alert-shown');
}
return;
}
renderedChars += source.length;
// Remove any extra spans added by the backend syntax highlighting.
Object.assign(el, { textContent: source });
mermaid.init(undefined, el, id => {
const svg = document.getElementById(id);
// As of https://github.com/knsv/mermaid/commit/57b780a0d,
// Mermaid will make two init callbacks:one to initialize the
// flow charts, and another to initialize the Gannt charts.
// Guard against an error caused by double initialization.
if (svg.classList.contains('mermaid')) {
return;
}
svg.classList.add('mermaid');
// pre > code > svg
svg.closest('pre').replaceWith(svg);
// We need to add the original source into the DOM to allow Copy-as-GFM
// to access it.
const sourceEl = document.createElement('text');
sourceEl.classList.add('source');
sourceEl.setAttribute('display', 'none');
sourceEl.textContent = source;
svg.appendChild(sourceEl);
});
renderMermaidEl(el);
});
})
.catch(err => {
flash(`Can't load mermaid module: ${err}`);
flash(sprintf(__('Encountered an error while rendering: %{err}'), { err }));
// eslint-disable-next-line no-console
console.error(err);
});
}
const hookLazyRenderMermaidEvent = once(() => {
$(document.body).on('click', '.js-lazy-render-mermaid', function eventHandler() {
const parent = $(this).closest('.js-lazy-render-mermaid-container');
const pre = parent.prev();
const el = pre.find('.js-render-mermaid');
parent.remove();
renderMermaidEl(el);
});
});
export default function renderMermaid($els) {
if (!$els.length) return;
@ -111,4 +173,6 @@ export default function renderMermaid($els) {
renderMermaids($(this).find('.js-render-mermaid'));
}
});
hookLazyRenderMermaidEvent();
}

View file

@ -1,5 +1,5 @@
import sqljs from 'sql.js';
import { template as _template } from 'underscore';
import { template as _template } from 'lodash';
import axios from '~/lib/utils/axios_utils';
import { successCodes } from '~/lib/utils/http_status';

View file

@ -45,7 +45,13 @@ export default {
<template v-else>
<blob-content-error v-if="viewerError" :viewer-error="viewerError" />
<component :is="viewer" v-else ref="contentViewer" :content="content" />
<component
:is="viewer"
v-else
ref="contentViewer"
:content="content"
:type="activeViewer.fileType"
/>
</template>
</div>
</template>

View file

@ -0,0 +1,49 @@
<script>
import { initEditorLite } from '~/blob/utils';
export default {
props: {
value: {
type: String,
required: true,
},
fileName: {
type: String,
required: false,
default: '',
},
},
data() {
return {
content: this.value,
editor: null,
};
},
watch: {
fileName(newVal) {
this.editor.updateModelLanguage(newVal);
},
},
mounted() {
this.editor = initEditorLite({
el: this.$refs.editor,
blobPath: this.fileName,
blobContent: this.content,
});
},
methods: {
triggerFileChange() {
const val = this.editor.getValue();
this.content = val;
this.$emit('input', val);
},
},
};
</script>
<template>
<div class="file-content code">
<pre id="editor" ref="editor" data-editor-loading @focusout="triggerFileChange">{{
content
}}</pre>
</div>
</template>

View file

@ -0,0 +1,35 @@
<script>
import { GlFormInput } from '@gitlab/ui';
export default {
components: {
GlFormInput,
},
props: {
value: {
type: String,
required: true,
},
},
data() {
return {
name: this.value,
};
},
};
</script>
<template>
<div class="js-file-title file-title-flex-parent">
<gl-form-input
id="snippet_file_name"
v-model="name"
:placeholder="
s__('Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby')
"
name="snippet_file_name"
class="form-control js-snippet-file-name qa-snippet-file-name"
type="text"
@change="$emit('input', name)"
/>
</div>
</template>

View file

@ -9,6 +9,7 @@ import GitignoreSelector from './template_selectors/gitignore_selector';
import LicenseSelector from './template_selectors/license_selector';
import toast from '~/vue_shared/plugins/global_toast';
import { __ } from '~/locale';
import initPopover from '~/blob/suggest_gitlab_ci_yml';
export default class FileTemplateMediator {
constructor({ editor, currentAction, projectId }) {
@ -128,6 +129,7 @@ export default class FileTemplateMediator {
selectTemplateFile(selector, query, data) {
const self = this;
const { name } = selector.config;
const suggestCommitChanges = document.querySelector('.js-suggest-gitlab-ci-yml-commit-changes');
selector.renderLoading();
@ -146,6 +148,10 @@ export default class FileTemplateMediator {
},
},
});
if (suggestCommitChanges) {
initPopover(suggestCommitChanges);
}
})
.catch(err => new Flash(`An error occurred while fetching the template: ${err}`));
}

View file

@ -1,87 +1,17 @@
/* eslint-disable no-new */
import Vue from 'vue';
import axios from '../../lib/utils/axios_utils';
import notebookLab from '../../notebook/index.vue';
import NotebookViewer from './notebook_viewer.vue';
export default () => {
const el = document.getElementById('js-notebook-viewer');
new Vue({
return new Vue({
el,
components: {
notebookLab,
render(createElement) {
return createElement(NotebookViewer, {
props: {
endpoint: el.dataset.endpoint,
},
});
},
data() {
return {
error: false,
loadError: false,
loading: true,
json: {},
};
},
mounted() {
if (gon.katex_css_url) {
const katexStyles = document.createElement('link');
katexStyles.setAttribute('rel', 'stylesheet');
katexStyles.setAttribute('href', gon.katex_css_url);
document.head.appendChild(katexStyles);
}
if (gon.katex_js_url) {
const katexScript = document.createElement('script');
katexScript.addEventListener('load', () => {
this.loadFile();
});
katexScript.setAttribute('src', gon.katex_js_url);
document.head.appendChild(katexScript);
} else {
this.loadFile();
}
},
methods: {
loadFile() {
axios
.get(el.dataset.endpoint)
.then(res => res.data)
.then(data => {
this.json = data;
this.loading = false;
})
.catch(e => {
if (e.status !== 200) {
this.loadError = true;
}
this.error = true;
});
},
},
template: `
<div class="container-fluid md prepend-top-default append-bottom-default">
<div
class="text-center loading"
v-if="loading && !error">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="iPython notebook loading">
</i>
</div>
<notebook-lab
v-if="!loading && !error"
:notebook="json"
code-css-class="code white" />
<p
class="text-center"
v-if="error">
<span v-if="loadError">
An error occurred while loading the file. Please try again later.
</span>
<span v-else>
An error occurred while parsing the file.
</span>
</p>
</div>
`,
});
};

View file

@ -0,0 +1,81 @@
<script>
import axios from '~/lib/utils/axios_utils';
import notebookLab from '~/notebook/index.vue';
import { GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
notebookLab,
GlLoadingIcon,
},
props: {
endpoint: {
type: String,
required: true,
},
},
data() {
return {
error: false,
loadError: false,
loading: true,
json: {},
};
},
mounted() {
if (gon.katex_css_url) {
const katexStyles = document.createElement('link');
katexStyles.setAttribute('rel', 'stylesheet');
katexStyles.setAttribute('href', gon.katex_css_url);
document.head.appendChild(katexStyles);
}
if (gon.katex_js_url) {
const katexScript = document.createElement('script');
katexScript.addEventListener('load', () => {
this.loadFile();
});
katexScript.setAttribute('src', gon.katex_js_url);
document.head.appendChild(katexScript);
} else {
this.loadFile();
}
},
methods: {
loadFile() {
axios
.get(this.endpoint)
.then(res => res.data)
.then(data => {
this.json = data;
this.loading = false;
})
.catch(e => {
if (e.status !== 200) {
this.loadError = true;
}
this.error = true;
});
},
},
};
</script>
<template>
<div
class="js-notebook-viewer-mounted container-fluid md prepend-top-default append-bottom-default"
>
<div v-if="loading && !error" class="text-center loading">
<gl-loading-icon class="mt-5" size="lg" />
</div>
<notebook-lab v-if="!loading && !error" :notebook="json" code-css-class="code white" />
<p v-if="error" class="text-center">
<span v-if="loadError" ref="loadErrorMessage">{{
__('An error occurred while loading the file. Please try again later.')
}}</span>
<span v-else ref="parsingErrorMessage">{{
__('An error occurred while parsing the file.')
}}</span>
</p>
</div>
</template>

View file

@ -1,57 +1,17 @@
import Vue from 'vue';
import pdfLab from '../../pdf/index.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import PdfViewer from './pdf_viewer.vue';
export default () => {
const el = document.getElementById('js-pdf-viewer');
return new Vue({
el,
components: {
pdfLab,
GlLoadingIcon,
render(createElement) {
return createElement(PdfViewer, {
props: {
pdf: el.dataset.endpoint,
},
});
},
data() {
return {
error: false,
loadError: false,
loading: true,
pdf: el.dataset.endpoint,
};
},
methods: {
onLoad() {
this.loading = false;
},
onError(error) {
this.loading = false;
this.loadError = true;
this.error = error;
},
},
template: `
<div class="js-pdf-viewer container-fluid md prepend-top-default append-bottom-default">
<div
class="text-center loading"
v-if="loading && !error">
<gl-loading-icon class="mt-5" size="lg"/>
</div>
<pdf-lab
v-if="!loadError"
:pdf="pdf"
@pdflabload="onLoad"
@pdflaberror="onError" />
<p
class="text-center"
v-if="error">
<span v-if="loadError">
An error occurred while loading the file. Please try again later.
</span>
<span v-else>
An error occurred while decoding the file.
</span>
</p>
</div>
`,
});
};

View file

@ -0,0 +1,49 @@
<script>
import PdfLab from '../../pdf/index.vue';
import { GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
PdfLab,
GlLoadingIcon,
},
props: {
pdf: {
type: String,
required: true,
},
},
data() {
return {
error: false,
loadError: false,
loading: true,
};
},
methods: {
onLoad() {
this.loading = false;
},
onError(error) {
this.loading = false;
this.loadError = true;
this.error = error;
},
},
};
</script>
<template>
<div class="js-pdf-viewer container-fluid md prepend-top-default append-bottom-default">
<div v-if="loading && !error" class="text-center loading">
<gl-loading-icon class="mt-5" size="lg" />
</div>
<pdf-lab v-if="!loadError" :pdf="pdf" @pdflabload="onLoad" @pdflaberror="onError" />
<p v-if="error" class="text-center">
<span v-if="loadError" ref="loadError">
{{ __('An error occurred while loading the file. Please try again later.') }}
</span>
<span v-else>{{ __('An error occurred while decoding the file.') }}</span>
</p>
</div>
</template>

View file

@ -0,0 +1,78 @@
<script>
import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale';
import Cookies from 'js-cookie';
import { glEmojiTag } from '~/emoji';
export default {
beginnerLink:
'https://about.gitlab.com/blog/2018/01/22/a-beginners-guide-to-continuous-integration/',
exampleLink: 'https://docs.gitlab.com/ee/ci/examples/',
bodyMessage: s__(
'MR widget|The pipeline will now run automatically every time you commit code. Pipelines are useful for deploying static web pages, detecting vulnerabilities in dependencies, static or dynamic application security testing (SAST and DAST), and so much more!',
),
modalTitle: sprintf(
__("That's it, well done!%{celebrate}"),
{
celebrate: glEmojiTag('tada'),
},
false,
),
components: {
GlModal,
GlSprintf,
GlLink,
},
props: {
goToPipelinesPath: {
type: String,
required: true,
},
commitCookie: {
type: String,
required: true,
},
},
mounted() {
this.disableModalFromRenderingAgain();
},
methods: {
disableModalFromRenderingAgain() {
Cookies.remove(this.commitCookie);
},
},
};
</script>
<template>
<gl-modal
visible
size="sm"
:title="$options.modalTitle"
modal-id="success-pipeline-modal-id-not-used"
>
<p>
{{ $options.bodyMessage }}
</p>
<gl-sprintf
:message="
s__(`MR widget|Take a look at our %{beginnerLinkStart}Beginner's Guide to Continuous Integration%{beginnerLinkEnd}
and our %{exampleLinkStart}examples of GitLab CI/CD%{exampleLinkEnd}
to see all the cool stuff you can do with it.`)
"
>
<template #beginnerLink="{content}">
<gl-link :href="$options.beginnerLink" target="_blank">
{{ content }}
</gl-link>
</template>
<template #exampleLink="{content}">
<gl-link :href="$options.exampleLink" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
<template #modal-footer>
<a :href="goToPipelinesPath" class="btn btn-success">{{ __('Go to Pipelines') }}</a>
</template>
</gl-modal>
</template>

View file

@ -0,0 +1,121 @@
<script>
import { GlPopover, GlSprintf, GlButton, GlIcon } from '@gitlab/ui';
import Cookies from 'js-cookie';
import { parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
import { glEmojiTag } from '~/emoji';
import Tracking from '~/tracking';
const trackingMixin = Tracking.mixin();
const popoverStates = {
suggest_gitlab_ci_yml: {
title: s__(`suggestPipeline|1/2: Choose a template`),
content: s__(
`suggestPipeline|We recommend the %{boldStart}Code Quality%{boldEnd} template, which will add a report widget to your Merge Requests. This way youll learn about code quality degradations much sooner. %{footerStart} Goodbye technical debt! %{footerEnd}`,
),
emoji: glEmojiTag('wave'),
},
suggest_commit_first_project_gitlab_ci_yml: {
title: s__(`suggestPipeline|2/2: Commit your changes`),
content: s__(
`suggestPipeline|Commit the changes and your pipeline will automatically run for the first time.`,
),
},
};
export default {
components: {
GlPopover,
GlSprintf,
GlIcon,
GlButton,
},
mixins: [trackingMixin],
props: {
target: {
type: String,
required: true,
},
trackLabel: {
type: String,
required: true,
},
dismissKey: {
type: String,
required: true,
},
humanAccess: {
type: String,
required: true,
},
},
data() {
return {
popoverDismissed: parseBoolean(Cookies.get(this.dismissKey)),
tracking: {
label: this.trackLabel,
property: this.humanAccess,
},
};
},
computed: {
suggestTitle() {
return popoverStates[this.trackLabel].title || '';
},
suggestContent() {
return popoverStates[this.trackLabel].content || '';
},
emoji() {
return popoverStates[this.trackLabel].emoji || '';
},
},
mounted() {
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 });
},
trackOnShow() {
if (!this.popoverDismissed) this.track();
},
},
};
</script>
<template>
<gl-popover
v-if="!popoverDismissed"
show
:target="target"
placement="rightbottom"
trigger="manual"
container="viewport"
:css-classes="['suggest-gitlab-ci-yml', 'ml-4']"
>
<template #title>
<span v-html="suggestTitle"></span>
<span class="ml-auto">
<gl-button :aria-label="__('Close')" class="btn-blank" @click="onDismiss">
<gl-icon name="close" aria-hidden="true" />
</gl-button>
</span>
</template>
<gl-sprintf :message="suggestContent">
<template #bold="{content}">
<strong> {{ content }} </strong>
</template>
<template #footer="{content}">
<div class="mt-3">
{{ content }}
<span v-html="emoji"></span>
</div>
</template>
</gl-sprintf>
</gl-popover>
</template>

View file

@ -0,0 +1,17 @@
import Vue from 'vue';
import Popover from './components/popover.vue';
export default el =>
new Vue({
el,
render(createElement) {
return createElement(Popover, {
props: {
target: el.dataset.target,
trackLabel: el.dataset.trackLabel,
dismissKey: el.dataset.dismissKey,
humanAccess: el.dataset.humanAccess,
},
});
},
});

View file

@ -92,10 +92,10 @@ export default class TemplateSelector {
}
startLoadingSpinner() {
this.$dropdownIcon.addClass('fa-spinner fa-spin').removeClass('fa-chevron-down');
this.$dropdownIcon.addClass('spinner').removeClass('fa-chevron-down');
}
stopLoadingSpinner() {
this.$dropdownIcon.addClass('fa-chevron-down').removeClass('fa-spinner fa-spin');
this.$dropdownIcon.addClass('fa-chevron-down').removeClass('spinner');
}
}

View file

@ -0,0 +1,24 @@
/* global ace */
import Editor from '~/editor/editor_lite';
export function initEditorLite({ el, blobPath, blobContent }) {
if (!el) {
throw new Error(`"el" parameter is required to initialize Editor`);
}
let editor;
if (window?.gon?.features?.monacoSnippets) {
editor = new Editor();
editor.createInstance({
el,
blobPath,
blobContent,
});
} else {
editor = ace.edit(el);
}
return editor;
}
export default () => ({});

View file

@ -5,10 +5,43 @@ import { handleLocationHash } from '../../lib/utils/common_utils';
import axios from '../../lib/utils/axios_utils';
import { __ } from '~/locale';
const loadRichBlobViewer = type => {
switch (type) {
case 'balsamiq':
return import(/* webpackChunkName: 'balsamiq_viewer' */ '../balsamiq_viewer');
case 'notebook':
return import(/* webpackChunkName: 'notebook_viewer' */ '../notebook_viewer');
case 'openapi':
return import(/* webpackChunkName: 'openapi_viewer' */ '../openapi_viewer');
case 'pdf':
return import(/* webpackChunkName: 'pdf_viewer' */ '../pdf_viewer');
case 'sketch':
return import(/* webpackChunkName: 'sketch_viewer' */ '../sketch_viewer');
case 'stl':
return import(/* webpackChunkName: 'stl_viewer' */ '../stl_viewer');
default:
return Promise.resolve();
}
};
export const handleBlobRichViewer = (viewer, type) => {
if (!viewer || !type) return;
loadRichBlobViewer(type)
.then(module => module?.default(viewer))
.catch(error => {
Flash(__('Error loading file viewer.'));
throw error;
});
};
export default class BlobViewer {
constructor() {
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
const type = viewer?.dataset?.richType;
BlobViewer.initAuxiliaryViewer();
BlobViewer.initRichViewer();
handleBlobRichViewer(viewer, type);
this.initMainViewers();
}
@ -20,42 +53,6 @@ export default class BlobViewer {
BlobViewer.loadViewer(auxiliaryViewer);
}
static initRichViewer() {
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
if (!viewer || !viewer.dataset.richType) return;
const initViewer = promise =>
promise
.then(module => module.default(viewer))
.catch(error => {
Flash(__('Error loading file viewer.'));
throw error;
});
switch (viewer.dataset.richType) {
case 'balsamiq':
initViewer(import(/* webpackChunkName: 'balsamiq_viewer' */ '../balsamiq_viewer'));
break;
case 'notebook':
initViewer(import(/* webpackChunkName: 'notebook_viewer' */ '../notebook_viewer'));
break;
case 'openapi':
initViewer(import(/* webpackChunkName: 'openapi_viewer' */ '../openapi_viewer'));
break;
case 'pdf':
initViewer(import(/* webpackChunkName: 'pdf_viewer' */ '../pdf_viewer'));
break;
case 'sketch':
initViewer(import(/* webpackChunkName: 'sketch_viewer' */ '../sketch_viewer'));
break;
case 'stl':
initViewer(import(/* webpackChunkName: 'stl_viewer' */ '../stl_viewer'));
break;
default:
break;
}
}
initMainViewers() {
this.$fileHolder = $('.file-holder');
if (!this.$fileHolder.length) return;

View file

@ -4,11 +4,13 @@ import $ from 'jquery';
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';
export default () => {
const editBlobForm = $('.js-edit-blob-form');
const uploadBlobForm = $('.js-upload-blob-form');
const deleteBlobForm = $('.js-delete-blob-form');
const suggestEl = document.querySelector('.js-suggest-gitlab-ci-yml');
if (editBlobForm.length) {
const urlRoot = editBlobForm.data('relativeUrlRoot');
@ -56,4 +58,8 @@ export default () => {
if (deleteBlobForm.length) {
new NewCommitForm(deleteBlobForm);
}
if (suggestEl) {
initPopover(suggestEl);
}
};

View file

@ -1,7 +1,7 @@
import $ from 'jquery';
import Sortable from 'sortablejs';
import Vue from 'vue';
import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui';
import { GlButtonGroup, GlButton, 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';
@ -14,6 +14,7 @@ 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 Vue.extend({
components: {
@ -24,6 +25,7 @@ export default Vue.extend({
GlButtonGroup,
IssueCount,
GlButton,
GlLabel,
GlTooltip,
},
directives: {
@ -95,6 +97,9 @@ export default Vue.extend({
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
watch: {
filter: {
@ -145,6 +150,10 @@ export default Vue.extend({
}
},
methods: {
showScopedLabels(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
showNewIssueForm() {
this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
},

View file

@ -65,11 +65,12 @@ export default {
},
showIssue(e) {
if (e.target.classList.contains('js-no-trigger')) return;
if (this.showDetail) {
this.showDetail = false;
// If CMD or CTRL is clicked
const isMultiSelect = this.canMultiSelect && (e.ctrlKey || e.metaKey);
// If CMD or CTRL is clicked
const isMultiSelect = this.canMultiSelect && (e.ctrlKey || e.metaKey);
if (this.showDetail || isMultiSelect) {
this.showDetail = false;
if (boardsStore.detail.issue && boardsStore.detail.issue.id === this.issue.id) {
eventHub.$emit('clearDetailIssue', isMultiSelect);

View file

@ -1,7 +1,6 @@
<script>
import { Sortable, MultiDrag } from 'sortablejs';
import { GlLoadingIcon } from '@gitlab/ui';
import _ from 'underscore';
import boardNewIssue from './board_new_issue.vue';
import boardCard from './board_card.vue';
import eventHub from '../eventhub';
@ -266,11 +265,12 @@ export default {
* same list or the other list. Don't remove items if it's same list.
*/
const isSameList = toList && toList.id === this.list.id;
if (toList && !isSameList && boardsStore.shouldRemoveIssue(this.list, toList)) {
const issues = items.map(item => this.list.findIssue(Number(item.dataset.issueId)));
if (_.compact(issues).length && !boardsStore.issuesAreContiguous(this.list, issues)) {
if (
issues.filter(Boolean).length &&
!boardsStore.issuesAreContiguous(this.list, issues)
) {
const indexes = [];
const ids = this.list.issues.map(i => i.id);
issues.forEach(issue => {

View file

@ -2,6 +2,7 @@
import $ from 'jquery';
import Vue from 'vue';
import { GlLabel } from '@gitlab/ui';
import Flash from '~/flash';
import { sprintf, __ } from '~/locale';
import Sidebar from '~/right_sidebar';
@ -22,6 +23,7 @@ export default Vue.extend({
components: {
AssigneeTitle,
Assignees,
GlLabel,
SidebarEpicsSelect: () =>
import('ee_component/sidebar/components/sidebar_item_epics_select.vue'),
RemoveBtn,
@ -67,6 +69,9 @@ export default Vue.extend({
selectedLabels() {
return this.hasLabels ? this.issue.labels.map(l => l.title).join(',') : '';
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
watch: {
detail: {
@ -147,8 +152,5 @@ export default Vue.extend({
showScopedLabels(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
},
});

View file

@ -1,5 +1,5 @@
<script>
import { throttle } from 'underscore';
import { throttle } from 'lodash';
import {
GlLoadingIcon,
GlSearchBoxByType,
@ -10,6 +10,11 @@ import {
} from '@gitlab/ui';
import httpStatusCodes from '~/lib/utils/http_status';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import projectQuery from '../queries/project_boards.query.graphql';
import groupQuery from '../queries/group_boards.query.graphql';
import boardsStore from '../stores/boards_store';
import BoardForm from './board_form.vue';
@ -88,8 +93,9 @@ export default {
},
data() {
return {
loading: true,
hasScrollFade: false,
loadingBoards: 0,
loadingRecentBoards: false,
scrollFadeInitialized: false,
boards: [],
recentBoards: [],
@ -102,6 +108,12 @@ export default {
};
},
computed: {
parentType() {
return this.groupId ? 'group' : 'project';
},
loading() {
return this.loadingRecentBoards && this.loadingBoards;
},
currentPage() {
return this.state.currentPage;
},
@ -110,14 +122,6 @@ export default {
board.name.toLowerCase().includes(this.filterTerm.toLowerCase()),
);
},
reload: {
get() {
return this.state.reload;
},
set(newValue) {
this.state.reload = newValue;
},
},
board() {
return this.state.currentBoard;
},
@ -142,16 +146,6 @@ export default {
this.scrollFadeInitialized = false;
this.$nextTick(this.setScrollFade);
},
reload() {
if (this.reload) {
this.boards = [];
this.recentBoards = [];
this.loading = true;
this.reload = false;
this.loadBoards(false);
}
},
},
created() {
boardsStore.setCurrentBoard(this.currentBoard);
@ -165,49 +159,71 @@ export default {
return;
}
const recentBoardsPromise = new Promise((resolve, reject) =>
boardsStore
.recentBoards()
.then(resolve)
.catch(err => {
/**
* If user is unauthorized we'd still want to resolve the
* request to display all boards.
*/
if (err.response.status === httpStatusCodes.UNAUTHORIZED) {
resolve({ data: [] }); // recent boards are empty
return;
}
reject(err);
}),
);
this.$apollo.addSmartQuery('boards', {
variables() {
return { fullPath: this.state.endpoints.fullPath };
},
query() {
return this.groupId ? groupQuery : projectQuery;
},
loadingKey: 'loadingBoards',
update(data) {
if (!data?.[this.parentType]) {
return [];
}
return data[this.parentType].boards.edges.map(({ node }) => ({
id: getIdFromGraphQLId(node.id),
name: node.name,
}));
},
});
Promise.all([boardsStore.allBoards(), recentBoardsPromise])
.then(([allBoards, recentBoards]) => [allBoards.data, recentBoards.data])
.then(([allBoardsJson, recentBoardsJson]) => {
this.loading = false;
this.boards = allBoardsJson;
this.recentBoards = recentBoardsJson;
this.loadingRecentBoards = true;
boardsStore
.recentBoards()
.then(res => {
this.recentBoards = res.data;
})
.catch(err => {
/**
* If user is unauthorized we'd still want to resolve the
* request to display all boards.
*/
if (err?.response?.status === httpStatusCodes.UNAUTHORIZED) {
this.recentBoards = []; // recent boards are empty
return;
}
throw err;
})
.then(() => this.$nextTick()) // Wait for boards list in DOM
.then(() => {
this.setScrollFade();
})
.catch(() => {
this.loading = false;
.catch(() => {})
.finally(() => {
this.loadingRecentBoards = false;
});
},
isScrolledUp() {
const { content } = this.$refs;
if (!content) {
return false;
}
const currentPosition = this.contentClientHeight + content.scrollTop;
return content && currentPosition < this.maxPosition;
return currentPosition < this.maxPosition;
},
initScrollFade() {
this.scrollFadeInitialized = true;
const { content } = this.$refs;
if (!content) {
return;
}
this.scrollFadeInitialized = true;
this.contentClientHeight = content.clientHeight;
this.maxPosition = content.scrollHeight;
},

View file

@ -1,7 +1,7 @@
<script>
import _ from 'underscore';
import { sortBy } from 'lodash';
import { mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui';
import { GlLabel, GlTooltipDirective } from '@gitlab/ui';
import issueCardInner from 'ee_else_ce/boards/mixins/issue_card_inner';
import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
@ -10,18 +10,17 @@ import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_
import IssueDueDate from './issue_due_date.vue';
import IssueTimeEstimate from './issue_time_estimate.vue';
import boardsStore from '../stores/boards_store';
import IssueCardInnerScopedLabel from './issue_card_inner_scoped_label.vue';
import { isScopedLabel } from '~/lib/utils/common_utils';
export default {
components: {
GlLabel,
Icon,
UserAvatarLink,
TooltipOnTruncate,
IssueDueDate,
IssueTimeEstimate,
IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
IssueCardInnerScopedLabel,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -101,10 +100,7 @@ export default {
return !groupId ? referencePath.split('#')[0] : null;
},
orderedLabels() {
return _.chain(this.issue.labels)
.filter(this.isNonListLabel)
.sortBy('title')
.value();
return sortBy(this.issue.labels.filter(this.isNonListLabel), 'title');
},
helpLink() {
return boardsStore.scopedLabels.helpLink;
@ -145,12 +141,6 @@ export default {
boardsStore.toggleFilter(filter);
},
labelStyle(label) {
return {
backgroundColor: label.color,
color: label.textColor,
};
},
showScopedLabel(label) {
return boardsStore.scopedLabels.enabled && isScopedLabel(label);
},
@ -177,34 +167,23 @@ export default {
class="confidential-icon append-right-4"
:aria-label="__('Confidential')"
/>
<a :href="issue.path" :title="issue.title" class="js-no-trigger" @mousemove.stop>
{{ issue.title }}
</a>
<a :href="issue.path" :title="issue.title" class="js-no-trigger" @mousemove.stop>{{
issue.title
}}</a>
</h4>
</div>
<div v-if="showLabelFooter" class="board-card-labels prepend-top-4 d-flex flex-wrap">
<template v-for="label in orderedLabels">
<issue-card-inner-scoped-label
v-if="showScopedLabel(label)"
<gl-label
:key="label.id"
:label="label"
:label-style="labelStyle(label)"
:background-color="label.color"
:title="label.title"
:description="label.description"
size="sm"
:scoped="showScopedLabel(label)"
:scoped-labels-documentation-link="helpLink"
@scoped-label-click="filterByLabel($event)"
/>
<button
v-else
:key="label.id"
v-gl-tooltip
:style="labelStyle(label)"
:title="label.description"
class="badge color-label append-right-4 prepend-top-4"
type="button"
@click="filterByLabel(label)"
>
{{ label.title }}
</button>
/>
</template>
</div>
<div class="board-card-footer d-flex justify-content-between align-items-end">
@ -225,7 +204,7 @@ export default {
#{{ issue.iid }}
</span>
<span class="board-info-items prepend-top-8 d-inline-block">
<issue-due-date v-if="issue.dueDate" :date="issue.dueDate" />
<issue-due-date v-if="issue.dueDate" :date="issue.dueDate" :closed="issue.closed" />
<issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" />
<issue-card-weight
v-if="validIssueWeight"

View file

@ -1,45 +0,0 @@
<script>
import { GlLink, GlTooltip } from '@gitlab/ui';
export default {
components: {
GlTooltip,
GlLink,
},
props: {
label: {
type: Object,
required: true,
},
labelStyle: {
type: Object,
required: true,
},
scopedLabelsDocumentationLink: {
type: String,
required: true,
},
},
};
</script>
<template>
<span
class="d-inline-block position-relative scoped-label-wrapper append-right-4 prepend-top-4 board-label"
>
<a @click="$emit('scoped-label-click', label)">
<span :ref="'labelTitleRef'" :style="labelStyle" class="badge label color-label">
{{ label.title }}
</span>
<gl-tooltip :target="() => $refs.labelTitleRef" placement="top" boundary="viewport">
<span class="font-weight-bold scoped-label-tooltip-title">{{ __('Scoped label') }}</span
><br />
{{ label.description }}
</gl-tooltip>
</a>
<gl-link :href="scopedLabelsDocumentationLink" target="_blank" class="label scoped-label"
><i class="fa fa-question-circle" :style="labelStyle"></i
></gl-link>
</span>
</template>

View file

@ -16,6 +16,11 @@ export default {
GlTooltip,
},
props: {
closed: {
type: Boolean,
required: false,
default: false,
},
date: {
type: String,
required: true,
@ -66,7 +71,7 @@ export default {
return getDayDifference(today, this.issueDueDate);
},
isPastDue() {
if (this.timeDifference >= 0) return false;
if (this.timeDifference >= 0 || this.closed) return false;
return true;
},
standardDateFormat() {
@ -92,7 +97,8 @@ export default {
}}</time>
</span>
<gl-tooltip :target="() => $refs.issueDueDate" :placement="tooltipPlacement">
<span class="bold">{{ __('Due date') }}</span> <br />
<span class="bold">{{ __('Due date') }}</span>
<br />
<span :class="{ 'text-danger-muted': isPastDue }">{{ title }}</span>
</gl-tooltip>
</span>

View file

@ -1,6 +1,6 @@
<script>
import $ from 'jquery';
import _ from 'underscore';
import { escape } from 'lodash';
import { GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
@ -83,7 +83,7 @@ export default {
}" data-project-name="${project.name}" data-project-name-with-namespace="${
project.name_with_namespace
}">
${_.escape(project.name_with_namespace)}
${escape(project.name_with_namespace)}
</a>
</li>
`;

View file

@ -84,7 +84,6 @@ export default () => {
rootPath: $boardApp.dataset.rootPath,
bulkUpdatePath: $boardApp.dataset.bulkUpdatePath,
detailIssue: boardsStore.detail,
defaultAvatar: $boardApp.dataset.defaultAvatar,
},
computed: {
detailIssueVisible() {
@ -98,6 +97,7 @@ export default () => {
listsEndpoint: this.listsEndpoint,
bulkUpdatePath: this.bulkUpdatePath,
boardId: this.boardId,
fullPath: $boardApp.dataset.fullPath,
});
boardsStore.rootPath = this.boardsEndpoint;
@ -129,13 +129,10 @@ export default () => {
position = -1;
}
boardsStore.addList(
{
...listObj,
position,
},
this.defaultAvatar,
);
boardsStore.addList({
...listObj,
position,
});
});
boardsStore.addBlankState();

View file

@ -26,7 +26,6 @@ export function getBoardSortableDefaultOptions(obj) {
scrollSpeed: 20,
onStart: sortableStart,
onEnd: sortableEnd,
fallbackTolerance: 1,
});
Object.keys(obj).forEach(key => {

View file

@ -1,9 +1,9 @@
export default class ListAssignee {
constructor(obj, defaultAvatar) {
constructor(obj) {
this.id = obj.id;
this.name = obj.name;
this.username = obj.username;
this.avatar = obj.avatar_url || obj.avatar || defaultAvatar;
this.avatar = obj.avatar_url || obj.avatar || gon.default_avatar_url;
this.path = obj.path;
this.state = obj.state;
this.webUrl = obj.web_url || obj.webUrl;

View file

@ -10,7 +10,7 @@ import IssueProject from './project';
import boardsStore from '../stores/boards_store';
class ListIssue {
constructor(obj, defaultAvatar) {
constructor(obj) {
this.subscribed = obj.subscribed;
this.labels = [];
this.assignees = [];
@ -19,42 +19,14 @@ class ListIssue {
this.isFetching = {
subscriptions: true,
};
this.closed = obj.closed;
this.isLoading = {};
this.refreshData(obj, defaultAvatar);
this.refreshData(obj);
}
refreshData(obj, defaultAvatar) {
this.id = obj.id;
this.iid = obj.iid;
this.title = obj.title;
this.confidential = obj.confidential;
this.dueDate = obj.due_date;
this.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
this.referencePath = obj.reference_path;
this.path = obj.real_path;
this.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
this.project_id = obj.project_id;
this.timeEstimate = obj.time_estimate;
this.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
this.blocked = obj.blocked;
if (obj.project) {
this.project = new IssueProject(obj.project);
}
if (obj.milestone) {
this.milestone = new ListMilestone(obj.milestone);
this.milestone_id = obj.milestone.id;
}
if (obj.labels) {
this.labels = obj.labels.map(label => new ListLabel(label));
}
if (obj.assignees) {
this.assignees = obj.assignees.map(a => new ListAssignee(a, defaultAvatar));
}
refreshData(obj) {
boardsStore.refreshIssueData(this, obj);
}
addLabel(label) {

View file

@ -36,7 +36,7 @@ const TYPES = {
};
class List {
constructor(obj, defaultAvatar) {
constructor(obj) {
this.id = obj.id;
this._uid = this.guid();
this.position = obj.position;
@ -55,7 +55,6 @@ class List {
this.maxIssueCount = Object.hasOwnProperty.call(obj, 'max_issue_count')
? obj.max_issue_count
: 0;
this.defaultAvatar = defaultAvatar;
if (obj.label) {
this.label = new ListLabel(obj.label);
@ -156,7 +155,7 @@ class List {
createIssues(data) {
data.forEach(issueObj => {
this.addIssue(new ListIssue(issueObj, this.defaultAvatar));
this.addIssue(new ListIssue(issueObj));
});
}

View file

@ -1,7 +1,15 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import BoardsSelector from '~/boards/components/boards_selector.vue';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
export default () => {
const boardsSwitcherElement = document.getElementById('js-multiple-boards-switcher');
return new Vue({
@ -9,6 +17,7 @@ export default () => {
components: {
BoardsSelector,
},
apolloProvider,
data() {
const { dataset } = boardsSwitcherElement;

View file

@ -0,0 +1,4 @@
fragment BoardFragment on Board {
id,
name
}

View file

@ -0,0 +1,13 @@
#import "ee_else_ce/boards/queries/board.fragment.graphql"
query group_boards($fullPath: ID!) {
group(fullPath: $fullPath) {
boards {
edges {
node {
...BoardFragment
}
}
}
}
}

View file

@ -0,0 +1,13 @@
#import "ee_else_ce/boards/queries/board.fragment.graphql"
query project_boards($fullPath: ID!) {
project(fullPath: $fullPath) {
boards {
edges {
node {
...BoardFragment
}
}
}
}
}

View file

@ -2,7 +2,7 @@
/* global List */
import $ from 'jquery';
import _ from 'underscore';
import { sortBy } from 'lodash';
import Vue from 'vue';
import Cookies from 'js-cookie';
import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
@ -12,6 +12,10 @@ import axios from '~/lib/utils/axios_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import eventHub from '../eventhub';
import { ListType } from '../constants';
import IssueProject from '../models/project';
import ListLabel from '../models/label';
import ListAssignee from '../models/assignee';
import ListMilestone from '../models/milestone';
const boardsStore = {
disabled: false,
@ -30,7 +34,6 @@ const boardsStore = {
labels: [],
},
currentPage: '',
reload: false,
endpoints: {},
},
detail: {
@ -42,7 +45,14 @@ const boardsStore = {
},
multiSelect: { list: [] },
setEndpoints({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) {
setEndpoints({
boardsEndpoint,
listsEndpoint,
bulkUpdatePath,
boardId,
recentBoardsEndpoint,
fullPath,
}) {
const listsEndpointGenerate = `${listsEndpoint}/generate.json`;
this.state.endpoints = {
boardsEndpoint,
@ -50,6 +60,7 @@ const boardsStore = {
listsEndpoint,
listsEndpointGenerate,
bulkUpdatePath,
fullPath,
recentBoardsEndpoint: `${recentBoardsEndpoint}.json`,
};
},
@ -61,13 +72,11 @@ const boardsStore = {
};
},
showPage(page) {
this.state.reload = false;
this.state.currentPage = page;
},
addList(listObj, defaultAvatar) {
const list = new List(listObj, defaultAvatar);
this.state.lists = _.sortBy([...this.state.lists, list], 'position');
addList(listObj) {
const list = new List(listObj);
this.state.lists = sortBy([...this.state.lists, list], 'position');
return list;
},
new(listObj) {
@ -80,7 +89,7 @@ const boardsStore = {
// Remove any new issues from the backlog
// as they will be visible in the new list
list.issues.forEach(backlogList.removeIssue.bind(backlogList));
this.state.lists = _.sortBy(this.state.lists, 'position');
this.state.lists = sortBy(this.state.lists, 'position');
})
.catch(() => {
// https://gitlab.com/gitlab-org/gitlab-foss/issues/30821
@ -184,10 +193,9 @@ const boardsStore = {
moveMultipleIssuesToList({ listFrom, listTo, issues, newIndex }) {
const issueTo = issues.map(issue => listTo.findIssue(issue.id));
const issueLists = _.flatten(issues.map(issue => issue.getLists()));
const issueLists = issues.map(issue => issue.getLists()).flat();
const listLabels = issueLists.map(list => list.label);
const hasMoveableIssues = _.compact(issueTo).length > 0;
const hasMoveableIssues = issueTo.filter(Boolean).length > 0;
if (!hasMoveableIssues) {
// Check if target list assignee is already present in this issue
@ -335,7 +343,8 @@ const boardsStore = {
return (
(listTo.type !== 'label' && listFrom.type === 'assignee') ||
(listTo.type !== 'assignee' && listFrom.type === 'label') ||
listFrom.type === 'backlog'
listFrom.type === 'backlog' ||
listFrom.type === 'closed'
);
},
moveIssueInList(list, issue, oldIndex, newIndex, idArray) {
@ -539,10 +548,6 @@ const boardsStore = {
return axios.post(endpoint);
},
allBoards() {
return axios.get(this.generateBoardsPath());
},
recentBoards() {
return axios.get(this.state.endpoints.recentBoardsEndpoint);
},
@ -595,6 +600,38 @@ const boardsStore = {
clearMultiSelect() {
this.multiSelect.list = [];
},
refreshIssueData(issue, obj) {
issue.id = obj.id;
issue.iid = obj.iid;
issue.title = obj.title;
issue.confidential = obj.confidential;
issue.dueDate = obj.due_date;
issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
issue.referencePath = obj.reference_path;
issue.path = obj.real_path;
issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
issue.project_id = obj.project_id;
issue.timeEstimate = obj.time_estimate;
issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
issue.blocked = obj.blocked;
if (obj.project) {
issue.project = new IssueProject(obj.project);
}
if (obj.milestone) {
issue.milestone = new ListMilestone(obj.milestone);
issue.milestone_id = obj.milestone.id;
}
if (obj.labels) {
issue.labels = obj.labels.map(label => new ListLabel(label));
}
if (obj.assignees) {
issue.assignees = obj.assignees.map(a => new ListAssignee(a));
}
},
};
BoardsStoreEE.initEESpecific(boardsStore);

View file

@ -6,16 +6,14 @@ const handleOnDismiss = ({ currentTarget }) => {
dataset: { id },
} = currentTarget;
Cookies.set(`hide_broadcast_notification_message_${id}`, true);
Cookies.set(`hide_broadcast_message_${id}`, true);
const notification = document.querySelector(`.js-broadcast-notification-${id}`);
notification.parentNode.removeChild(notification);
};
export default () => {
const dismissButton = document.querySelector('.js-dismiss-current-broadcast-notification');
if (dismissButton) {
dismissButton.addEventListener('click', handleOnDismiss);
}
document
.querySelectorAll('.js-dismiss-current-broadcast-notification')
.forEach(dismissButton => dismissButton.addEventListener('click', handleOnDismiss));
};

View file

@ -0,0 +1,224 @@
<script>
import { __ } from '~/locale';
import { mapActions, mapState } from 'vuex';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
import {
GlButton,
GlModal,
GlFormSelect,
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlFormCheckbox,
GlLink,
GlIcon,
} from '@gitlab/ui';
export default {
modalId: ADD_CI_VARIABLE_MODAL_ID,
components: {
GlButton,
GlModal,
GlFormSelect,
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlFormCheckbox,
GlLink,
GlIcon,
},
computed: {
...mapState([
'projectId',
'environments',
'typeOptions',
'variable',
'variableBeingEdited',
'isGroup',
'maskableRegex',
]),
canSubmit() {
if (this.variableData.masked && this.maskedState === false) {
return false;
}
return 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 !== '';
},
maskedState() {
if (this.displayMaskedError) {
return false;
}
return null;
},
variableData() {
return this.variableBeingEdited || this.variable;
},
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');
},
},
methods: {
...mapActions([
'addVariable',
'updateVariable',
'resetEditing',
'displayInputValue',
'clearModal',
'deleteVariable',
]),
updateOrAddVariable() {
if (this.variableBeingEdited) {
this.updateVariable(this.variableBeingEdited);
} else {
this.addVariable();
}
this.hideModal();
},
resetModalHandler() {
if (this.variableBeingEdited) {
this.resetEditing();
} else {
this.clearModal();
}
},
hideModal() {
this.$refs.modal.hide();
},
deleteVarAndClose() {
this.deleteVariable(this.variableBeingEdited);
this.hideModal();
},
},
};
</script>
<template>
<gl-modal
ref="modal"
:modal-id="$options.modalId"
:title="modalActionText"
@hidden="resetModalHandler"
>
<form>
<gl-form-group :label="__('Key')" label-for="ci-variable-key">
<gl-form-input
id="ci-variable-key"
v-model="variableData.key"
data-qa-selector="variable_key"
/>
</gl-form-group>
<gl-form-group
:label="__('Value')"
label-for="ci-variable-value"
:state="maskedState"
:invalid-feedback="maskedFeedback"
>
<gl-form-textarea
id="ci-variable-value"
v-model="variableData.secret_value"
rows="3"
max-rows="6"
data-qa-selector="variable_value"
/>
</gl-form-group>
<div class="d-flex">
<gl-form-group
:label="__('Type')"
label-for="ci-variable-type"
class="w-50 append-right-15"
:class="{ 'w-100': isGroup }"
>
<gl-form-select
id="ci-variable-type"
v-model="variableData.variable_type"
:options="typeOptions"
/>
</gl-form-group>
<gl-form-group
v-if="!isGroup"
:label="__('Environment scope')"
label-for="ci-variable-env"
class="w-50"
>
<gl-form-select
id="ci-variable-env"
v-model="variableData.environment_scope"
:options="environments"
/>
</gl-form-group>
</div>
<gl-form-group :label="__('Flags')" label-for="ci-variable-flags">
<gl-form-checkbox v-model="variableData.protected" class="mb-0">
{{ __('Protect variable') }}
<gl-link href="/help/ci/variables/README#protected-environment-variables">
<gl-icon name="question" :size="12" />
</gl-link>
<p class="prepend-top-4 text-secondary">
{{ __('Export variable to pipelines running on protected branches and tags only.') }}
</p>
</gl-form-checkbox>
<gl-form-checkbox
ref="masked-ci-variable"
v-model="variableData.masked"
data-qa-selector="variable_masked"
>
{{ __('Mask variable') }}
<gl-link href="/help/ci/variables/README#masked-variables">
<gl-icon name="question" :size="12" />
</gl-link>
<p class="prepend-top-4 append-bottom-0 text-secondary">
{{ __('Variable will be masked in job logs.') }}
<span
:class="{
'bold text-plain': displayMaskedError,
}"
>
{{ __('Requires values to meet regular expression requirements.') }}</span
>
<gl-link href="/help/ci/variables/README#masked-variables">{{
__('More information')
}}</gl-link>
</p>
</gl-form-checkbox>
</gl-form-group>
</form>
<template #modal-footer>
<gl-button @click="hideModal">{{ __('Cancel') }}</gl-button>
<gl-button
v-if="variableBeingEdited"
ref="deleteCiVariable"
category="secondary"
variant="danger"
@click="deleteVarAndClose"
>{{ __('Delete variable') }}</gl-button
>
<gl-button
ref="updateOrAddVariable"
:disabled="!canSubmit"
variant="success"
@click="updateOrAddVariable"
>{{ modalActionText }}
</gl-button>
</template>
</gl-modal>
</template>

View file

@ -0,0 +1,55 @@
<script>
import { GlPopover, GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
export default {
maxTextLength: 95,
components: {
GlPopover,
GlIcon,
GlButton,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
target: {
type: String,
required: true,
},
value: {
type: String,
required: true,
},
tooltipText: {
type: String,
required: true,
},
},
computed: {
displayValue() {
if (this.value.length > this.$options.maxTextLength) {
return `${this.value.substring(0, this.$options.maxTextLength)}...`;
}
return this.value;
},
},
};
</script>
<template>
<div id="popover-container">
<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
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>
</div>
</gl-popover>
</div>
</template>

View file

@ -0,0 +1,32 @@
<script>
import CiVariableModal from './ci_variable_modal.vue';
import CiVariableTable from './ci_variable_table.vue';
import { mapState, mapActions } from 'vuex';
export default {
components: {
CiVariableModal,
CiVariableTable,
},
computed: {
...mapState(['isGroup']),
},
mounted() {
if (!this.isGroup) {
this.fetchEnvironments();
}
},
methods: {
...mapActions(['fetchEnvironments']),
},
};
</script>
<template>
<div class="row">
<div class="col-lg-12">
<ci-variable-table />
<ci-variable-modal />
</div>
</div>
</template>

View file

@ -0,0 +1,184 @@
<script>
import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { mapState, mapActions } from 'vuex';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
import CiVariablePopover from './ci_variable_popover.vue';
export default {
modalId: ADD_CI_VARIABLE_MODAL_ID,
trueIcon: 'mobile-issue-close',
falseIcon: 'close',
iconSize: 16,
fields: [
{
key: 'variable_type',
label: s__('CiVariables|Type'),
customStyle: { width: '70px' },
},
{
key: 'key',
label: s__('CiVariables|Key'),
tdClass: 'text-plain',
sortable: true,
customStyle: { width: '40%' },
},
{
key: 'value',
label: s__('CiVariables|Value'),
tdClass: 'qa-ci-variable-input-value',
customStyle: { width: '40%' },
},
{
key: 'protected',
label: s__('CiVariables|Protected'),
customStyle: { width: '100px' },
},
{
key: 'masked',
label: s__('CiVariables|Masked'),
customStyle: { width: '100px' },
},
{
key: 'environment_scope',
label: s__('CiVariables|Environments'),
customStyle: { width: '20%' },
},
{
key: 'actions',
label: '',
customStyle: { width: '35px' },
},
],
components: {
GlTable,
GlButton,
GlIcon,
CiVariablePopover,
},
directives: {
GlModalDirective,
},
computed: {
...mapState(['variables', 'valuesHidden', 'isGroup', 'isLoading', 'isDeleting']),
valuesButtonText() {
return this.valuesHidden ? __('Reveal values') : __('Hide values');
},
tableIsNotEmpty() {
return this.variables && this.variables.length > 0;
},
fields() {
if (this.isGroup) {
return this.$options.fields.filter(field => field.key !== 'environment_scope');
}
return this.$options.fields;
},
},
mounted() {
this.fetchVariables();
},
methods: {
...mapActions(['fetchVariables', 'toggleValues', 'editVariable']),
},
};
</script>
<template>
<div class="ci-variable-table">
<gl-table
:fields="fields"
:items="variables"
tbody-tr-class="js-ci-variable-row"
sort-by="key"
sort-direction="asc"
stacked="lg"
fixed
show-empty
sort-icon-left
no-sort-reset
>
<template #table-colgroup="scope">
<col v-for="field in scope.fields" :key="field.key" :style="field.customStyle" />
</template>
<template #cell(key)="{ item }">
<div class="d-flex truncated-container">
<span :id="`ci-variable-key-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
item.key
}}</span>
<ci-variable-popover
:target="`ci-variable-key-${item.id}`"
:value="item.key"
:tooltip-text="__('Copy key')"
/>
</div>
</template>
<template #cell(value)="{ item }">
<span v-if="valuesHidden">*********************</span>
<div v-else class="d-flex truncated-container">
<span :id="`ci-variable-value-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
item.value
}}</span>
<ci-variable-popover
:target="`ci-variable-value-${item.id}`"
:value="item.value"
:tooltip-text="__('Copy value')"
/>
</div>
</template>
<template #cell(protected)="{ item }">
<gl-icon v-if="item.protected" :size="$options.iconSize" :name="$options.trueIcon" />
<gl-icon v-else :size="$options.iconSize" :name="$options.falseIcon" />
</template>
<template #cell(masked)="{ item }">
<gl-icon v-if="item.masked" :size="$options.iconSize" :name="$options.trueIcon" />
<gl-icon v-else :size="$options.iconSize" :name="$options.falseIcon" />
</template>
<template #cell(environment_scope)="{ item }">
<div class="d-flex truncated-container">
<span :id="`ci-variable-env-${item.id}`" class="d-inline-block mw-100 text-truncate">{{
item.environment_scope
}}</span>
<ci-variable-popover
:target="`ci-variable-env-${item.id}`"
:value="item.environment_scope"
:tooltip-text="__('Copy environment')"
/>
</div>
</template>
<template #cell(actions)="{ item }">
<gl-button
ref="edit-ci-variable"
v-gl-modal-directive="$options.modalId"
@click="editVariable(item)"
>
<gl-icon :size="$options.iconSize" name="pencil" />
</gl-button>
</template>
<template #empty>
<p ref="empty-variables" class="text-center empty-variables text-plain">
{{ __('There are no variables yet.') }}
</p>
</template>
</gl-table>
<div
class="ci-variable-actions d-flex justify-content-end"
:class="{ 'justify-content-center': !tableIsNotEmpty }"
>
<gl-button
v-if="tableIsNotEmpty"
ref="secret-value-reveal-button"
data-qa-selector="reveal_ci_variable_value"
class="append-right-8"
@click="toggleValues(!valuesHidden)"
>{{ valuesButtonText }}</gl-button
>
<gl-button
ref="add-ci-variable"
v-gl-modal-directive="$options.modalId"
data-qa-selector="add_ci_variable"
variant="success"
>{{ __('Add Variable') }}</gl-button
>
</div>
</div>
</template>

View file

@ -0,0 +1,16 @@
import { __ } from '~/locale';
// eslint-disable import/prefer-default-export
export const ADD_CI_VARIABLE_MODAL_ID = 'add-ci-variable';
export const displayText = {
variableText: __('Var'),
fileText: __('File'),
allEnvironmentsText: __('All'),
};
export const types = {
variableType: 'env_var',
fileType: 'file',
allEnvironmentsType: '*',
};

View file

@ -0,0 +1,25 @@
import Vue from 'vue';
import CiVariableSettings from './components/ci_variable_settings.vue';
import createStore from './store';
import { parseBoolean } from '~/lib/utils/common_utils';
export default () => {
const el = document.getElementById('js-ci-project-variables');
const { endpoint, projectId, group, maskableRegex } = el.dataset;
const isGroup = parseBoolean(group);
const store = createStore({
endpoint,
projectId,
isGroup,
maskableRegex,
});
return new Vue({
el,
store,
render(createElement) {
return createElement(CiVariableSettings);
},
});
};

View file

@ -0,0 +1,155 @@
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
import Api from '~/api';
import createFlash from '~/flash';
import { __ } from '~/locale';
import { prepareDataForApi, prepareDataForDisplay, prepareEnvironments } from './utils';
export const toggleValues = ({ commit }, valueState) => {
commit(types.TOGGLE_VALUES, valueState);
};
export const clearModal = ({ commit }) => {
commit(types.CLEAR_MODAL);
};
export const resetEditing = ({ commit, dispatch }) => {
// fetch variables again if modal is being edited and then hidden
// without saving changes, to cover use case of reactivity in the table
dispatch('fetchVariables');
commit(types.RESET_EDITING);
};
export const requestAddVariable = ({ commit }) => {
commit(types.REQUEST_ADD_VARIABLE);
};
export const receiveAddVariableSuccess = ({ commit }) => {
commit(types.RECEIVE_ADD_VARIABLE_SUCCESS);
};
export const receiveAddVariableError = ({ commit }, error) => {
commit(types.RECEIVE_ADD_VARIABLE_ERROR, error);
};
export const addVariable = ({ state, dispatch }) => {
dispatch('requestAddVariable');
return axios
.patch(state.endpoint, {
variables_attributes: [prepareDataForApi(state.variable)],
})
.then(() => {
dispatch('receiveAddVariableSuccess');
dispatch('fetchVariables');
})
.catch(error => {
createFlash(error.response.data[0]);
dispatch('receiveAddVariableError', error);
});
};
export const requestUpdateVariable = ({ commit }) => {
commit(types.REQUEST_UPDATE_VARIABLE);
};
export const receiveUpdateVariableSuccess = ({ commit }) => {
commit(types.RECEIVE_UPDATE_VARIABLE_SUCCESS);
};
export const receiveUpdateVariableError = ({ commit }, error) => {
commit(types.RECEIVE_UPDATE_VARIABLE_ERROR, error);
};
export const updateVariable = ({ state, dispatch }, variable) => {
dispatch('requestUpdateVariable');
const updatedVariable = prepareDataForApi(variable);
updatedVariable.secrect_value = updateVariable.value;
return axios
.patch(state.endpoint, { variables_attributes: [updatedVariable] })
.then(() => {
dispatch('receiveUpdateVariableSuccess');
dispatch('fetchVariables');
})
.catch(error => {
createFlash(error.response.data[0]);
dispatch('receiveUpdateVariableError', error);
});
};
export const editVariable = ({ commit }, variable) => {
const variableToEdit = variable;
variableToEdit.secret_value = variableToEdit.value;
commit(types.VARIABLE_BEING_EDITED, variableToEdit);
};
export const requestVariables = ({ commit }) => {
commit(types.REQUEST_VARIABLES);
};
export const receiveVariablesSuccess = ({ commit }, variables) => {
commit(types.RECEIVE_VARIABLES_SUCCESS, variables);
};
export const fetchVariables = ({ dispatch, state }) => {
dispatch('requestVariables');
return axios
.get(state.endpoint)
.then(({ data }) => {
dispatch('receiveVariablesSuccess', prepareDataForDisplay(data.variables));
})
.catch(() => {
createFlash(__('There was an error fetching the variables.'));
});
};
export const requestDeleteVariable = ({ commit }) => {
commit(types.REQUEST_DELETE_VARIABLE);
};
export const receiveDeleteVariableSuccess = ({ commit }) => {
commit(types.RECEIVE_DELETE_VARIABLE_SUCCESS);
};
export const receiveDeleteVariableError = ({ commit }, error) => {
commit(types.RECEIVE_DELETE_VARIABLE_ERROR, error);
};
export const deleteVariable = ({ dispatch, state }, variable) => {
dispatch('requestDeleteVariable');
const destroy = true;
return axios
.patch(state.endpoint, { variables_attributes: [prepareDataForApi(variable, destroy)] })
.then(() => {
dispatch('receiveDeleteVariableSuccess');
dispatch('fetchVariables');
})
.catch(error => {
createFlash(error.response.data[0]);
dispatch('receiveDeleteVariableError', error);
});
};
export const requestEnvironments = ({ commit }) => {
commit(types.REQUEST_ENVIRONMENTS);
};
export const receiveEnvironmentsSuccess = ({ commit }, environments) => {
commit(types.RECEIVE_ENVIRONMENTS_SUCCESS, environments);
};
export const fetchEnvironments = ({ dispatch, state }) => {
dispatch('requestEnvironments');
return Api.environments(state.projectId)
.then(res => {
dispatch('receiveEnvironmentsSuccess', prepareEnvironments(res.data));
})
.catch(() => {
createFlash(__('There was an error fetching the environments information.'));
});
};

View file

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

View file

@ -0,0 +1,22 @@
export const TOGGLE_VALUES = 'TOGGLE_VALUES';
export const VARIABLE_BEING_EDITED = 'VARIABLE_BEING_EDITED';
export const RESET_EDITING = 'RESET_EDITING';
export const CLEAR_MODAL = 'CLEAR_MODAL';
export const REQUEST_VARIABLES = 'REQUEST_VARIABLES';
export const RECEIVE_VARIABLES_SUCCESS = 'RECEIVE_VARIABLES_SUCCESS';
export const REQUEST_DELETE_VARIABLE = 'REQUEST_DELETE_VARIABLE';
export const RECEIVE_DELETE_VARIABLE_SUCCESS = 'RECEIVE_DELETE_VARIABLE_SUCCESS';
export const RECEIVE_DELETE_VARIABLE_ERROR = 'RECEIVE_DELETE_VARIABLE_ERROR';
export const REQUEST_ADD_VARIABLE = 'REQUEST_ADD_VARIABLE';
export const RECEIVE_ADD_VARIABLE_SUCCESS = 'RECEIVE_ADD_VARIABLE_SUCCESS';
export const RECEIVE_ADD_VARIABLE_ERROR = 'RECEIVE_ADD_VARIABLE_ERROR';
export const REQUEST_UPDATE_VARIABLE = 'REQUEST_UPDATE_VARIABLE';
export const RECEIVE_UPDATE_VARIABLE_SUCCESS = 'RECEIVE_UPDATE_VARIABLE_SUCCESS';
export const RECEIVE_UPDATE_VARIABLE_ERROR = 'RECEIVE_UPDATE_VARIABLE_ERROR';
export const REQUEST_ENVIRONMENTS = 'REQUEST_ENVIRONMENTS';
export const RECEIVE_ENVIRONMENTS_SUCCESS = 'RECEIVE_ENVIRONMENTS_SUCCESS';

View file

@ -0,0 +1,86 @@
import * as types from './mutation_types';
import { displayText } from '../constants';
export default {
[types.REQUEST_VARIABLES](state) {
state.isLoading = true;
},
[types.RECEIVE_VARIABLES_SUCCESS](state, variables) {
state.isLoading = false;
state.variables = variables;
},
[types.REQUEST_DELETE_VARIABLE](state) {
state.isDeleting = true;
},
[types.RECEIVE_DELETE_VARIABLE_SUCCESS](state) {
state.isDeleting = false;
},
[types.RECEIVE_DELETE_VARIABLE_ERROR](state, error) {
state.isDeleting = false;
state.error = error;
},
[types.REQUEST_ADD_VARIABLE](state) {
state.isLoading = true;
},
[types.RECEIVE_ADD_VARIABLE_SUCCESS](state) {
state.isLoading = false;
},
[types.RECEIVE_ADD_VARIABLE_ERROR](state, error) {
state.isLoading = false;
state.error = error;
},
[types.REQUEST_UPDATE_VARIABLE](state) {
state.isLoading = true;
},
[types.RECEIVE_UPDATE_VARIABLE_SUCCESS](state) {
state.isLoading = false;
},
[types.RECEIVE_UPDATE_VARIABLE_ERROR](state, error) {
state.isLoading = false;
state.error = error;
},
[types.TOGGLE_VALUES](state, valueState) {
state.valuesHidden = valueState;
},
[types.REQUEST_ENVIRONMENTS](state) {
state.isLoading = true;
},
[types.RECEIVE_ENVIRONMENTS_SUCCESS](state, environments) {
state.isLoading = false;
state.environments = environments;
state.environments.unshift(displayText.allEnvironmentsText);
},
[types.VARIABLE_BEING_EDITED](state, variable) {
state.variableBeingEdited = variable;
},
[types.CLEAR_MODAL](state) {
state.variable = {
variable_type: displayText.variableText,
key: '',
secret_value: '',
protected: false,
masked: false,
environment_scope: displayText.allEnvironmentsText,
};
},
[types.RESET_EDITING](state) {
state.variableBeingEdited = null;
state.showInputValue = false;
},
};

View file

@ -0,0 +1,24 @@
import { displayText } from '../constants';
export default () => ({
endpoint: null,
projectId: null,
isGroup: null,
maskableRegex: null,
isLoading: false,
isDeleting: false,
variable: {
variable_type: displayText.variableText,
key: '',
secret_value: '',
protected: false,
masked: false,
environment_scope: displayText.allEnvironmentsText,
},
variables: null,
valuesHidden: true,
error: null,
environments: [],
typeOptions: [displayText.variableText, displayText.fileText],
variableBeingEdited: null,
});

View file

@ -0,0 +1,43 @@
import { cloneDeep } from 'lodash';
import { displayText, types } from '../constants';
const variableTypeHandler = type =>
type === displayText.variableText ? types.variableType : types.fileType;
export const prepareDataForDisplay = variables => {
const variablesToDisplay = [];
variables.forEach(variable => {
const variableCopy = variable;
if (variableCopy.variable_type === types.variableType) {
variableCopy.variable_type = displayText.variableText;
} else {
variableCopy.variable_type = displayText.fileText;
}
variableCopy.secret_value = variableCopy.value;
if (variableCopy.environment_scope === types.allEnvironmentsType) {
variableCopy.environment_scope = displayText.allEnvironmentsText;
}
variablesToDisplay.push(variableCopy);
});
return variablesToDisplay;
};
export const prepareDataForApi = (variable, destroy = false) => {
const variableCopy = cloneDeep(variable);
variableCopy.protected = variableCopy.protected.toString();
variableCopy.masked = variableCopy.masked.toString();
variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type);
if (variableCopy.environment_scope === displayText.allEnvironmentsText) {
variableCopy.environment_scope = types.allEnvironmentsType;
}
if (destroy) {
// eslint-disable-next-line
variableCopy._destroy = destroy;
}
return variableCopy;
};
export const prepareEnvironments = environments => environments.map(e => e.name);

View file

@ -255,6 +255,8 @@ export default class Clusters {
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(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));
// Add event listener to all the banner close buttons
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
@ -268,6 +270,8 @@ export default class Clusters {
eventHub.$off('setKnativeHostname');
eventHub.$off('setCrossplaneProviderStack');
eventHub.$off('uninstallApplication');
eventHub.$off('setIngressModSecurityEnabled');
eventHub.$off('resetIngressModSecurityEnabled');
}
initPolling(method, successCallback, errorCallback) {
@ -313,10 +317,13 @@ export default class Clusters {
this.checkForNewInstalls(prevApplicationMap, this.store.state.applications);
this.updateContainer(prevStatus, this.store.state.status, this.store.state.statusReason);
this.toggleIngressDomainHelpText(
prevApplicationMap[INGRESS],
this.store.state.applications[INGRESS],
);
if (this.ingressDomainHelpText) {
this.toggleIngressDomainHelpText(
prevApplicationMap[INGRESS],
this.store.state.applications[INGRESS],
);
}
}
showToken() {
@ -513,6 +520,15 @@ export default class Clusters {
this.store.updateAppProperty(appId, 'validationError', null);
}
setIngressModSecurityEnabled({ id, modSecurityEnabled }) {
this.store.updateAppProperty(id, 'isEditingModSecurityEnabled', true);
this.store.updateAppProperty(id, 'modsecurity_enabled', modSecurityEnabled);
}
resetIngressModSecurityEnabled(id) {
this.store.updateAppProperty(id, 'isEditingModSecurityEnabled', false);
}
destroy() {
this.destroyed = true;

View file

@ -21,6 +21,7 @@ import KnativeDomainEditor from './knative_domain_editor.vue';
import { CLUSTER_TYPE, PROVIDER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants';
import eventHub from '~/clusters/event_hub';
import CrossplaneProviderStack from './crossplane_provider_stack.vue';
import IngressModsecuritySettings from './ingress_modsecurity_settings.vue';
export default {
components: {
@ -29,6 +30,7 @@ export default {
GlLoadingIcon,
KnativeDomainEditor,
CrossplaneProviderStack,
IngressModsecuritySettings,
},
props: {
type: {
@ -117,9 +119,6 @@ export default {
ingressInstalled() {
return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED;
},
ingressEnableModsecurity() {
return this.applications.ingress.modsecurity_enabled;
},
ingressExternalEndpoint() {
return this.applications.ingress.externalIp || this.applications.ingress.externalHostname;
},
@ -129,18 +128,6 @@ export default {
crossplaneInstalled() {
return this.applications.crossplane.status === APPLICATION_STATUS.INSTALLED;
},
ingressModSecurityDescription() {
const escapedUrl = _.escape(this.ingressModSecurityHelpPath);
return sprintf(
s__('ClusterIntegration|Learn more about %{startLink}ModSecurity%{endLink}'),
{
startLink: `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">`,
endLink: '</a>',
},
false,
);
},
ingressDescription() {
return sprintf(
_.escape(
@ -241,6 +228,9 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
}
return null;
},
ingress() {
return this.applications.ingress;
},
},
created() {
this.helmInstallIllustration = helmInstallIllustration;
@ -270,7 +260,6 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
<template>
<section id="cluster-applications">
<h4>{{ s__('ClusterIntegration|Applications') }}</h4>
<p class="append-bottom-0">
{{
s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.
@ -329,6 +318,7 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
:uninstall-successful="applications.ingress.uninstallSuccessful"
:uninstall-failed="applications.ingress.uninstallFailed"
:disabled="!helmInstalled"
:updateable="false"
title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
>
<div slot="description">
@ -340,25 +330,10 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
}}
</p>
<template>
<div class="form-group">
<div class="form-check form-check-inline">
<input
v-model="applications.ingress.modsecurity_enabled"
:disabled="ingressInstalled"
type="checkbox"
autocomplete="off"
class="form-check-input"
/>
<label class="form-check-label label-bold" for="ingress-enable-modsecurity">
{{ s__('ClusterIntegration|Enable Web Application Firewall') }}
</label>
</div>
<p class="form-text text-muted">
<strong v-html="ingressModSecurityDescription"></strong>
</p>
</div>
</template>
<ingress-modsecurity-settings
:ingress="ingress"
:ingress-mod-security-help-path="ingressModSecurityHelpPath"
/>
<template v-if="ingressInstalled">
<div class="form-group">

View file

@ -0,0 +1,164 @@
<script>
import _ from 'lodash';
import { __ } from '../../locale';
import { APPLICATION_STATUS, INGRESS } from '~/clusters/constants';
import { GlAlert, GlSprintf, GlLink, GlToggle, GlButton } from '@gitlab/ui';
import eventHub from '~/clusters/event_hub';
import modSecurityLogo from 'images/cluster_app_logos/modsecurity.png';
const { UPDATING, UNINSTALLING, INSTALLING, INSTALLED, UPDATED } = APPLICATION_STATUS;
export default {
title: 'ModSecurity Web Application Firewall',
modsecurityUrl: 'https://modsecurity.org/about.html',
components: {
GlAlert,
GlSprintf,
GlLink,
GlToggle,
GlButton,
},
props: {
ingress: {
type: Object,
required: true,
},
ingressModSecurityHelpPath: {
type: String,
required: false,
default: '',
},
},
data: () => ({
modSecurityLogo,
hasValueChanged: false,
}),
computed: {
modSecurityEnabled: {
get() {
return this.ingress.modsecurity_enabled;
},
set(isEnabled) {
eventHub.$emit('setIngressModSecurityEnabled', {
id: INGRESS,
modSecurityEnabled: isEnabled,
});
if (this.hasValueChanged) {
this.resetStatus();
} else {
this.hasValueChanged = true;
}
},
},
ingressModSecurityDescription() {
return _.escape(this.ingressModSecurityHelpPath);
},
saving() {
return [UPDATING].includes(this.ingress.status);
},
saveButtonDisabled() {
return [UNINSTALLING, UPDATING, INSTALLING].includes(this.ingress.status);
},
saveButtonLabel() {
return this.saving ? __('Saving') : __('Save changes');
},
/**
* Returns true either when:
* - The application is getting updated.
* - The user has changed some of the settings for an application which is
* neither getting installed nor updated.
*/
showButtons() {
return (
this.saving || (this.hasValueChanged && [INSTALLED, UPDATED].includes(this.ingress.status))
);
},
},
methods: {
updateApplication() {
eventHub.$emit('updateApplication', {
id: INGRESS,
params: { modsecurity_enabled: this.ingress.modsecurity_enabled },
});
this.resetStatus();
},
resetStatus() {
eventHub.$emit('resetIngressModSecurityEnabled', INGRESS);
this.hasValueChanged = false;
},
},
};
</script>
<template>
<div>
<gl-alert
v-if="ingress.updateFailed"
class="mb-3"
variant="danger"
:dismissible="false"
@dismiss="alert = null"
>
{{
s__(
'ClusterIntegration|Something went wrong while trying to save your settings. Please try again.',
)
}}
</gl-alert>
<div class="gl-responsive-table-row-layout" role="row">
<div class="table-section append-right-8 section-align-top" role="gridcell">
<img
:src="modSecurityLogo"
:alt="`${$options.title} logo`"
class="cluster-application-logo avatar s40"
/>
</div>
<div class="table-section section-wrap" role="gridcell">
<strong>
<gl-link :href="$options.modsecurityUrl" target="_blank">{{ $options.title }} </gl-link>
</strong>
<div class="form-group">
<p class="form-text text-muted">
<strong>
<gl-sprintf
:message="
s__(
'ClusterIntegration|Real-time web application monitoring, logging and access control. %{linkStart}More information%{linkEnd}',
)
"
>
<template #link="{ content }">
<gl-link :href="ingressModSecurityDescription" target="_blank"
>{{ content }}
</gl-link>
</template>
</gl-sprintf>
</strong>
</p>
<div class="form-check form-check-inline mt-3">
<gl-toggle
v-model="modSecurityEnabled"
:label-on="__('Enabled')"
:label-off="__('Disabled')"
:disabled="saveButtonDisabled"
label-position="right"
/>
</div>
<div v-if="showButtons">
<gl-button
class="btn-success inline mr-1"
:loading="saving"
:disabled="saveButtonDisabled"
@click="updateApplication"
>
{{ saveButtonLabel }}
</gl-button>
<gl-button :disabled="saveButtonDisabled" @click="resetStatus">
{{ __('Cancel') }}
</gl-button>
</div>
</div>
</div>
</div>
</div>
</template>

View file

@ -12,6 +12,7 @@ import {
INSTALL_EVENT,
UPDATE_EVENT,
UNINSTALL_EVENT,
ELASTIC_STACK,
} from '../constants';
import transitionApplicationState from '../services/application_state_machine';
@ -54,6 +55,8 @@ export default class ClusterStore {
modsecurity_enabled: false,
externalIp: null,
externalHostname: null,
isEditingModSecurityEnabled: false,
updateFailed: false,
},
cert_manager: {
...applicationInitialState,
@ -208,8 +211,9 @@ 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.modsecurity_enabled =
serverAppEntry.modsecurity_enabled || this.state.applications.ingress.modsecurity_enabled;
if (!this.state.applications.ingress.isEditingModSecurityEnabled) {
this.state.applications.ingress.modsecurity_enabled = serverAppEntry.modsecurity_enabled;
}
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
this.state.applications.cert_manager.email || serverAppEntry.email;
@ -234,6 +238,9 @@ export default class ClusterStore {
} else if (appId === RUNNER) {
this.state.applications.runner.version = version;
this.state.applications.runner.updateAvailable = updateAvailable;
} else if (appId === ELASTIC_STACK) {
this.state.applications.elastic_stack.version = version;
this.state.applications.elastic_stack.updateAvailable = updateAvailable;
}
});
}

View file

@ -0,0 +1,99 @@
<script>
import { mapState, mapActions } from 'vuex';
import { GlTable, GlLoadingIcon, GlBadge } from '@gitlab/ui';
import tooltip from '~/vue_shared/directives/tooltip';
import { CLUSTER_TYPES, STATUSES } from '../constants';
import { __, sprintf } from '~/locale';
export default {
components: {
GlTable,
GlLoadingIcon,
GlBadge,
},
directives: {
tooltip,
},
fields: [
{
key: 'name',
label: __('Kubernetes cluster'),
},
{
key: 'environmentScope',
label: __('Environment scope'),
},
{
key: 'size',
label: __('Size'),
},
{
key: 'cpu',
label: __('Total cores (vCPUs)'),
},
{
key: 'memory',
label: __('Total memory (GB)'),
},
{
key: 'clusterType',
label: __('Cluster level'),
formatter: value => CLUSTER_TYPES[value],
},
],
computed: {
...mapState(['clusters', 'loading']),
},
mounted() {
// TODO - uncomment this once integrated with BE
// this.fetchClusters();
},
methods: {
...mapActions(['fetchClusters']),
statusClass(status) {
return STATUSES[status].className;
},
statusTitle(status) {
const { title } = STATUSES[status];
return sprintf(__('Status: %{title}'), { title }, false);
},
},
};
</script>
<template>
<gl-loading-icon v-if="loading" size="md" class="mt-3" />
<gl-table
v-else
:items="clusters"
:fields="$options.fields"
stacked="md"
variant="light"
class="qa-clusters-table"
>
<template #cell(name)="{ item }">
<div class="d-flex flex-row-reverse flex-md-row js-status">
{{ item.name }}
<gl-loading-icon
v-if="item.status === 'deleting'"
v-tooltip
:title="statusTitle(item.status)"
size="sm"
class="mr-2 ml-md-2"
/>
<div
v-else
v-tooltip
class="cluster-status-indicator rounded-circle align-self-center gl-w-8 gl-h-8 mr-2 ml-md-2"
:class="statusClass(item.status)"
:title="statusTitle(item.status)"
></div>
</div>
</template>
<template #cell(clusterType)="{value}">
<gl-badge variant="light">
{{ value }}
</gl-badge>
</template>
</gl-table>
</template>

View file

@ -0,0 +1,15 @@
import { __ } from '~/locale';
export const CLUSTER_TYPES = {
project_type: __('Project'),
group_type: __('Group'),
instance_type: __('Instance'),
};
export const STATUSES = {
disabled: { className: 'disabled', title: __('Disabled') },
connected: { className: 'bg-success', title: __('Connected') },
unreachable: { className: 'bg-danger', title: __('Unreachable') },
authentication_failure: { className: 'bg-warning', title: __('Authentication Failure') },
deleting: { title: __('Deleting') },
};

View file

@ -0,0 +1,22 @@
import Vue from 'vue';
import Clusters from './components/clusters.vue';
import { createStore } from './store';
export default () => {
const entryPoint = document.querySelector('#js-clusters-list-app');
if (!entryPoint) {
return;
}
const { endpoint } = entryPoint.dataset;
// eslint-disable-next-line no-new
new Vue({
el: '#js-clusters-list-app',
store: createStore({ endpoint }),
render(createElement) {
return createElement(Clusters);
},
});
};

View file

@ -0,0 +1,37 @@
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import Poll from '~/lib/utils/poll';
import axios from '~/lib/utils/axios_utils';
import Visibility from 'visibilityjs';
import flash from '~/flash';
import { __ } from '~/locale';
import * as types from './mutation_types';
export const fetchClusters = ({ state, commit }) => {
const poll = new Poll({
resource: {
fetchClusters: endpoint => axios.get(endpoint),
},
data: state.endpoint,
method: 'fetchClusters',
successCallback: ({ data }) => {
commit(types.SET_CLUSTERS_DATA, convertObjectPropsToCamelCase(data, { deep: true }));
commit(types.SET_LOADING_STATE, false);
},
errorCallback: () => flash(__('An error occurred while loading clusters')),
});
if (!Visibility.hidden()) {
poll.makeRequest();
}
Visibility.change(() => {
if (!Visibility.hidden()) {
poll.restart();
} else {
poll.stop();
}
});
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};

View file

@ -0,0 +1,16 @@
import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import mutations from './mutations';
import * as actions from './actions';
Vue.use(Vuex);
export const createStore = initialState =>
new Vuex.Store({
actions,
mutations,
state: state(initialState),
});
export default createStore;

View file

@ -0,0 +1,2 @@
export const SET_CLUSTERS_DATA = 'SET_CLUSTERS_DATA';
export const SET_LOADING_STATE = 'SET_LOADING_STATE';

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