New upstream version 12.6.1

This commit is contained in:
Sruthi Chandran 2020-01-01 13:55:28 +05:30
parent 510528bb68
commit ba8cc108d2
3205 changed files with 67527 additions and 153776 deletions

View file

@ -1,6 +1,8 @@
extends: extends:
- '@gitlab' - '@gitlab'
- plugin:promise/recommended - plugin:promise/recommended
- plugin:no-jquery/slim
- plugin:no-jquery/deprecated-3.4
globals: globals:
__webpack_public_path__: true __webpack_public_path__: true
gl: false gl: false
@ -30,7 +32,13 @@ rules:
no-else-return: no-else-return:
- error - error
- allowElseIf: true - allowElseIf: true
import/no-unresolved:
- error
- ignore:
# https://gitlab.com/gitlab-org/gitlab/issues/38226
- '^ee_component/'
import/no-useless-path-segments: off import/no-useless-path-segments: off
import/order: off
lines-between-class-members: off lines-between-class-members: off
# Disabled for now, to make the plugin-vue 4.5 -> 5.0 update smoother # Disabled for now, to make the plugin-vue 4.5 -> 5.0 update smoother
vue/no-confusing-v-for-v-if: error vue/no-confusing-v-for-v-if: error
@ -38,11 +46,13 @@ rules:
vue/no-use-v-if-with-v-for: off vue/no-use-v-if-with-v-for: off
vue/no-v-html: off vue/no-v-html: off
vue/use-v-on-exact: off vue/use-v-on-exact: off
no-jquery/no-ajax: error no-jquery/no-animate: off
no-jquery/no-ajax-events: error # all offenses of no-jquery/no-animate-toggle are false positives ( $toast.show() )
no-jquery/no-load: error no-jquery/no-animate-toggle: off
no-jquery/no-load-shorthand: error no-jquery/no-event-shorthand: off
no-jquery/no-fade: off
no-jquery/no-serialize: error no-jquery/no-serialize: error
no-jquery/no-sizzle: off
promise/always-return: off promise/always-return: off
promise/no-callback-in-promise: off promise/no-callback-in-promise: off
overrides: overrides:

1
.gitattributes vendored
View file

@ -1,2 +1,3 @@
VERSION merge=ours VERSION merge=ours
Dangerfile gitlab-language=ruby Dangerfile gitlab-language=ruby
*.pdf filter=lfs diff=lfs merge=lfs -text

3
.gitignore vendored
View file

@ -66,7 +66,7 @@ eslint-report.html
/vendor/gitaly-ruby /vendor/gitaly-ruby
/builds* /builds*
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
/.gitlab_pages_shared_secret /.gitlab_pages_secret
/webpack-report/ /webpack-report/
/knapsack/ /knapsack/
/rspec_flaky/ /rspec_flaky/
@ -84,3 +84,4 @@ jsdoc/
.overcommit.yml .overcommit.yml
.projections.json .projections.json
/qa/.rakeTasks /qa/.rakeTasks
webpack-dev-server.json

View file

@ -1,38 +1,42 @@
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.33" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.33"
stages: stages:
- sync - sync
- prepare - prepare
- quick-test - quick-test
- test - test
- post-test
- review-prepare - review-prepare
- review - review
- qa - qa
- post-test - post-qa
- notification
- pages - pages
variables: variables:
RAILS_ENV: "test" RAILS_ENV: "test"
NODE_ENV: "test" NODE_ENV: "test"
SIMPLECOV: "true" SIMPLECOV: "true"
GIT_DEPTH: "50" GIT_DEPTH: "20"
GIT_SUBMODULE_STRATEGY: "none" GIT_SUBMODULE_STRATEGY: "none"
GET_SOURCES_ATTEMPTS: "3" GET_SOURCES_ATTEMPTS: "3"
KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/report-master.json KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/report-master.json
FLAKY_RSPEC_SUITE_REPORT_PATH: rspec_flaky/report-suite.json FLAKY_RSPEC_SUITE_REPORT_PATH: rspec_flaky/report-suite.json
BUILD_ASSETS_IMAGE: "false" BUILD_ASSETS_IMAGE: "false"
ES_JAVA_OPTS: "-Xms256m -Xmx256m" ES_JAVA_OPTS: "-Xms256m -Xmx256m"
ELASTIC_URL: "http://elastic:changeme@docker.elastic.co-elasticsearch-elasticsearch:9200" ELASTIC_URL: "http://elastic:changeme@elasticsearch:9200"
after_script: after_script:
- date - date
include: include:
- local: .gitlab/ci/cache-repo.gitlab-ci.yml
- local: .gitlab/ci/cng.gitlab-ci.yml - local: .gitlab/ci/cng.gitlab-ci.yml
- local: .gitlab/ci/docs.gitlab-ci.yml - local: .gitlab/ci/docs.gitlab-ci.yml
- local: .gitlab/ci/frontend.gitlab-ci.yml - local: .gitlab/ci/frontend.gitlab-ci.yml
- local: .gitlab/ci/global.gitlab-ci.yml - local: .gitlab/ci/global.gitlab-ci.yml
- local: .gitlab/ci/memory.gitlab-ci.yml - local: .gitlab/ci/memory.gitlab-ci.yml
- local: .gitlab/ci/notifications.gitlab-ci.yml
- local: .gitlab/ci/pages.gitlab-ci.yml - local: .gitlab/ci/pages.gitlab-ci.yml
- local: .gitlab/ci/qa.gitlab-ci.yml - local: .gitlab/ci/qa.gitlab-ci.yml
- local: .gitlab/ci/reports.gitlab-ci.yml - local: .gitlab/ci/reports.gitlab-ci.yml

View file

@ -0,0 +1,33 @@
# Builds a cached .tar.gz of the master branch with full history and
# uploads it to Google Cloud Storage. This archive is downloaded by a
# script defined by a CI/CD variable named CI_PRE_CLONE_SCRIPT. This has
# two benefits:
#
# 1. It speeds up builds. A 800 MB download only takes seconds.
# 2. It significantly reduces load on the file server. Smaller deltas
# means less time spent in git pack-objects.
#
# Since the destination directory of the archive depends on the project
# ID, this is only run on GitLab.com.
#
# CI_REPO_CACHE_CREDENTIALS contains the Google Cloud service account
# JSON for uploading to the gitlab-ci-git-repo-cache bucket. These
# credentials are stored in the Production vault.
#
# Note that this bucket should be located in the same continent as the
# runner, or network egress charges will apply:
# https://cloud.google.com/storage/pricing
cache-repo:
extends:
- .only:variables_refs-canonical-dot-com-schedules
image: gcr.io/google.com/cloudsdktool/cloud-sdk:alpine
stage: sync
allow_failure: true
variables:
GIT_DEPTH: 0
TAR_FILENAME: /tmp/gitlab-master.tar
script:
- gcloud auth activate-service-account --key-file=$CI_REPO_CACHE_CREDENTIALS
- tar cf $TAR_FILENAME .
- gzip $TAR_FILENAME
- gsutil cp $TAR_FILENAME.gz gs://gitlab-ci-git-repo-cache/project-$CI_PROJECT_ID/gitlab-master.tar.gz

View file

@ -24,7 +24,8 @@
- apk add --update openssl - apk add --update openssl
- wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/trigger-build-docs - wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/trigger-build-docs
- chmod 755 trigger-build-docs - chmod 755 trigger-build-docs
- gem install gitlab --no-document - gem install httparty --no-document --version 0.17.3
- gem install gitlab --no-document --version 4.13.0
# Always trigger a docs build in gitlab-docs only on docs-only branches. # Always trigger a docs build in gitlab-docs only on docs-only branches.
# Useful to preview the docs changes live. # Useful to preview the docs changes live.

View file

@ -13,7 +13,7 @@
- .default-before_script - .default-before_script
- .assets-compile-cache - .assets-compile-cache
- .only:changes-code-backstage-qa - .only:changes-code-backstage-qa
image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-git-2.22-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.33-docker-18.06.1 image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.16-graphicsmagick-1.3.33-docker-19.03.1
stage: test stage: test
dependencies: ["setup-test-env"] dependencies: ["setup-test-env"]
needs: ["setup-test-env"] needs: ["setup-test-env"]
@ -74,7 +74,6 @@ gitlab:assets:compile pull-cache:
- .default-before_script - .default-before_script
- .assets-compile-cache - .assets-compile-cache
- .only:changes-code-backstage-qa - .only:changes-code-backstage-qa
- .use-pg9
stage: prepare stage: prepare
script: script:
- node --version - node --version
@ -83,6 +82,7 @@ gitlab:assets:compile pull-cache:
- retry bundle exec rake gitlab:assets:compile - retry bundle exec rake gitlab:assets:compile
- scripts/clean-old-cached-assets - scripts/clean-old-cached-assets
variables: variables:
SETUP_DB: "false"
# we override the max_old_space_size to prevent OOM errors # we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584 NODE_OPTIONS: --max_old_space_size=3584
cache: cache:
@ -244,6 +244,12 @@ webpack-dev-server:
dependencies: ["setup-test-env", "compile-assets pull-cache"] dependencies: ["setup-test-env", "compile-assets pull-cache"]
variables: variables:
WEBPACK_MEMORY_TEST: "true" WEBPACK_MEMORY_TEST: "true"
WEBPACK_VENDOR_DLL: "true"
script: script:
- node --version - yarn webpack-vendor
- node --expose-gc node_modules/.bin/webpack-dev-server --config config/webpack.config.js - node --expose-gc node_modules/.bin/webpack-dev-server --config config/webpack.config.js
artifacts:
name: webpack-dev-server
expire_in: 31d
paths:
- webpack-dev-server.json

View file

@ -93,7 +93,7 @@
- "config.ru" - "config.ru"
- "{package.json,yarn.lock}" - "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated
.backstage-patterns: &backstage-patterns .backstage-patterns: &backstage-patterns
- "Dangerfile" - "Dangerfile"
@ -139,7 +139,7 @@
- "config.ru" - "config.ru"
- "{package.json,yarn.lock}" - "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes # Backstage changes
- "Dangerfile" - "Dangerfile"
- "danger/**/*" - "danger/**/*"
@ -163,7 +163,7 @@
- "config.ru" - "config.ru"
- "{package.json,yarn.lock}" - "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# QA changes # QA changes
- ".dockerignore" - ".dockerignore"
- "qa/**/*" - "qa/**/*"
@ -183,7 +183,7 @@
- "config.ru" - "config.ru"
- "{package.json,yarn.lock}" - "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*" - "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/**/*" - "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes # Backstage changes
- "Dangerfile" - "Dangerfile"
- "danger/**/*" - "danger/**/*"
@ -202,7 +202,7 @@
- name: redis:alpine - name: redis:alpine
.use-pg10: .use-pg10:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33"
services: services:
- name: postgres:10.9 - name: postgres:10.9
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
@ -213,15 +213,15 @@
- name: postgres:9.6 - name: postgres:9.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine - name: redis:alpine
- name: docker.elastic.co/elasticsearch/elasticsearch:5.6.12 - name: elasticsearch:5.6.12
.use-pg10-ee: .use-pg10-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33"
services: services:
- name: postgres:10.9 - name: postgres:10.9
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine - name: redis:alpine
- name: docker.elastic.co/elasticsearch/elasticsearch:5.6.12 - name: elasticsearch:5.6.12
.only-ee: .only-ee:
only: only:

View file

@ -0,0 +1,23 @@
.notify:
image: ruby:2.6-alpine
stage: notification
dependencies: []
cache: {}
before_script:
- apk update && apk add git curl bash
- source scripts/utils.sh
- source scripts/notifications.sh
- install_gitlab_gem
variables:
COMMIT_NOTES_URL: "https://${CI_SERVER_HOST}/${CI_PROJECT_PATH}/commit/${CI_COMMIT_SHA}#notes-list"
schedule:package-and-qa:notify-failure:
extends:
- .only:variables_refs-canonical-dot-com-schedules
- .notify
script:
- 'export NOTIFICATION_MESSAGE=":skull_and_crossbones: Scheduled QA against master failed! :skull_and_crossbones: See ${CI_PIPELINE_URL}. For downstream pipelines, see ${COMMIT_NOTES_URL}"'
- 'notify_on_job_failure schedule:package-and-qa qa-master "${NOTIFICATION_MESSAGE}" ci_failing'
needs: ["schedule:package-and-qa"]
allow_failure: true
when: always

View file

@ -1,22 +1,27 @@
--- ---
# Syncs any changes pushed to a stable branch to the corresponding CE stable # Syncs any changes pushed to a stable branch to the corresponding
# branch. We run this prior to any tests so that random failures don't prevent a # gitlab-foss/CE stable branch. We run this prior to any tests so that random
# sync. # failures don't prevent a sync.
sync-stable-branch: .merge-train-sync:
# We don't need/want any global before/after commands, so we overwrite these # We don't need/want any global before/after commands, so we overwrite these
# settings. # settings.
image: alpine:edge image: alpine:edge
stage: sync stage: sync
# This job should only run on EE stable branches on the canonical GitLab.com
# repository.
only:
variables:
- $CI_SERVER_HOST == "gitlab.com"
refs:
- /^[\d-]+-stable-ee$/@gitlab-org/gitlab
before_script: before_script:
- apk add --no-cache --update curl bash - apk add --no-cache --update curl bash
after_script: [] after_script: []
script: script:
- bash scripts/sync-stable-branch.sh - bash scripts/sync-stable-branch.sh
only:
variables:
- $CI_SERVER_HOST == "gitlab.com"
sync-stable-branch:
extends: .merge-train-sync
variables:
SOURCE_PROJECT: gitlab-org/gitlab
TARGET_PROJECT: gitlab-org/gitlab-foss
only:
refs:
- /^[\d-]+-stable-ee$/@gitlab-org/gitlab

View file

@ -20,6 +20,7 @@ code_quality:
variables: variables:
DOCKER_DRIVER: overlay2 DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "" DOCKER_TLS_CERTDIR: ""
CODE_QUALITY_IMAGE: "registry.gitlab.com/gitlab-org/security-products/codequality:12-5-stable"
script: script:
- | - |
if ! docker info &>/dev/null; then if ! docker info &>/dev/null; then
@ -27,14 +28,17 @@ code_quality:
export DOCKER_HOST='tcp://localhost:2375' export DOCKER_HOST='tcp://localhost:2375'
fi fi
fi fi
- docker pull --quiet "$CODE_QUALITY_IMAGE"
- docker run - docker run
--env SOURCE_CODE="$PWD" --env SOURCE_CODE="$PWD"
--volume "$PWD":/code --volume "$PWD":/code
--volume /var/run/docker.sock:/var/run/docker.sock --volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/codequality:12-0-stable" /code "$CODE_QUALITY_IMAGE" /code
artifacts: artifacts:
reports: reports:
codequality: gl-code-quality-report.json codequality: gl-code-quality-report.json
paths:
- gl-code-quality-report.json
expire_in: 1 week expire_in: 1 week
dependencies: [] dependencies: []
except: except:
@ -165,7 +169,6 @@ dependency_scanning:
DS_ANALYZER_IMAGE_TAG \ DS_ANALYZER_IMAGE_TAG \
DS_DEFAULT_ANALYZERS \ DS_DEFAULT_ANALYZERS \
DS_EXCLUDED_PATHS \ DS_EXCLUDED_PATHS \
DEP_SCAN_DISABLE_REMOTE_CHECKS \
DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \ DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
DS_PULL_ANALYZER_IMAGE_TIMEOUT \ DS_PULL_ANALYZER_IMAGE_TIMEOUT \
DS_RUN_ANALYZER_TIMEOUT \ DS_RUN_ANALYZER_TIMEOUT \
@ -231,9 +234,3 @@ dast:
- gl-dast-report.json - gl-dast-report.json
reports: reports:
dast: gl-dast-report.json dast: gl-dast-report.json
only:
variables:
- $GITLAB_FEATURES =~ /\bdast\b/
except:
variables:
- $DAST_DISABLED

View file

@ -23,9 +23,11 @@ build-qa-image:
stage: prepare stage: prepare
script: script:
- '[[ ! -d "ee/" ]] || export GITLAB_EDITION="ee"' - '[[ ! -d "ee/" ]] || export GITLAB_EDITION="ee"'
- export QA_MASTER_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab/gitlab-${GITLAB_EDITION}-qa:master"
- export QA_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab/gitlab-${GITLAB_EDITION}-qa:${CI_COMMIT_REF_SLUG}" - export QA_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab/gitlab-${GITLAB_EDITION}-qa:${CI_COMMIT_REF_SLUG}"
- time docker build --cache-from gitlab/gitlab-${GITLAB_EDITION}-qa:nightly --tag ${QA_IMAGE} --file ./qa/Dockerfile ./
- echo "${CI_JOB_TOKEN}" | docker login --username gitlab-ci-token --password-stdin ${CI_REGISTRY} - echo "${CI_JOB_TOKEN}" | docker login --username gitlab-ci-token --password-stdin ${CI_REGISTRY}
- time docker pull "${QA_MASTER_IMAGE}"
- time docker build --cache-from "${QA_MASTER_IMAGE}" --tag ${QA_IMAGE} --file ./qa/Dockerfile ./
- time docker push ${QA_IMAGE} - time docker push ${QA_IMAGE}
.base-review-cleanup: .base-review-cleanup:
@ -94,10 +96,7 @@ schedule:review-build-cng:
variables: variables:
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}" HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}" DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
# v2.4.4 + two improvements: GITLAB_HELM_CHART_REF: "v2.5.1"
# - Allow to pass an EE license when installing the chart: https://gitlab.com/gitlab-org/charts/gitlab/merge_requests/1008
# - Allow to customize the livenessProbe for `gitlab-shell`: https://gitlab.com/gitlab-org/charts/gitlab/merge_requests/1021
GITLAB_HELM_CHART_REF: "6c655ed77e60f1f7f533afb97bef8c9cb7dc61eb"
GITLAB_EDITION: "ce" GITLAB_EDITION: "ce"
environment: environment:
name: review/${CI_COMMIT_REF_NAME} name: review/${CI_COMMIT_REF_NAME}
@ -135,13 +134,11 @@ review-deploy:
- .review-deploy-base - .review-deploy-base
- .only-review - .only-review
- .only:changes-code-qa - .only:changes-code-qa
needs: ["review-build-cng"]
schedule:review-deploy: schedule:review-deploy:
extends: extends:
- .review-deploy-base - .review-deploy-base
- .only-review-schedules - .only-review-schedules
needs: ["schedule:review-build-cng"]
.base-review-stop: .base-review-stop:
extends: extends:
@ -280,7 +277,7 @@ parallel-spec-reports:
- .only-review - .only-review
- .only:changes-code-qa - .only:changes-code-qa
image: ruby:2.6-alpine image: ruby:2.6-alpine
stage: post-test stage: post-qa
dependencies: ["review-qa-all"] dependencies: ["review-qa-all"]
variables: variables:
NEW_PARALLEL_SPECS_REPORT: qa/report-new.html NEW_PARALLEL_SPECS_REPORT: qa/report-new.html

View file

@ -0,0 +1,47 @@
## What is the productivity problem to solve?
<!--
Please describe the productivity problem that needs to be solved backed by charts from
https://about.gitlab.com/handbook/engineering/quality/engineering-productivity-team/#engineering-productivity-team-metrics.
-->
### Problem identification checklist
- [ ] The root cause of the problem is identified.
- [ ] The surface of the problem is as small as possible.
## What are the potential solutions?
<!--
Please provide potential solutions here. Example solutions could be:
- Dogfood a feature.
- Refactor/improve some workflow code.
- Throw more money at the problem.
Please provide pros/cons and a weight estimate for each solution.
-->
- [ ] All potential solutions are listed.
- [ ] A solution has been chosen for the first iteration: `PUT THE CHOSEN SOLUTION HERE`
## Who and when will the solution be implemented?
<!--
For history reason, please list the person that will implement the solution and
the planned milestone/date.
-->
## Verify that the solution has improved the situation
<!--
Ideally, looking at the charts from the first part, we should see an improvement
after the implementation is merged/deployed/released.
-->
- [ ] The solution improved the situation.
- If yes, check this box and close the issue. Well done! :tada:
- Otherwise, create a new "Productivity Improvement" issue. You can re-use the description from this issue, but obviously another solution should be chosen this time.
/label ~"Engineering Productivity" ~meta
/cc @gl-quality/eng-prod

View file

@ -411,6 +411,7 @@ linters:
- 'app/views/shared/snippets/_snippet.html.haml' - 'app/views/shared/snippets/_snippet.html.haml'
- 'app/views/shared/tokens/_scopes_list.html.haml' - 'app/views/shared/tokens/_scopes_list.html.haml'
- 'app/views/shared/web_hooks/_form.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/shared/web_hooks/_test_button.html.haml'
- 'app/views/u2f/_authenticate.html.haml' - 'app/views/u2f/_authenticate.html.haml'
- 'app/views/u2f/_register.html.haml' - 'app/views/u2f/_register.html.haml'
@ -442,7 +443,7 @@ linters:
- 'ee/app/views/groups/epics/_epic.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/_ldap_sync.html.haml'
- 'ee/app/views/groups/group_members/_sync_button.html.haml' - 'ee/app/views/groups/group_members/_sync_button.html.haml'
- 'ee/app/views/groups/hooks/_project_hook.html.haml' - 'ee/app/views/groups/hooks/edit.html.haml'
- 'ee/app/views/groups/hooks/index.html.haml' - 'ee/app/views/groups/hooks/index.html.haml'
- 'ee/app/views/groups/ldap_group_links/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/groups/pipeline_quota/index.html.haml'

View file

@ -26,5 +26,102 @@
"first-line-h1": false, "first-line-h1": false,
"code-block-style": { "code-block-style": {
"style": "fenced" "style": "fenced"
},
"proper-names": {
"names": [
"Akismet",
"Alertmanager",
"API",
"Asana",
"Auth0",
"Authentiq",
"Azure",
"Bamboo",
"Bitbucket",
"Bugzilla",
"CAS",
"CentOS",
"Consul",
"Debian",
"Elasticsearch",
"Facebook",
"Git LFS",
"git-annex",
"Git",
"Gitaly",
"GitHub",
"GitLab Geo",
"GitLab Monitor",
"GitLab Operator",
"GitLab Pages",
"GitLab Rails",
"GitLab Runner",
"GitLab Shell",
"GitLab Workhorse",
"GitLab",
"Gmail",
"Google",
"Grafana",
"Helm",
"HipChat",
"Ingress",
"jasmine-jquery",
"JavaScript",
"Jaeger",
"Jenkins",
"Jira",
"Jira Cloud",
"Jira Server",
"jQuery",
"JupyterHub",
"Karma",
"Kerberos",
"Knative",
"Kubernetes",
"LDAP",
"Let's Encrypt",
"Markdown",
"markdownlint",
"Mattermost",
"Microsoft",
"MinIO",
"NGINX Ingress",
"NGINX",
"OAuth",
"OAuth 2",
"OmniAuth",
"Omnibus GitLab",
"OpenID",
"OpenShift",
"PgBouncer",
"PostgreSQL",
"Prometheus",
"Puma",
"Python",
"Redis",
"Redmine",
"reCAPTCHA",
"runit",
"Salesforce",
"SAML",
"Sentry",
"Sidekiq",
"Shibboleth",
"Slack",
"SMTP",
"SSH",
"Tiller",
"Trello",
"Trello Power-Ups",
"TypeScript",
"Twitter",
"Ubuntu",
"Ultra Auth",
"Unicorn",
"unicorn-worker-killer",
"WebdriverIO",
"YouTrack"
],
"code_blocks": false
} }
} }

2
.nvmrc
View file

@ -1 +1 @@
12.4.0 12.10.0

View file

@ -56,7 +56,6 @@ Style/FrozenStringLiteralComment:
- 'qa/**/*' - 'qa/**/*'
- 'rubocop/**/*' - 'rubocop/**/*'
- 'scripts/**/*' - 'scripts/**/*'
- 'spec/lib/gitlab/**/*'
RSpec/FilePath: RSpec/FilePath:
Exclude: Exclude:

View file

@ -1,5 +1,116 @@
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 12.6.1
- No changes.
## 12.6.0
### Fixed (32 changes, 5 of them are from the community)
- Exclude forks from Group Security Dashboard filter. !14667
- Clarify why Service Desk feature is unavailable. !19244
- Bump code quality version in template to 0.85.5. !19354
- Nullify user roles that have been accidentaly set to a value of 0. !19569
- Display CI Minutes warning only if minutes left is still below last level. !19751
- Add a unique constraint to `software_licenses.name` column. !19840
- Link user accounts to new Smartcards identities on login. !20059
- Allow valid namespace paths with dots for api PUT. !20079
- Map software license names from the v1 license scan report to an equivalent SPDX identifer. !20195
- Prefer sending external pull request pipeline statuses over general statuses to GitHub. !20364
- Abort rendering of security reports that aren't enabled. !20381
- Fix Infinite Scrolling on Environments Dashboard Project Selector. !20408
- Link user accounts to new Smartcards certificate ldap identities on login. !20470
- Handle design repositories when moving a project to a new storage. !20509
- Resolve Version dropdown goes wrong if versions are not monotonic. !20515 (Tom Quirk)
- Turn auto_complete_issues on by default. !20525
- Handle design repositories when moving existing projects to Hashed Storage. !20540
- Fix dependency metadata on the NPM registry responses. !20549
- Fix the hiding of undismissed vulnerabilities. !20599
- Fix check for existing ES limited indexing IDs. !20866
- Show actions area for fixed vulnerabilities in merge requests. !20867
- Fix typo in Kubernetes GKE setup error message. !21091
- Include projects in subgroups in group boards relative position. !21189
- Fix inability to add comments to a discussion in Design Management. !21229
- Fix Infinity % / Infinity % on Stacked Progress Bar. !21437
- Fix sort icon direction when sorting by weight. !21447 (Jan Beckmann)
- Auto-focus title text box when creating new epics. !21516 (Jan Beckmann)
- Fix analytics icon alignment. !21555
- Invalid trial form to remember user & country. !21840
- Fix styling on contribution analytics dashboard. !207012 (briankabiro)
- Add correct link to milestone in groups for issuables list after refactor.
- Show the proper message when adding a duplicate issue to an epic. (20175)
### Changed (13 changes, 1 of them is from the community)
- Make "Learn more about" links for security scanning popovers on merge request page open in new tab. !13333 (Daniel Tian)
- Redirect Admin > Settings > Geo to Admin > Geo > Settings. !19833
- Expose epic_id parameter in issues API. !19953
- Allow to login with Smartcard certificates using SAN extensions that only defines one global email identity. !20052
- Update SAST.gitlab-ci.yml - Add kubesec analyzer. !20129
- Update start trial CTA in top right banner to only appear if all namespaces are free. !20177
- Update billing page trial CTAs. !20383
- Rename software_license_policies.approval_status to software_license_policies.classification. !20414
- Add ability to edit Group Hooks. !20898
- Improve the performance of group templates finder. !20947
- Hide elasticsearch namespaces and projects when too many in rollout. !21225
- Update Explore Geo Page. !21448
- Renamed Conversational Development Index feature to DevOps Score.
### Performance (1 change)
- Do not trigger count query for pagination without count. !21232
### Added (24 changes, 2 of them are from the community)
- Add new approval rule type which allows anyone to approve. !15378
- Add Personal access token expiry policy. !17344
- Expose time logs for group issues via the GraphQL API. !18689
- Add application settings needed for soft-deletion. !18790
- Add link to new epic for promoted issues. !18839 (Jan Beckmann)
- Use issue templates on service desk(backend). !19515
- Log history for gitlab_subscriptions table. !19694
- Resolve Show plan of root group on subgroup details page. !20218
- Adjust group members API to include group SAML info. !20357
- Add user ability to append template to incoming service desk issues. !20476
- Add audit event when member access is removed due to expiration. !20529
- Update CI templates to use sitespeed 11.2.0. !20561
- Added migration for issue link types. !20617
- Add security configuration navigation item. !20711
- Create a new database composite index to support cross-project artifacts downloads. !20721
- Add deployment API updated_at filters. !20731
- Show loading spinner in design card while design is uploading. !20814
- Add most affected projects to group security dashboard. !20892
- Introduce Credentials Inventory. !20912
- Add GraphQL mutation for changing weight of an issue. !21331
- Cache vulnerability findings history endpoint for security dashboards. !21349
- Added Marginalia feature which can generate PostgreSQL query comments to Gitlab. !21364 (BalaKumar)
- Add API for states by country. !21417
- Improved trials sign up for gitlab.com. !21650
### Other (8 changes, 2 of them are from the community)
- Store and look up design management version authorship from database. !17322
- Remove redundant ManagedLicenses controller. !20131 (briankabiro)
- Updated board_service.js to use boardStore directly. !20141 (nuwe1)
- Delete any stale deploy access levels by group. !20689
- Add project webhooks limits on GitLab.com. !20730
- Remove the design_management_flag feature flag from the codebase. The feature flag toggles the Design Management feature, and has been enabled by default since 12.2. !20883
- Remove operations_feature_flags_clients.token column. !21016
- Update the alerts used in the Dependency List to follow GitLab design guidelines. !21760
## 12.5.5
- No changes.
## 12.5.4
### Security (1 change)
- Fix stale Elasticsearch permissions when moving group from public group to private parent group.
## 12.5.3 ## 12.5.3
### Performance (1 change) ### Performance (1 change)
@ -11,10 +122,6 @@ Please view this file on the master branch, on stable branches it's out of date.
- Geo - Does not schedule duplicated jobs while backfilling uploads, LFS objects and job artifacts. !20324 - Geo - Does not schedule duplicated jobs while backfilling uploads, LFS objects and job artifacts. !20324
## 12.5.2
- No changes.
## 12.5.1 ## 12.5.1
### Security (6 changes) ### Security (6 changes)
@ -113,6 +220,18 @@ Please view this file on the master branch, on stable branches it's out of date.
- Remove IIFEs from jira_connect.js file. !19248 (nuwe1) - Remove IIFEs from jira_connect.js file. !19248 (nuwe1)
## 12.4.5
- No changes.
## 12.4.3
### Fixed (2 changes)
- Fix admin welcome image not found. !19676
- Revert ES support for public/internal project snippets. !19715
## 12.4.2 ## 12.4.2
### Fixed (1 change) ### Fixed (1 change)
@ -243,6 +362,25 @@ Please view this file on the master branch, on stable branches it's out of date.
- Docs for protected branch code owner approval API. !17132 - Docs for protected branch code owner approval API. !17132
## 12.3.9
### Security (1 change)
- Fix stale Elasticsearch permissions when moving group from public group to private parent group.
## 12.3.7
### Security (6 changes)
- Protect Jira integration endpoints from guest users.
- Fix private comment Elasticsearch leak on project search scope.
- Filter snippet search results by feature visibility.
- Hide AWS secret on Admin Integration page.
- Fail pull mirror when mirror user is blocked.
- Prevent IDOR when adding users to protected environments.
## 12.3.4 ## 12.3.4
### Fixed (2 changes) ### Fixed (2 changes)
@ -446,6 +584,13 @@ Please view this file on the master branch, on stable branches it's out of date.
- Fixes style-lint errors and warnings for EE builds.scss file. - Fixes style-lint errors and warnings for EE builds.scss file.
## 12.2.11
### Fixed (1 change)
- Backport the new reliable fetcher. !21198
## 12.2.8 ## 12.2.8
### Fixed (1 change) ### Fixed (1 change)
@ -794,6 +939,21 @@ Please view this file on the master branch, on stable branches it's out of date.
- Don't send CI usage email notifications for self-hosted instances. !14809 - Don't send CI usage email notifications for self-hosted instances. !14809
## 12.0.12
### Fixed (1 change)
- Backport the new reliable fetcher to 12.0.9. !20532
## 12.0.10
- No changes.
### Fixed (1 change)
- Backport the new reliable fetcher to 12.0.9. !20532
## 12.0.7 ## 12.0.7
### Security (3 changes) ### Security (3 changes)

View file

@ -2,9 +2,424 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 12.5.4 ## 12.6.1
- No changes. - No changes.
### Fixed (2 changes)
- Handle forbidden error when checking for knative. !22170
- Fix stack trace highlight for PHP. !22258
### Performance (1 change)
- Eliminate N+1 queries in PipelinesController#index. !22189
## 12.6.0
### Security (4 changes)
- Update Rugged to v0.28.4.1. !21869
- Update maven_file_name_regex for full string match.
- Add maven file_name regex validation on incoming files.
- Update Workhorse and Gitaly to fix a security issue.
### Removed (1 change)
- Remove downstream pipeline connecting lines. !21196
### Fixed (101 changes, 16 of them are from the community)
- Fix delete user dialog bypass caused by hitting enter. !17343
- Fix broken UI on Environment folder. !17427 (Takuya Noguchi)
- Fix award emoji tooltip being escaped twice if multiple people voted. !19273 (Brian T)
- Use cascading deletes for deleting oauth_openid_requests upon deleting an oauth_access_grant. !19617
- Update merging an MR behavior on the API when pipeline fails. !19641 (briankabiro)
- Vertically align collapse button on epic sidebar. !19656
- Fix projects list to show info in user's locale. !20015 (Arun Kumar Mohan)
- Update padding for cluster alert warning. !20036 (George Tsiolis)
- Show correct warning on issue when project is archived. !20078
- Resets aria-describedby on mouseleave. !20092 (carolcarvalhosa)
- Allow patch notes on repo tags page to word wrap. !20135
- Remove Release edit url for users not allowed to update a release. !20136
- Fix group managed accounts members cleanup. !20157
- Epic tree bug fixes. !20209
- Add missing external-link icon for Crossplane managed app. !20283
- Fixes MR approvers tooltip wrong color. !20287 (Dheeraj Joshi)
- Ignore empty MR diffs when migrating to external storage. !20296
- Add link color to design comments. !20302
- Fix graph groups in monitor dashboard that are hidden on load. !20312
- Update Container Registry naming restrictions to allow for sequential '-'. !20318
- Fixed monitor charts from throwing error when zoomed. !20331
- Validate the merge sha before merging, confirming that the merge will only contain what the user saw. !20348
- Change container registry column name from Tag ID to Image ID. !20349
- Fix dropdown location on the monitoring charts. !20400
- Fixed project import from export ignoring namespace selection. !20405
- Backup: Disable setting of ACL for Google uploads. !20407
- Fix documentation link from empty environment dashboard. !20415
- Move persistent_ref.create into run_after_commit. !20422
- Update external link to provider in cluster settings. !20425
- Fix issue trying to edit weight with collapsed sidebar as guest. !20431
- Handle empty stacktrace and entries with no code. !20458
- Refactor the Deployment model so state machine events are used by both CI and the API. !20474
- Guest users should not delete project snippets they created. !20477
- Accept user-defined dashboard uids in Grafana embeds. !20486
- Fix multi select input padding in project and group user select. !20520 (Kevin Lee)
- Use correct fragment identifier for vulnerability help path. !20524
- Fix group search in groups dropdown. !20535
- Fix removing of child epics that belong to subgroups. !20610
- Fix opening Sentry error details in new tab. !20611
- Ensure next unresolved discussion button takes user to the right place. !20620
- Allow Gitlab GKE clusters to access Google Cloud Registry private images. !20662 (Tan Yee Jian)
- Fix cron parsing for Daylight Savings. !20667
- Fix incorrect new branch name from issue. !20677 (Lee Tickett)
- Improve the way the metrics dashboard waits for data. !20687
- Remove destroy_personal_snippet ability. !20717
- Try longer to clean up after using a gpg-keychain and raise exption if the cleanup fails. !20718
- Fix tooltip hovers in environments table. !20737
- Remove DB transaction from Rebase operation. !20739
- Improve UX for vulnerability dismissal note. !20768
- Fix change to default foreground and backgorund colors in job log. !20787
- Display Labels item in sidebar when Issues are disabled. !20817
- Junit success percentage no longer displays 100% if there are failures. !20835
- Ensure to check create_personal_snippet ability. !20838
- Fix a display bug in the fork removal description message. !20843
- Validate unique environment scope for instance clusters. !20886
- Add empty region when group metrics are missing. !20900
- Adjust issue metrics first_mentioned_in_commit_at calculation. !20923
- Update copy on managed namespace prefixes. !20935
- Add protected branch permission check to run downstream pipelines. !20964
- Fix assignee url in issue board sidebar. !20992 (Lee Tickett)
- Retrieve issues from subgroups when rendering group milestone. !21024
- Adds 409 when user cannot be soft deleted through the API. !21037
- Respect the timezone reported from Gitaly. !21066
- Fix Container repositories can not be replicated when s3 is used. !21068
- Remove redundant toast.scss file and variables. !21105
- Respect snippet query params when displaying embed urls. !21131
- Remove action buttons from designs tab if there are no designs. !21186
- Correctly return stripped PGP text. !21187 (Roger Meier)
- Fix error when linking already linked issue to epic. !21213
- Do not attribute unverified commit e-mails to GitLab users. !21214
- Add nonunique indexes to Labels. !21230
- Fix snippet routes. !21248
- Fix Zoom Quick Action server error when creating a GitLab Issue. !21262
- Rename snippet refactored routes. !21267
- Validate connection section in direct upload config. !21270
- Fix pipeline retry in a CI DAG. !21296
- Authenticate runner requests in Rack::Attack. !21311
- Fix top border of README file header in file list. !21314
- Fix forking a deduplicated project after it was moved to a different shard. !21339
- Fix misaligned approval tr. !21368 (Lee Tickett)
- Fix crash registry contains helm charts. !21381
- Web IDE: Fix the console error that happens when discarding a newly added/uploaded file. !21537
- Authenticate requests with job token as basic auth header for request limiting. !21562
- Fix Single-File-Editor-Layout breaking when branch name is too long. !21577 (Roman Kuba)
- Fix top border of README in vue_file_list. !21578 (Hector Bustillos)
- Stage dropdown lists style corrections. !21607 (Hector Bustillos)
- Change commit_id type on commit_user_mentions table. !21651
- Do not clean the prometheus metrics directory for sidekiq. !21671
- !21542 Part 1: Add new utils for Web IDE store. !21673
- Update auto-deploy-image to v0.8.3. !21696
- Match external user new snippet button visibility to permissions. !21718
- Links to design comments now lead to specific note. !21724
- Re-enable the cloud run feature. !21762
- Ensure forks count cache refresh for source project. !21771
- Fix padding on the design comments. !21839
- Fix "Discard all" for new and renamed files. !21854
- Fix project file finder url encoding file path separators. !21861
- Ensure namespace is present for Managed-Cluster-Applications CI template. !21903
- Rename common template jobs in sast and ds. !22084
- Fixed query behind release filter on merge request search page. !38244
- Activate projects Prometheus service integration when Prometheus managed application is installed on shared cluster.
### Deprecated (4 changes)
- Drop deprecated column from projects table. !18914
- Limit number of projects displayed in GET /groups/:id API. !20023
- Move operations project routes under - scope. !20456
- Move wiki routing under /-/ scope. !21185
### Changed (60 changes, 10 of them are from the community)
- Use better context-specific empty state screens for the Security Dashboards. !18382
- Add evidence collection for Releases. !18874
- Update information and button text for deployment footer. !18918
- Move merge request description into discussions tab. !18940
- Keep details in MR when changing target branch. !19138
- Make internal projects poolable. !19295 (briankabiro)
- Enable support for multiple content query in GraphQL Todo API. !19576
- Allow merge without refresh when new commits are pushed. !19725
- Correct link to Merge trains documentation on MR widget. !19726
- Preserve merge train history. !19864
- Support go-source meta tag for godoc.org. !19888 (Ethan Reesor (@firelizzard))
- Display a better message when starting a discussion on a deleted comment. !20031 (Jacopo Beschi @jacopo-beschi)
- Add sort param to error tracking issue index. !20101
- Add template repository usage to the usage ping. !20126 (minghuan lei)
- Convert flash epic error to form validation error. !20130
- Add 'download' button to Performance Bar. !20205 (Will Chandler)
- SaaS trial copy shows plan. !20207
- Add rbac access to knative-serving namespace deployments to get knative version information. !20244
- Unlock button changed from Icon to String. !20307
- Upgrade to Gitaly v1.72.0. !20313
- Increase upper limit of start_in attribute to 1 week. !20323 (Will Layton)
- Add CI variable to show when Auto-DevOps is explicitly enabled. !20332
- Hashed Storage attachments migration: exclude files in object storage as they are all hashed already. !20338
- Removes caching for design tab discusisons. !20374
- Fixes to inconsistent margins/sapcing in the project detail page. !20395
- Changes to how the search term is styled in the results. !20416
- Move confidence column in the security dashboard. !20435 (Dheeraj Joshi)
- Upgrade to Gitaly v1.73.0. !20443
- Replacing incorrect icon in security dashboard. !20510
- Rework pod logs navigation scheme. !20578
- Reduce start a trial rocket emoji size. !20579
- Upgrade auto-deploy-image for helm default values file. !20588
- Exposed deployment build manual actions for merge request page. !20615
- Upgrade to Gitaly v1.74.0. !20706
- Fetches initial merge request widget data async. !20719
- Add service desk information to project graphQL endpoint. !20722
- Add admin mode controller path to Rack::Attack defaults. !20735 (Diego Louzán)
- Add more filters to SnippetsFinder. !20767
- Clean up the cohorts table. !20779
- Remove vulnerability counter from security tab. !20800
- Only blacklist IPs from Git requests. !20828
- Optimize Deployments endpoint by preloading associations and make record ordering more consistent. !20848
- Update deploy instances color scheme. !20890
- Add service desk information to projects API endpoint. !20913
- Added event tracking to the package details installation components. !20967
- Hide Merge Request information on milestones when MRs are disabled for project. !20985 (Wolfgang Faust)
- Upgrade to Gitaly v1.75.0. !21045
- Evidence - Added restriction for guest on Release page. !21102
- Increase lower DAG `needs` limit from five to ten. !21237
- Add doc links to features on admin dashboard. !21419
- Autofocus cluster dropdown search input. !21440
- Add autofocus to label search fields. !21508
- When a forked project is less visible than its source, merge requests opened in the fork now target the less visible project by default. !21517
- UI improvements in the views for new project from template and the user groups and snippets. !21524 (Hector Bustillos)
- Show merge immediately dialog even if the MR's pipeline hasn't finished. !21556
- Support toggling service desk from API. !21627
- Make `workflow:rules` to work well with Merge Requests. !21742
- Upgrade to Gitaly v1.76.0. !21857
- Remove authentication step from visual review tools instructions.
- Fixes wording on runner admin.
### Performance (22 changes)
- Optimize query for CI pipelines of merge request. !19653
- Replace index on environments table project_id and state with project_id, state, and environment_type. !19902
- Remove reactive caching value keys once the alive key has expired. !20111
- Suggest squash commit messages based on recent commits. !20231
- Improve performance of /api/:version/snippets/public API and only return public personal snippets. !20339
- Add limit for snippet content size. !20346
- Reduce Gitaly calls in BuildHooksWorker. !20365
- Enable ETag caching for MR notes polling. !20440
- Disable public project counts on welcome page. !20517
- Optimize query when Projects API requests private visibility level. !20594
- Improve issues search performance on GraphQL. !20784
- UpdateProjectStatistics updates after commit. !20852
- Run housekeeping after moving a repository between shards. !20863
- Require group_id or project_id for MR target branch autocomplete action. !20933
- Cache the ancestor? Gitaly call to speed up polling for the merge request widget. !20958
- Optimize loading the repository deploy keys page. !20970
- Added lightweight check when retrieving Prometheus metrics. !21099
- Limit max metrics embeds in GFM to 100. !21356
- Fork Puma to validate scheduler fixes. !21547
- Remove an N+1 call rendering projects search results. !21626
- Skip updating LFS objects in mirror updates if repository has not changed. !21744
- Add indexes on deployments to improve environments search. !21789
### Added (117 changes, 16 of them are from the community)
- Add upvote/downvotes attributes to GraphQL Epic query. !14311
- Delete kubernetes cluster association and resources. !16954
- Add badge name field. !16998 (Lee Tickett)
- Add OmniAuth authentication support to admin mode feature. !18214 (Diego Louzán)
- Creates DB tables for storing mentioned users, groups, projects referenced in a note or issuable description. !18316
- Add body data elements for pageview context. !18450
- Added filtering of inherited members for subgroups. !18842
- Added responsiveness to audit events table. !18859
- Add ability to make Jira comments optional. !19004
- Store users, groups, projects mentioned in Markdown to DB tables. !19088
- Upgrade `mail_room` gem to 0.10.0 and enable structured logging. !19186
- Add possibility to save max issue weight on lists. !19220
- Return 422 status code in case of error in submitting comments. !19276 (raju249)
- Add Personal Access Token expiration reminder. !19296
- Add recent search to error tracking. !19301
- Resolve Limit the number of stored sessions per user. !19325
- Add services for 'soft-delete for groups' feature. !19358
- Notify user when over 1000 epics in roadmap. !19419
- Search list of Sentry errors by title in GitLab. !19439
- Add issue statistics to releases on the Releases page. !19448
- Add snowplow events for monitoring dashboard. !19455
- Add snowplow events for APM. !19463
- Add GraphQL mutation to mark all todos done for a user. !19482
- Added rules configuration for Ci::Bridge. !19605
- Add workers for 'soft-delete for groups' feature. !19679
- add tagger within tag view. !19681 (Roger Meier)
- Strong validate import export references. !19682
- Update Release API with evidence related data. !19706
- Graphql query for issues can now be sorted by weight. !19721
- GraphQL for Sentry rror details. !19733
- View closed issues in epic. !19741
- Add API endpoint to unpublish GitLab Pages. !19781
- Add Pipeline Metadata to Packages. !19796
- Create data model for serverless domains. !19835
- Add Unify Circuit project integration service. !19849 (Fabio Huser)
- add sha256 fingerprint to keys model, view and extend users API to search user via fingerprint. !19860 (Roger Meier)
- Allow order_by updated_at in Pipelines API. !19886
- Implement pagination for project releases page. !19912 (Fabio Huser)
- Add migrations for secret snippets. !19939
- Control passing artifacts from CI DAG needs. !19943
- Genereate a set of sample prometheus metrics and route to the sample metrics when enabled. !19987
- Add warning dialog when users click the "Merge immediately" merge train option. !20054
- Expose moved_to_id in issues API. !20083 (Lee Tickett)
- Relate issues when they are marked as duplicated. !20161 (minghuan lei)
- Asks for confirmation before changing project visibility level. !20170
- Allow CI config path to point to a URL or file in a different repository. !20179
- Allow groups to disable mentioning their members, if the group is mentioned. !20184 (Fabio Huser)
- Add modsecurity deployment counts to usage ping. !20196
- Added legend to deploy boards. !20208
- Support passing CI variables via git push options. !20255
- Add GraphQL mutation to restore a Todo. !20261
- Allow specifying Kubernetes namespace for an environment in gitlab-ci.yml. !20270
- Add migrations for 'soft-delete for groups' feature. !20276
- Add Maven installation commands to package detail page for Maven packages. !20300
- Add feature to allow specifying userWithId strategies per environment spec. !20325
- Enable creating Amazon EKS clusters from GitLab. !20333
- Add ability to create new issue from sentry error detail page. !20337
- Convert flash alerts to toasts. !20356
- Return project commit url instead of commits url. !20369 (raju249)
- Collect the date a SaaS trial starts on. !20384
- Add option to delete cached Kubernetes namespaces. !20411
- Create container expiration policies for projects. !20412
- Adjust fork network relations upon project visibility change. !20466
- Create a license info rake task. !20501 (Jason Colyer)
- Add GraphQL mutation for changing due date of an issue. !20577
- Add Snippet GraphQL resolver endpoints. !20613
- Allow Job-Token authentication on Releases creation API. !20632
- Add created_before/after filter to group/project audit events. !20641
- Allow searching of projects by full path. !20659
- Allow administrators to set a minimum password length. !20661
- Update helper text for sentry error tracking settings. !20663 (Rajendra Kadam)
- Adds ability to create issues from sentry details page. !20666
- Add coverage difference visualization to merge request page. !20676 (Fabio Huser)
- Use CI configured namespace for deployments to unmanaged clusters. !20686
- Resolve Design view: Download single issue design image. !20703
- Import large gitlab_project exports via rake task. !20724
- Added Total/Frontend metrics to the performance bar. !20725
- Add dependency scanning flag for skipping automatic bundler audit update. !20743
- Add GraphQL mutation for setting an issue as confidential. !20785
- Track adding metric via monitoring dashboard. !20818
- Add _links object to package api response. !20820
- CI template for installing cluster applications. !20822
- Add SalesforceDX project template. !20831
- Allow NPM package downloads with CI_JOB_TOKEN. !20868
- Allow raw blobs to be served from an external storage. !20936
- Added Snippets GraphQL mutations. !20956
- Added WebHookLogs for ServiceHooks. !20976
- Surface GitLab issue in error detail page. !21019
- Add type to broadcast messages. !21038
- add OpenAPI file viewer. !21106 (Roger Meier)
- Add updated_before and updated_after filters to the Pipelines API endpoint. !21133
- Implement pagination for sentry errors. !21136
- Add support for Conan package management in the package registry. !21152
- Add syntax highlight for Sentry error stack trace. !21182
- Keyset pagination for REST API (Project endpoint). !21194
- CI template for Sentry managed app. !21208
- Add CI variable to set the version of pip when scanning dependencies of Python projects. !21218
- Add dependency scanning flag for specifying pip requirements file for scanning. !21219
- Do not allow specifying a Kubernetes namespace via CI template for managed clusters. !21223
- Sort Sentry error list by first seen, last seen or frequency. !21250
- Add documentation about dependency scanning gradle support. !21253
- Allow PDF attachments to be opened on browser. !21272
- Add child label to commit box. !21323
- Update Knative to 0.9.0. !21361 (cab105)
- Add target_path to broadcast message API. !21430
- Allow Kubernetes namespaces specified via CI template to be used for terminals, pod logs and deploy boards. !21460
- Allow styling broadcast messages. !21522
- Enable new job log by default. !21543
- Document support for sbt dependency scanning. !21588
- Return multiple errors from CI linter. !21589
- Add specific error states to dashboard. !21618
- Add timestamps to pod logs. !21663
- Hide profile information when user is blocked. !21706
- link to group on group admin page. !21709
- Added migration which adds service desk username column. !21733
- Add SentryIssue table to store a link between issue and sentry issue. !37026
- Add path based targeting to broadcast messages.
### Other (51 changes, 28 of them are from the community)
- Remove done callbacks from vue_shared/components/markdown. !16842 (Lee Tickett)
- Update timeago to the latest release. !19407
- Improve job tokens and provide access helper. !19793
- Add post deployment migration to complete pages metadata migration. !19928
- Resolve Document - Make using GitLab auth with Vault easy. !19980
- Remove IIFEs from gl_dropdown.js. !19983 (nuwe1)
- Improve sparkline chart in MR widget deployment. !20085
- Updated jekyll project_template. !20090 (Marc Schwede)
- Updated hexo project_template. !20105 (Marc Schwede)
- Updated hugo project_template. !20109 (Marc Schwede)
- Resolve environment rollback was not friendly. !20121
- Removed all references of BoardService. !20144 (nuwe1)
- Removes references of BoardService in list file. !20145 (nuwe1)
- replace var gl_dropdown.js. !20166 (nuwe1)
- delete board_service.js. !20168 (nuwe1)
- Improve create confidential MR dropdown styling. !20176 (Lee Tickett)
- Remove milestone_id from epics. !20187 (Lee Tickett)
- Remove build badge path from route. !20188 (Lee Tickett)
- Add worker attributes to Sidekiq metrics. !20292
- Update GitLab Runner Helm Chart to 0.11.0. !20461
- add missing test for add_index rubocop rule. !20464 (Eric Thomas)
- Suppress progress on pulling image on Code Quality of Auto DevOps. !20604 (Takuya Noguchi)
- Increase margin between project stats. !20606
- Remove extra spacing below sidebar time tracking info. !20657 (Lee Tickett)
- Add e2e qa test for email delivery. !20675 (Diego Louzán)
- Collect project import failures instead of failing fast. !20727
- Removed unused methods in monitoring dashboard. !20819
- removes references of BoardService. !20872 (nuwe1)
- removes references of BoardService. !20874 (nuwe1)
- removes references of BoardService. !20875 (nuwe1)
- removes references of BoardService. !20876 (nuwe1)
- removes references of BoardService. !20877 (nuwe1)
- removes references of BoardService. !20879 (nuwe1)
- removes references of BoardService. !20880 (nuwe1)
- removes references of BoardService. !20881 (nuwe1)
- Remove whitespaces between tree-controls elements. !20952
- Add Project Export request/download rate limits. !20962
- Remove feature flag for limiting diverging commit counts. !20999
- Changed 'Add approvers' to 'Approval rules'. !21079
- Resolve Add missing popover and remove none in MR widget. !21095
- Change Puma log format to JSON. !21101
- Update GitLab Shell to v10.3.0. !21151
- Improve diff expansion text. !21616
- Remove var from app/assets/javascripts/commit/image_file.js. !21649 (Abubakar Hassan)
- Rename User#full_private_access? to User#can_read_all_resources?. !21668 (Diego Louzán)
- Replace CI_COMMIT_REF with CI_COMMIT_SHA on CI docs. !21781 (Takuya Noguchi)
- Add reportSnippet permission to Snippet GraphQL. !21836
- Harmonize capitalization on cluster UI. !21878 (Evan Read)
- Add mark as spam snippet mutation. !21912
- Update Workhorse to v8.18.0. !22091
- Replace Font Awesome bullhorn icon with GitLab bullhorn icon.
## 12.5.5
### Security (1 change)
- Upgrade Akismet gem to v3.0.0. !21786
### Fixed (2 changes)
- Fix error in updating runner session. !20902
- Fix Asana integration. !21501
## 12.5.4
### Security (1 change)
- Update maven_file_name_regex for full string match.
## 12.5.3 ## 12.5.3
@ -20,13 +435,6 @@ entry.
- Flatten exception details in API and controller logs. !20434 - Flatten exception details in API and controller logs. !20434
## 12.5.2
### Security (1 change)
- Fix 500 error caused by invalid byte sequences in links.
## 12.5.1 ## 12.5.1
### Security (11 changes) ### Security (11 changes)
@ -395,6 +803,18 @@ entry.
- Change selects from default browser style to custom style. - Change selects from default browser style to custom style.
## 12.4.5
- No changes.
## 12.4.3
### Fixed (2 changes)
- Only enable protected paths for POST requests. !19184
- Fix Bitbucket Cloud importer pull request state. !19734
## 12.4.2 ## 12.4.2
### Fixed (10 changes) ### Fixed (10 changes)
@ -755,6 +1175,31 @@ entry.
- Remove Postgresql specific setup tasks and move to schema.rb. - Remove Postgresql specific setup tasks and move to schema.rb.
## 12.3.9
### Security (1 change)
- Update maven_file_name_regex for full string match.
## 12.3.7
### Security (12 changes)
- Do not create todos for approvers without access. !1442
- Limit potential for DNS rebind SSRF in chat notifications.
- Encrypt application setting tokens.
- Update Workhorse and Gitaly to fix a security issue.
- Add maven file_name regex validation on incoming files.
- Hide commit counts from guest users in Cycle Analytics.
- Check permissions before showing a forked project's source.
- Fix 500 error caused by invalid byte sequences in links.
- Ensure are cleaned by ImportExport::AttributeCleaner.
- Remove notes regarding Related Branches from Issue activity feeds for guest users.
- Escape namespace in label references to prevent XSS.
- Add authorization to using filter vulnerable in Dependency List.
## 12.3.4 ## 12.3.4
### Fixed (2 changes) ### Fixed (2 changes)
@ -1060,6 +1505,10 @@ entry.
- Updates tooltip of 'detached' label/state. - Updates tooltip of 'detached' label/state.
## 12.2.11
- No changes.
## 12.2.8 ## 12.2.8
### Security (1 change) ### Security (1 change)
@ -1774,6 +2223,15 @@ entry.
- Removes EE differences for app/views/admin/users/show.html.haml. - Removes EE differences for app/views/admin/users/show.html.haml.
## 12.0.12
- No changes.
## 12.0.10
- No changes.
- No changes.
## 12.0.7 ## 12.0.7
### Security (22 changes) ### Security (22 changes)

View file

@ -5,6 +5,7 @@ require_relative 'lib/gitlab/danger/request_helper'
danger.import_plugin('danger/plugins/helper.rb') danger.import_plugin('danger/plugins/helper.rb')
danger.import_plugin('danger/plugins/roulette.rb') danger.import_plugin('danger/plugins/roulette.rb')
danger.import_plugin('danger/plugins/changelog.rb')
unless helper.release_automation? unless helper.release_automation?
GitlabDanger.new(helper.gitlab_helper).rule_names.each do |file| GitlabDanger.new(helper.gitlab_helper).rule_names.each do |file|

View file

@ -1 +1 @@
1.72.1 a4b6c71d4b7c1588587345e2dfe0c6bd7cc63a83

View file

@ -1 +1 @@
10.2.0 10.3.0

View file

@ -1 +1 @@
8.14.1 8.18.0

53
Gemfile
View file

@ -22,6 +22,7 @@ gem 'rugged', '~> 0.28'
gem 'grape-path-helpers', '~> 1.1' gem 'grape-path-helpers', '~> 1.1'
gem 'faraday', '~> 0.12' gem 'faraday', '~> 0.12'
gem 'marginalia', '~> 1.8.0'
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.6' gem 'devise', '~> 4.6'
@ -53,7 +54,7 @@ gem 'gssapi', group: :kerberos
# Spam and anti-bot protection # Spam and anti-bot protection
gem 'recaptcha', '~> 4.11', require: 'recaptcha/rails' gem 'recaptcha', '~> 4.11', require: 'recaptcha/rails'
gem 'akismet', '~> 2.0' gem 'akismet', '~> 3.0'
gem 'invisible_captcha', '~> 0.12.1' gem 'invisible_captcha', '~> 0.12.1'
# Two-factor authentication # Two-factor authentication
@ -101,7 +102,7 @@ gem 'hashie-forbidden_attributes'
gem 'kaminari', '~> 1.0' gem 'kaminari', '~> 1.0'
# HAML # HAML
gem 'hamlit', '~> 2.8.8' gem 'hamlit', '~> 2.11.0'
# Files attachments # Files attachments
gem 'carrierwave', '~> 1.3' gem 'carrierwave', '~> 1.3'
@ -135,11 +136,11 @@ gem 'aws-sdk'
gem 'faraday_middleware-aws-signers-v4' gem 'faraday_middleware-aws-signers-v4'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 2.8' gem 'html-pipeline', '~> 2.12'
gem 'deckar01-task_list', '2.2.1' gem 'deckar01-task_list', '2.2.1'
gem 'gitlab-markup', '~> 1.7.0' gem 'gitlab-markup', '~> 1.7.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup' gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'commonmarker', '~> 0.17' gem 'commonmarker', '~> 0.20'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.0' gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
@ -170,8 +171,8 @@ group :unicorn do
end end
group :puma do group :puma do
gem 'puma', '~> 3.12', require: false gem 'gitlab-puma', '~> 4.3.1.gitlab.2', require: false
gem 'puma_worker_killer', require: false gem 'gitlab-puma_worker_killer', '~> 0.1.1.gitlab.1', require: false
gem 'rack-timeout', require: false gem 'rack-timeout', require: false
end end
@ -242,7 +243,7 @@ gem 'slack-notifier', '~> 1.5.1'
gem 'hangouts-chat', '~> 0.0.5' gem 'hangouts-chat', '~> 0.0.5'
# Asana integration # Asana integration
gem 'asana', '~> 0.8.1' gem 'asana', '~> 0.9'
# FogBugz integration # FogBugz integration
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
@ -273,8 +274,8 @@ gem 'mimemagic', '~> 0.3.2'
gem 'fast_blank' gem 'fast_blank'
# Parse time & duration # Parse time & duration
gem 'chronic', '~> 0.10.2' gem 'gitlab-chronic', '~> 0.10.5'
gem 'gitlab_chronic_duration', '~> 0.10.6.1' gem 'gitlab_chronic_duration', '~> 0.10.6.2'
gem 'webpack-rails', '~> 0.9.10' gem 'webpack-rails', '~> 0.9.10'
gem 'rack-proxy', '~> 0.6.0' gem 'rack-proxy', '~> 0.6.0'
@ -312,8 +313,7 @@ gem 'gettext', '~> 3.2.2', require: false, group: :development
gem 'batch-loader', '~> 1.4.0' gem 'batch-loader', '~> 1.4.0'
# Perf bar # Perf bar
# https://gitlab.com/gitlab-org/gitlab/issues/13996 gem 'peek', '~> 1.1'
gem 'gitlab-peek', '~> 0.0.1', require: 'peek'
# Snowplow events tracking # Snowplow events tracking
gem 'snowplow-tracker', '~> 0.6.1' gem 'snowplow-tracker', '~> 0.6.1'
@ -347,20 +347,15 @@ group :development do
end end
group :development, :test do group :development, :test do
gem 'bullet', '~> 5.5.0', require: !!ENV['ENABLE_BULLET'] gem 'bullet', '~> 6.0.2', require: !!ENV['ENABLE_BULLET']
gem 'pry-byebug', '~> 3.5.1', platform: :mri gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem 'pry-rails', '~> 0.3.4' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', require: false gem 'awesome_print', require: false
gem 'fuubar', '~> 2.2.0'
gem 'database_cleaner', '~> 1.7.0' gem 'database_cleaner', '~> 1.7.0'
gem 'factory_bot_rails', '~> 5.1.0' gem 'factory_bot_rails', '~> 5.1.0'
gem 'rspec-rails', '~> 3.8.0' gem 'rspec-rails', '~> 4.0.0.beta3'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
gem 'rspec-parameterized', require: false
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826) # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.11.0' gem 'minitest', '~> 5.11.0'
@ -368,10 +363,6 @@ group :development, :test do
# Generate Fake data # Generate Fake data
gem 'ffaker', '~> 2.10' gem 'ffaker', '~> 2.10'
gem 'capybara', '~> 3.22.0'
gem 'capybara-screenshot', '~> 1.0.22'
gem 'selenium-webdriver', '~> 3.141'
gem 'spring', '~> 2.0.0' gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-rspec', '~> 1.0.4'
@ -382,7 +373,7 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.22.1' gem 'rubocop-rspec', '~> 1.22.1'
gem 'scss_lint', '~> 0.56.0', require: false gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.31.0', require: false gem 'haml_lint', '~> 0.34.0', require: false
gem 'simplecov', '~> 0.16.1', require: false gem 'simplecov', '~> 0.16.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false gem 'bundler-audit', '~> 0.5.0', require: false
@ -390,7 +381,7 @@ group :development, :test do
gem 'knapsack', '~> 1.17' gem 'knapsack', '~> 1.17'
gem 'stackprof', '~> 0.2.10', require: false gem 'stackprof', '~> 0.2.13', require: false
gem 'simple_po_parser', '~> 1.1.2', require: false gem 'simple_po_parser', '~> 1.1.2', require: false
@ -403,6 +394,16 @@ group :development, :test, :omnibus do
end end
group :test do group :test do
gem 'fuubar', '~> 2.2.0'
gem 'rspec-retry', '~> 0.6.1'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
gem 'rspec-parameterized', require: false
gem 'capybara', '~> 3.22.0'
gem 'capybara-screenshot', '~> 1.0.22'
gem 'selenium-webdriver', '~> 3.142'
gem 'shoulda-matchers', '~> 4.0.1', require: false gem 'shoulda-matchers', '~> 4.0.1', require: false
gem 'email_spec', '~> 2.2.0' gem 'email_spec', '~> 2.2.0'
gem 'json-schema', '~> 2.8.0' gem 'json-schema', '~> 2.8.0'
@ -416,7 +417,7 @@ end
gem 'octokit', '~> 4.9' gem 'octokit', '~> 4.9'
gem 'mail_room', '~> 0.9.1' gem 'mail_room', '~> 0.10.0'
gem 'email_reply_trimmer', '~> 0.1' gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text' gem 'html2text'
@ -451,7 +452,7 @@ group :ed25519 do
end end
# Gitaly GRPC protocol definitions # Gitaly GRPC protocol definitions
gem 'gitaly', '~> 1.70.0' gem 'gitaly', '~> 1.73.0'
gem 'grpc', '~> 1.24.0' gem 'grpc', '~> 1.24.0'

View file

@ -58,16 +58,16 @@ GEM
addressable (2.5.2) addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0) public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1) aes_key_wrap (1.0.1)
akismet (2.0.0) akismet (3.0.0)
apollo_upload_server (2.0.0.beta.3) apollo_upload_server (2.0.0.beta.3)
graphql (>= 1.8) graphql (>= 1.8)
rails (>= 4.2) rails (>= 4.2)
arel (9.0.0) arel (9.0.0)
asana (0.8.1) asana (0.9.3)
faraday (~> 0.9) faraday (~> 0.9)
faraday_middleware (~> 0.9) faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.4)
asciidoctor (2.0.10) asciidoctor (2.0.10)
asciidoctor-include-ext (0.3.1) asciidoctor-include-ext (0.3.1)
asciidoctor (>= 1.5.6, < 3.0.0) asciidoctor (>= 1.5.6, < 3.0.0)
@ -118,9 +118,9 @@ GEM
brakeman (4.2.1) brakeman (4.2.1)
browser (2.5.3) browser (2.5.3)
builder (3.2.3) builder (3.2.3)
bullet (5.5.1) bullet (6.0.2)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0) uniform_notifier (~> 1.11)
bundler-audit (0.5.0) bundler-audit (0.5.0)
bundler (~> 1.2) bundler (~> 1.2)
thor (~> 0.18) thor (~> 0.18)
@ -143,9 +143,7 @@ GEM
cause (0.1) cause (0.1)
character_set (1.1.2) character_set (1.1.2)
charlock_holmes (0.7.6) charlock_holmes (0.7.6)
childprocess (0.9.0) childprocess (3.0.0)
ffi (~> 1.0, >= 1.0.11)
chronic (0.10.2)
chunky_png (1.3.5) chunky_png (1.3.5)
citrus (3.0.2) citrus (3.0.2)
claide (1.0.3) claide (1.0.3)
@ -157,7 +155,7 @@ GEM
coercible (1.0.0) coercible (1.0.0)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
colored2 (3.1.2) colored2 (3.1.2)
commonmarker (0.17.13) commonmarker (0.20.1)
ruby-enum (~> 0.5) ruby-enum (~> 0.5)
concord (0.1.5) concord (0.1.5)
adamantium (~> 0.2.0) adamantium (~> 0.2.0)
@ -287,7 +285,7 @@ GEM
fast_blank (1.0.0) fast_blank (1.0.0)
fast_gettext (1.6.0) fast_gettext (1.6.0)
ffaker (2.10.0) ffaker (2.10.0)
ffi (1.11.1) ffi (1.11.3)
flipper (0.17.1) flipper (0.17.1)
flipper-active_record (0.17.1) flipper-active_record (0.17.1)
activerecord (>= 4.2, < 7) activerecord (>= 4.2, < 7)
@ -359,9 +357,11 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
git (1.5.0) git (1.5.0)
gitaly (1.70.0) gitaly (1.73.0)
grpc (~> 1.0) grpc (~> 1.0)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
gitlab-labkit (0.7.0) gitlab-labkit (0.7.0)
actionpack (>= 5.0.0, < 6.1.0) actionpack (>= 5.0.0, < 6.1.0)
activesupport (>= 5.0.0, < 6.1.0) activesupport (>= 5.0.0, < 6.1.0)
@ -372,8 +372,11 @@ GEM
gitlab-license (1.0.0) gitlab-license (1.0.0)
gitlab-markup (1.7.0) gitlab-markup (1.7.0)
gitlab-net-dns (0.9.1) gitlab-net-dns (0.9.1)
gitlab-peek (0.0.1) gitlab-puma (4.3.1.gitlab.2)
railties (>= 4.0.0) nio4r (~> 2.0)
gitlab-puma_worker_killer (0.1.1.gitlab.1)
get_process_mem (~> 0.2)
gitlab-puma (>= 2.7, < 5)
gitlab-sidekiq-fetcher (0.5.2) gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5) sidekiq (~> 5)
gitlab-styles (2.8.0) gitlab-styles (2.8.0)
@ -381,8 +384,8 @@ GEM
rubocop-gitlab-security (~> 0.1.0) rubocop-gitlab-security (~> 0.1.0)
rubocop-performance (~> 1.1.0) rubocop-performance (~> 1.1.0)
rubocop-rspec (~> 1.19) rubocop-rspec (~> 1.19)
gitlab_chronic_duration (0.10.6.1) gitlab_chronic_duration (0.10.6.2)
numerizer (~> 0.1.1) numerizer (~> 0.2)
gitlab_omniauth-ldap (2.1.1) gitlab_omniauth-ldap (2.1.1)
net-ldap (~> 0.16) net-ldap (~> 0.16)
omniauth (~> 1.3) omniauth (~> 1.3)
@ -460,17 +463,16 @@ GEM
guard (~> 2.1) guard (~> 2.1)
guard-compat (~> 1.1) guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0) rspec (>= 2.99.0, < 4.0)
haml (5.0.4) haml (5.1.2)
temple (>= 0.8.0) temple (>= 0.8.0)
tilt tilt
haml_lint (0.31.0) haml_lint (0.34.0)
haml (>= 4.0, < 5.1) haml (>= 4.0, < 5.2)
rainbow rainbow
rake (>= 10, < 13)
rubocop (>= 0.50.0) rubocop (>= 0.50.0)
sysexits (~> 1.1) sysexits (~> 1.1)
hamlit (2.8.8) hamlit (2.11.0)
temple (>= 0.8.0) temple (>= 0.8.2)
thor thor
tilt tilt
hangouts-chat (0.0.5) hangouts-chat (0.0.5)
@ -484,7 +486,7 @@ GEM
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
mimemagic mimemagic
html-pipeline (2.8.4) html-pipeline (2.12.2)
activesupport (>= 2) activesupport (>= 2)
nokogiri (>= 1.4) nokogiri (>= 1.4)
html2text (0.2.0) html2text (0.2.0)
@ -591,9 +593,12 @@ GEM
lumberjack (1.0.13) lumberjack (1.0.13)
mail (2.7.1) mail (2.7.1)
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
mail_room (0.9.1) mail_room (0.10.0)
marcel (0.3.3) marcel (0.3.3)
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
marginalia (1.8.0)
actionpack (>= 2.3)
activerecord (>= 2.3)
memoist (0.16.0) memoist (0.16.0)
memoizable (0.4.2) memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
@ -622,7 +627,7 @@ GEM
net-ntp (2.1.3) net-ntp (2.1.3)
net-ssh (5.2.0) net-ssh (5.2.0)
netrc (0.11.0) netrc (0.11.0)
nio4r (2.3.1) nio4r (2.5.2)
no_proxy_fix (0.1.2) no_proxy_fix (0.1.2)
nokogiri (1.10.5) nokogiri (1.10.5)
mini_portile2 (~> 2.4.0) mini_portile2 (~> 2.4.0)
@ -631,7 +636,7 @@ GEM
notiffany (0.1.3) notiffany (0.1.3)
nenv (~> 0.1) nenv (~> 0.1)
shellany (~> 0.0) shellany (~> 0.0)
numerizer (0.1.1) numerizer (0.2.0)
oauth (0.5.4) oauth (0.5.4)
oauth2 (1.4.1) oauth2 (1.4.1)
faraday (>= 0.8, < 0.16.0) faraday (>= 0.8, < 0.16.0)
@ -724,6 +729,8 @@ GEM
parser (2.6.3.0) parser (2.6.3.0)
ast (~> 2.4.0) ast (~> 2.4.0)
parslet (1.8.2) parslet (1.8.2)
peek (1.1.0)
railties (>= 4.0.0)
pg (1.1.4) pg (1.1.4)
po_to_json (1.0.1) po_to_json (1.0.1)
json (>= 1.6.0) json (>= 1.6.0)
@ -749,10 +756,6 @@ GEM
pry-rails (0.3.6) pry-rails (0.3.6)
pry (>= 0.10.4) pry (>= 0.10.4)
public_suffix (3.1.1) public_suffix (3.1.1)
puma (3.12.0)
puma_worker_killer (0.1.0)
get_process_mem (~> 0.2)
puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
raabro (1.1.6) raabro (1.1.6)
rack (2.0.7) rack (2.0.7)
@ -881,14 +884,14 @@ GEM
proc_to_ast proc_to_ast
rspec (>= 2.13, < 4) rspec (>= 2.13, < 4)
unparser unparser
rspec-rails (3.8.2) rspec-rails (4.0.0.beta3)
actionpack (>= 3.0) actionpack (>= 4.2)
activesupport (>= 3.0) activesupport (>= 4.2)
railties (>= 3.0) railties (>= 4.2)
rspec-core (~> 3.8.0) rspec-core (~> 3.8)
rspec-expectations (~> 3.8.0) rspec-expectations (~> 3.8)
rspec-mocks (~> 3.8.0) rspec-mocks (~> 3.8)
rspec-support (~> 3.8.0) rspec-support (~> 3.8)
rspec-retry (0.6.1) rspec-retry (0.6.1)
rspec-core (> 3.3) rspec-core (> 3.3)
rspec-set (0.1.3) rspec-set (0.1.3)
@ -927,7 +930,7 @@ GEM
rubyntlm (0.6.2) rubyntlm (0.6.2)
rubypants (0.2.0) rubypants (0.2.0)
rubyzip (1.3.0) rubyzip (1.3.0)
rugged (0.28.3.1) rugged (0.28.4.1)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (4.6.6) sanitize (4.6.6)
crass (~> 1.0.2) crass (~> 1.0.2)
@ -956,9 +959,9 @@ GEM
seed-fu (2.3.7) seed-fu (2.3.7)
activerecord (>= 3.1) activerecord (>= 3.1)
activesupport (>= 3.1) activesupport (>= 3.1)
selenium-webdriver (3.141.0) selenium-webdriver (3.142.6)
childprocess (~> 0.5) childprocess (>= 0.5, < 4.0)
rubyzip (~> 1.2, >= 1.2.2) rubyzip (>= 1.2.2)
sentry-raven (2.9.0) sentry-raven (2.9.0)
faraday (>= 0.7.6, < 1.0) faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9) settingslogic (2.0.9)
@ -1002,7 +1005,7 @@ GEM
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sqlite3 (1.3.13) sqlite3 (1.3.13)
sshkey (2.0.0) sshkey (2.0.0)
stackprof (0.2.10) stackprof (0.2.13)
state_machines (0.5.0) state_machines (0.5.0)
state_machines-activemodel (0.7.1) state_machines-activemodel (0.7.1)
activemodel (>= 4.1) activemodel (>= 4.1)
@ -1017,7 +1020,7 @@ GEM
sys-filesystem (1.1.6) sys-filesystem (1.1.6)
ffi ffi
sysexits (1.2.0) sysexits (1.2.0)
temple (0.8.1) temple (0.8.2)
terminal-table (1.8.0) terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1, >= 1.1.1)
test-prof (0.10.0) test-prof (0.10.0)
@ -1026,10 +1029,10 @@ GEM
daemons (~> 1.0, >= 1.0.9) daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4) eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3) rack (>= 1, < 3)
thor (0.19.4) thor (0.20.3)
thread_safe (0.3.6) thread_safe (0.3.6)
thrift (0.11.0.0) thrift (0.11.0.0)
tilt (2.0.9) tilt (2.0.10)
timecop (0.8.1) timecop (0.8.1)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
toml (0.2.0) toml (0.2.0)
@ -1057,7 +1060,7 @@ GEM
unicorn-worker-killer (0.4.4) unicorn-worker-killer (0.4.4)
get_process_mem (~> 0) get_process_mem (~> 0)
unicorn (>= 4, < 6) unicorn (>= 4, < 6)
uniform_notifier (1.10.0) uniform_notifier (1.13.0)
unleash (0.1.5) unleash (0.1.5)
murmurhash3 (~> 0.1.6) murmurhash3 (~> 0.1.6)
unparser (0.4.5) unparser (0.4.5)
@ -1117,9 +1120,9 @@ DEPENDENCIES
activerecord-explain-analyze (~> 0.1) activerecord-explain-analyze (~> 0.1)
acts-as-taggable-on (~> 6.0) acts-as-taggable-on (~> 6.0)
addressable (~> 2.5.2) addressable (~> 2.5.2)
akismet (~> 2.0) akismet (~> 3.0)
apollo_upload_server (~> 2.0.0.beta3) apollo_upload_server (~> 2.0.0.beta3)
asana (~> 0.8.1) asana (~> 0.9)
asciidoctor (~> 2.0.10) asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1) asciidoctor-include-ext (~> 0.3.1)
asciidoctor-plantuml (= 0.0.9) asciidoctor-plantuml (= 0.0.9)
@ -1139,14 +1142,13 @@ DEPENDENCIES
bootstrap_form (~> 4.2.0) bootstrap_form (~> 4.2.0)
brakeman (~> 4.2) brakeman (~> 4.2)
browser (~> 2.5) browser (~> 2.5)
bullet (~> 5.5.0) bullet (~> 6.0.2)
bundler-audit (~> 0.5.0) bundler-audit (~> 0.5.0)
capybara (~> 3.22.0) capybara (~> 3.22.0)
capybara-screenshot (~> 1.0.22) capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3) carrierwave (~> 1.3)
charlock_holmes (~> 0.7.5) charlock_holmes (~> 0.7.5)
chronic (~> 0.10.2) commonmarker (~> 0.20)
commonmarker (~> 0.17)
concurrent-ruby (~> 1.1) concurrent-ruby (~> 1.1)
connection_pool (~> 2.0) connection_pool (~> 2.0)
countries (~> 3.0) countries (~> 3.0)
@ -1194,16 +1196,18 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly (~> 1.70.0) gitaly (~> 1.73.0)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-labkit (~> 0.5) gitlab-labkit (~> 0.5)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
gitlab-markup (~> 1.7.0) gitlab-markup (~> 1.7.0)
gitlab-net-dns (~> 0.9.1) gitlab-net-dns (~> 0.9.1)
gitlab-peek (~> 0.0.1) gitlab-puma (~> 4.3.1.gitlab.2)
gitlab-puma_worker_killer (~> 0.1.1.gitlab.1)
gitlab-sidekiq-fetcher (= 0.5.2) gitlab-sidekiq-fetcher (= 0.5.2)
gitlab-styles (~> 2.7) gitlab-styles (~> 2.7)
gitlab_chronic_duration (~> 0.10.6.1) gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.1.1) gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2) gon (~> 6.2)
google-api-client (~> 0.23) google-api-client (~> 0.23)
@ -1219,13 +1223,13 @@ DEPENDENCIES
grpc (~> 1.24.0) grpc (~> 1.24.0)
gssapi gssapi
guard-rspec guard-rspec
haml_lint (~> 0.31.0) haml_lint (~> 0.34.0)
hamlit (~> 2.8.8) hamlit (~> 2.11.0)
hangouts-chat (~> 0.0.5) hangouts-chat (~> 0.0.5)
hashie-forbidden_attributes hashie-forbidden_attributes
health_check (~> 2.6.0) health_check (~> 2.6.0)
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
html-pipeline (~> 2.8) html-pipeline (~> 2.12)
html2text html2text
httparty (~> 0.16.4) httparty (~> 0.16.4)
icalendar icalendar
@ -1243,7 +1247,8 @@ DEPENDENCIES
licensee (~> 8.9) licensee (~> 8.9)
lograge (~> 0.5) lograge (~> 0.5)
loofah (~> 2.2) loofah (~> 2.2)
mail_room (~> 0.9.1) mail_room (~> 0.10.0)
marginalia (~> 1.8.0)
memory_profiler (~> 0.9) memory_profiler (~> 0.9)
method_source (~> 0.8) method_source (~> 0.8)
mimemagic (~> 0.3.2) mimemagic (~> 0.3.2)
@ -1275,13 +1280,12 @@ DEPENDENCIES
omniauth_crowd (~> 2.2.0) omniauth_crowd (~> 2.2.0)
omniauth_openid_connect (~> 0.3.3) omniauth_openid_connect (~> 0.3.3)
org-ruby (~> 0.9.12) org-ruby (~> 0.9.12)
peek (~> 1.1)
pg (~> 1.1) pg (~> 1.1)
premailer-rails (~> 1.10.3) premailer-rails (~> 1.10.3)
prometheus-client-mmap (~> 0.9.10) prometheus-client-mmap (~> 0.9.10)
pry-byebug (~> 3.5.1) pry-byebug (~> 3.5.1)
pry-rails (~> 0.3.4) pry-rails (~> 0.3.4)
puma (~> 3.12)
puma_worker_killer
rack (~> 2.0.7) rack (~> 2.0.7)
rack-attack (~> 6.2.0) rack-attack (~> 6.2.0)
rack-cors (~> 1.0.0) rack-cors (~> 1.0.0)
@ -1307,7 +1311,7 @@ DEPENDENCIES
rouge (~> 3.11.0) rouge (~> 3.11.0)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-parameterized rspec-parameterized
rspec-rails (~> 3.8.0) rspec-rails (~> 4.0.0.beta3)
rspec-retry (~> 0.6.1) rspec-retry (~> 0.6.1)
rspec-set (~> 0.1.3) rspec-set (~> 0.1.3)
rspec_junit_formatter rspec_junit_formatter
@ -1325,7 +1329,7 @@ DEPENDENCIES
sassc-rails (~> 2.1.0) sassc-rails (~> 2.1.0)
scss_lint (~> 0.56.0) scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7) seed-fu (~> 2.3.7)
selenium-webdriver (~> 3.141) selenium-webdriver (~> 3.142)
sentry-raven (~> 2.9) sentry-raven (~> 2.9)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
shoulda-matchers (~> 4.0.1) shoulda-matchers (~> 4.0.1)
@ -1339,7 +1343,7 @@ DEPENDENCIES
spring-commands-rspec (~> 1.0.4) spring-commands-rspec (~> 1.0.4)
sprockets (~> 3.7.0) sprockets (~> 3.7.0)
sshkey (~> 2.0) sshkey (~> 2.0)
stackprof (~> 0.2.10) stackprof (~> 0.2.13)
state_machines-activerecord (~> 0.6.0) state_machines-activerecord (~> 0.6.0)
sys-filesystem (~> 1.1.6) sys-filesystem (~> 1.1.6)
test-prof (~> 0.10.0) test-prof (~> 0.10.0)

View file

@ -1 +1 @@
12.5.4 12.6.1

View file

@ -5,6 +5,8 @@ import { joinPaths } from './lib/utils/url_utility';
import flash from '~/flash'; import flash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
const DEFAULT_PER_PAGE = 20;
const Api = { const Api = {
groupsPath: '/api/:version/groups.json', groupsPath: '/api/:version/groups.json',
groupPath: '/api/:version/groups/:id', groupPath: '/api/:version/groups/:id',
@ -41,7 +43,7 @@ const Api = {
releasesPath: '/api/:version/projects/:id/releases', releasesPath: '/api/:version/projects/:id/releases',
releasePath: '/api/:version/projects/:id/releases/:tag_name', releasePath: '/api/:version/projects/:id/releases/:tag_name',
mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines', mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
adminStatisticsPath: 'api/:version/application/statistics', adminStatisticsPath: '/api/:version/application/statistics',
group(groupId, callback) { group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
@ -66,7 +68,7 @@ const Api = {
params: Object.assign( params: Object.assign(
{ {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
}, },
options, options,
), ),
@ -90,7 +92,7 @@ const Api = {
.get(url, { .get(url, {
params: { params: {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
}, },
}) })
.then(({ data }) => callback(data)); .then(({ data }) => callback(data));
@ -101,7 +103,7 @@ const Api = {
const url = Api.buildUrl(Api.projectsPath); const url = Api.buildUrl(Api.projectsPath);
const defaults = { const defaults = {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
simple: true, simple: true,
}; };
@ -126,7 +128,7 @@ const Api = {
.get(url, { .get(url, {
params: { params: {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
...options, ...options,
}, },
}) })
@ -235,7 +237,7 @@ const Api = {
const url = Api.buildUrl(Api.groupProjectsPath).replace(':id', groupId); const url = Api.buildUrl(Api.groupProjectsPath).replace(':id', groupId);
const defaults = { const defaults = {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
}; };
return axios return axios
.get(url, { .get(url, {
@ -325,7 +327,7 @@ const Api = {
params: Object.assign( params: Object.assign(
{ {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
}, },
options, options,
), ),
@ -355,7 +357,7 @@ const Api = {
const url = Api.buildUrl(Api.userProjectsPath).replace(':id', userId); const url = Api.buildUrl(Api.userProjectsPath).replace(':id', userId);
const defaults = { const defaults = {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
}; };
return axios return axios
.get(url, { .get(url, {
@ -371,7 +373,7 @@ const Api = {
return axios.get(url, { return axios.get(url, {
params: { params: {
search: query, search: query,
per_page: 20, per_page: DEFAULT_PER_PAGE,
...options, ...options,
}, },
}); });
@ -403,10 +405,15 @@ const Api = {
return axios.post(url); return axios.post(url);
}, },
releases(id) { releases(id, options = {}) {
const url = Api.buildUrl(this.releasesPath).replace(':id', encodeURIComponent(id)); const url = Api.buildUrl(this.releasesPath).replace(':id', encodeURIComponent(id));
return axios.get(url); return axios.get(url, {
params: {
per_page: DEFAULT_PER_PAGE,
...options,
},
});
}, },
release(projectPath, tagName) { release(projectPath, tagName) {

View file

@ -1,9 +1,9 @@
/* eslint-disable no-param-reassign, no-void, consistent-return */ /* eslint-disable no-param-reassign, consistent-return */
import AccessorUtilities from './lib/utils/accessor'; import AccessorUtilities from './lib/utils/accessor';
export default class Autosave { export default class Autosave {
constructor(field, key) { constructor(field, key, fallbackKey) {
this.field = field; this.field = field;
this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
@ -11,6 +11,7 @@ export default class Autosave {
key = key.join('/'); key = key.join('/');
} }
this.key = `autosave/${key}`; this.key = `autosave/${key}`;
this.fallbackKey = fallbackKey;
this.field.data('autosave', this); this.field.data('autosave', this);
this.restore(); this.restore();
this.field.on('input', () => this.save()); this.field.on('input', () => this.save());
@ -21,9 +22,12 @@ export default class Autosave {
if (!this.field.length) return; if (!this.field.length) return;
const text = window.localStorage.getItem(this.key); const text = window.localStorage.getItem(this.key);
const fallbackText = window.localStorage.getItem(this.fallbackKey);
if ((text != null ? text.length : void 0) > 0) { if (text) {
this.field.val(text); this.field.val(text);
} else if (fallbackText) {
this.field.val(fallbackText);
} }
this.field.trigger('input'); this.field.trigger('input');
@ -41,7 +45,10 @@ export default class Autosave {
const text = this.field.val(); const text = this.field.val();
if (this.isLocalStorageAvailable && (text != null ? text.length : void 0) > 0) { if (this.isLocalStorageAvailable && text) {
if (this.fallbackKey) {
window.localStorage.setItem(this.fallbackKey, text);
}
return window.localStorage.setItem(this.key, text); return window.localStorage.setItem(this.key, text);
} }
@ -51,6 +58,7 @@ export default class Autosave {
reset() { reset() {
if (!this.isLocalStorageAvailable) return; if (!this.isLocalStorageAvailable) return;
window.localStorage.removeItem(this.fallbackKey);
return window.localStorage.removeItem(this.key); return window.localStorage.removeItem(this.key);
} }

View file

@ -1,6 +1,6 @@
<script> <script>
import Icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui'; import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
export default { export default {
// name: 'Badge' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25 // name: 'Badge' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25
@ -14,6 +14,11 @@ export default {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
props: { props: {
name: {
type: String,
required: false,
default: '',
},
imageUrl: { imageUrl: {
type: String, type: String,
required: true, required: true,

View file

@ -1,10 +1,10 @@
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon, GlFormInput, GlFormGroup } from '@gitlab/ui';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import createEmptyBadge from '../empty_badge'; import createEmptyBadge from '../empty_badge';
import Badge from './badge.vue'; import Badge from './badge.vue';
@ -16,6 +16,8 @@ export default {
Badge, Badge,
LoadingButton, LoadingButton,
GlLoadingIcon, GlLoadingIcon,
GlFormInput,
GlFormGroup,
}, },
props: { props: {
isEditing: { isEditing: {
@ -64,6 +66,18 @@ export default {
renderedLinkUrl() { renderedLinkUrl() {
return this.renderedBadge ? this.renderedBadge.renderedLinkUrl : ''; return this.renderedBadge ? this.renderedBadge.renderedLinkUrl : '';
}, },
name: {
get() {
return this.badge ? this.badge.name : '';
},
set(name) {
const badge = this.badge || createEmptyBadge();
this.updateBadgeInForm({
...badge,
name,
});
},
},
imageUrl: { imageUrl: {
get() { get() {
return this.badge ? this.badge.imageUrl : ''; return this.badge ? this.badge.imageUrl : '';
@ -154,6 +168,10 @@ export default {
novalidate novalidate
@submit.prevent.stop="onSubmit" @submit.prevent.stop="onSubmit"
> >
<gl-form-group :label="s__('Badges|Name')" label-for="badge-name">
<gl-form-input id="badge-name" v-model="name" />
</gl-form-group>
<div class="form-group"> <div class="form-group">
<label for="badge-link-url" class="label-bold">{{ s__('Badges|Link') }}</label> <label for="badge-link-url" class="label-bold">{{ s__('Badges|Link') }}</label>
<p v-html="helpText"></p> <p v-html="helpText"></p>

View file

@ -1,8 +1,8 @@
<script> <script>
import { mapActions, mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { PROJECT_BADGE } from '../constants'; import { PROJECT_BADGE } from '../constants';
import Badge from './badge.vue'; import Badge from './badge.vue';
@ -43,13 +43,14 @@ export default {
<badge <badge
:image-url="badge.renderedImageUrl" :image-url="badge.renderedImageUrl"
:link-url="badge.renderedLinkUrl" :link-url="badge.renderedLinkUrl"
class="table-section section-40" class="table-section section-30"
/> />
<span class="table-section section-30 str-truncated">{{ badge.linkUrl }}</span> <div class="table-section section-30">
<div class="table-section section-15"> <label class="label-bold str-truncated mb-0">{{ badge.name }}</label>
<span class="badge badge-pill">{{ badgeKindText }}</span> <span class="badge badge-pill">{{ badgeKindText }}</span>
</div> </div>
<div class="table-section section-15 table-button-footer"> <span class="table-section section-30 str-truncated">{{ badge.linkUrl }}</span>
<div class="table-section section-10 table-button-footer">
<div v-if="canEditBadge" class="table-action-buttons"> <div v-if="canEditBadge" class="table-action-buttons">
<button <button
:disabled="badge.isDeleting" :disabled="badge.isDeleting"

View file

@ -1,4 +1,5 @@
export default () => ({ export default () => ({
name: '',
imageUrl: '', imageUrl: '',
isDeleting: false, isDeleting: false,
linkUrl: '', linkUrl: '',

View file

@ -1,13 +1,9 @@
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import types from './mutation_types'; import types from './mutation_types';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
export const transformBackendBadge = badge => ({ export const transformBackendBadge = badge => ({
id: badge.id, ...convertObjectPropsToCamelCase(badge, true),
imageUrl: badge.image_url,
kind: badge.kind,
linkUrl: badge.link_url,
renderedImageUrl: badge.rendered_image_url,
renderedLinkUrl: badge.rendered_link_url,
isDeleting: false, isDeleting: false,
}); });
@ -27,6 +23,7 @@ export default {
dispatch('requestNewBadge'); dispatch('requestNewBadge');
return axios return axios
.post(endpoint, { .post(endpoint, {
name: newBadge.name,
image_url: newBadge.imageUrl, image_url: newBadge.imageUrl,
link_url: newBadge.linkUrl, link_url: newBadge.linkUrl,
}) })
@ -141,6 +138,7 @@ export default {
dispatch('requestUpdatedBadge'); dispatch('requestUpdatedBadge');
return axios return axios
.put(endpoint, { .put(endpoint, {
name: badge.name,
image_url: badge.imageUrl, image_url: badge.imageUrl,
link_url: badge.linkUrl, link_url: badge.linkUrl,
}) })

View file

@ -1,6 +1,6 @@
import $ from 'jquery'; import $ from 'jquery';
import { parseBoolean } from '~/lib/utils/common_utils';
import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete'; import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import { parseBoolean } from '~/lib/utils/common_utils';
export default function initGFMInput() { export default function initGFMInput() {
$('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => { $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {

View file

@ -1,8 +1,8 @@
/* eslint-disable class-methods-use-this */ /* eslint-disable class-methods-use-this */
import { Image as BaseImage } from 'tiptap-extensions'; import { Image as BaseImage } from 'tiptap-extensions';
import { placeholderImage } from '~/lazy_loader';
import { defaultMarkdownSerializer } from 'prosemirror-markdown'; import { defaultMarkdownSerializer } from 'prosemirror-markdown';
import { placeholderImage } from '~/lazy_loader';
export default class Image extends BaseImage { export default class Image extends BaseImage {
get schema() { get schema() {

View file

@ -1,6 +1,6 @@
import $ from 'jquery';
import Api from '~/api'; import Api from '~/api';
import $ from 'jquery';
import Flash from '../flash'; import Flash from '../flash';
import FileTemplateTypeSelector from './template_selectors/type_selector'; import FileTemplateTypeSelector from './template_selectors/type_selector';
import BlobCiYamlSelector from './template_selectors/ci_yaml_selector'; import BlobCiYamlSelector from './template_selectors/ci_yaml_selector';

View file

@ -0,0 +1,19 @@
import { SwaggerUIBundle } from 'swagger-ui-dist';
import flash from '~/flash';
import { __ } from '~/locale';
export default () => {
const el = document.getElementById('js-openapi-viewer');
Promise.all([import(/* webpackChunkName: 'openapi' */ 'swagger-ui-dist/swagger-ui.css')])
.then(() => {
SwaggerUIBundle({
url: el.dataset.endpoint,
dom_id: '#js-openapi-viewer',
});
})
.catch(error => {
flash(__('Something went wrong while initializing the OpenAPI viewer'));
throw error;
});
};

View file

@ -0,0 +1,3 @@
import renderOpenApi from './openapi';
export default renderOpenApi;

View file

@ -39,6 +39,9 @@ export default class BlobViewer {
case 'notebook': case 'notebook':
initViewer(import(/* webpackChunkName: 'notebook_viewer' */ '../notebook_viewer')); initViewer(import(/* webpackChunkName: 'notebook_viewer' */ '../notebook_viewer'));
break; break;
case 'openapi':
initViewer(import(/* webpackChunkName: 'openapi_viewer' */ '../openapi_viewer'));
break;
case 'pdf': case 'pdf':
initViewer(import(/* webpackChunkName: 'pdf_viewer' */ '../pdf_viewer')); initViewer(import(/* webpackChunkName: 'pdf_viewer' */ '../pdf_viewer'));
break; break;

View file

@ -1,6 +1,8 @@
import $ from 'jquery'; import $ from 'jquery';
import Sortable from 'sortablejs'; import Sortable from 'sortablejs';
import Vue from 'vue'; import Vue from 'vue';
import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import { n__, s__ } from '~/locale'; import { n__, s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip'; import Tooltip from '~/vue_shared/directives/tooltip';
@ -8,8 +10,10 @@ import AccessorUtilities from '../../lib/utils/accessor';
import BoardBlankState from './board_blank_state.vue'; import BoardBlankState from './board_blank_state.vue';
import BoardDelete from './board_delete'; import BoardDelete from './board_delete';
import BoardList from './board_list.vue'; import BoardList from './board_list.vue';
import IssueCount from './issue_count.vue';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options'; import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
import { ListType } from '../constants';
export default Vue.extend({ export default Vue.extend({
components: { components: {
@ -17,10 +21,15 @@ export default Vue.extend({
BoardDelete, BoardDelete,
BoardList, BoardList,
Icon, Icon,
GlButtonGroup,
IssueCount,
GlButton,
GlTooltip,
}, },
directives: { directives: {
Tooltip, Tooltip,
}, },
mixins: [isWipLimitsOn],
props: { props: {
list: { list: {
type: Object, type: Object,
@ -53,6 +62,11 @@ export default Vue.extend({
isLoggedIn() { isLoggedIn() {
return Boolean(gon.current_user_id); return Boolean(gon.current_user_id);
}, },
showListHeaderButton() {
return (
!this.disabled && this.list.type !== ListType.closed && this.list.type !== ListType.blank
);
},
counterTooltip() { counterTooltip() {
const { issuesSize } = this.list; const { issuesSize } = this.list;
return `${n__('%d issue', '%d issues', issuesSize)}`; return `${n__('%d issue', '%d issues', issuesSize)}`;
@ -61,11 +75,19 @@ export default Vue.extend({
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand'); return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
}, },
isNewIssueShown() { isNewIssueShown() {
return this.list.type === ListType.backlog || this.showListHeaderButton;
},
isSettingsShown() {
return ( return (
this.list.type === 'backlog' || this.list.type !== ListType.backlog &&
(!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank') this.showListHeaderButton &&
this.list.isExpanded &&
this.isWipLimitsOn
); );
}, },
showBoardListAndBoardInfo() {
return this.list.type !== ListType.blank && this.list.type !== ListType.promotion;
},
uniqueKey() { uniqueKey() {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`; return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;

View file

@ -1,7 +1,7 @@
<script> <script>
import Cookies from 'js-cookie';
import { __ } from '~/locale'; import { __ } from '~/locale';
import ListLabel from '~/boards/models/label'; import ListLabel from '~/boards/models/label';
import Cookies from 'js-cookie';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
export default { export default {

View file

@ -71,6 +71,9 @@ export default {
total: this.list.issuesSize, total: this.list.issuesSize,
}); });
}, },
issuesSizeExceedsMax() {
return this.list.maxIssueCount > 0 && this.list.issuesSize > this.list.maxIssueCount;
},
}, },
watch: { watch: {
filters: { filters: {
@ -435,7 +438,7 @@ export default {
ref="list" ref="list"
:data-board="list.id" :data-board="list.id"
:data-board-type="list.type" :data-board-type="list.type"
:class="{ 'is-smaller': showIssueForm }" :class="{ 'is-smaller': showIssueForm, 'bg-danger-100': issuesSizeExceedsMax }"
class="board-list w-100 h-100 list-unstyled mb-0 p-1 js-board-list" class="board-list w-100 h-100 list-unstyled mb-0 p-1 js-board-list"
> >
<board-card <board-card

View file

@ -315,8 +315,7 @@ export default {
<gl-dropdown-item <gl-dropdown-item
v-if="showDelete" v-if="showDelete"
class="text-danger" class="text-danger js-delete-board"
data-qa-selector="delete_board_button"
@click.prevent="showPage('delete')" @click.prevent="showPage('delete')"
> >
{{ s__('IssueBoards|Delete board') }} {{ s__('IssueBoards|Delete board') }}

View file

@ -2,10 +2,10 @@
import _ from 'underscore'; import _ from 'underscore';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui'; import { GlTooltipDirective } from '@gitlab/ui';
import issueCardInner from 'ee_else_ce/boards/mixins/issue_card_inner';
import { sprintf, __ } from '~/locale'; import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import issueCardInner from 'ee_else_ce/boards/mixins/issue_card_inner';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import IssueDueDate from './issue_due_date.vue'; import IssueDueDate from './issue_due_date.vue';
import IssueTimeEstimate from './issue_time_estimate.vue'; import IssueTimeEstimate from './issue_time_estimate.vue';

View file

@ -0,0 +1,36 @@
<script>
export default {
name: 'IssueCount',
props: {
maxIssueCount: {
type: Number,
required: false,
default: 0,
},
issuesSize: {
type: Number,
required: false,
default: 0,
},
},
computed: {
isMaxLimitSet() {
return this.maxIssueCount !== 0;
},
issuesExceedMax() {
return this.isMaxLimitSet && this.issuesSize > this.maxIssueCount;
},
},
};
</script>
<template>
<div class="issue-count">
<span class="js-issue-size" :class="{ 'text-danger': issuesExceedMax }">
{{ issuesSize }}
</span>
<span v-if="isMaxLimitSet" class="js-max-issue-size">
{{ maxIssueCount }}
</span>
</div>
</template>

View file

@ -35,10 +35,10 @@ export default {
title() { title() {
const timeago = getTimeago(); const timeago = getTimeago();
const { timeDifference, standardDateFormat } = this; const { timeDifference, standardDateFormat } = this;
const formatedDate = standardDateFormat; const formattedDate = standardDateFormat;
if (timeDifference >= -1 && timeDifference < 7) { if (timeDifference >= -1 && timeDifference < 7) {
return `${timeago.format(this.issueDueDate)} (${formatedDate})`; return `${timeago.format(this.issueDueDate)} (${formattedDate})`;
} }
return timeago.format(this.issueDueDate); return timeago.format(this.issueDueDate);

View file

@ -1,5 +1,6 @@
<script> <script>
/* global ListIssue */ /* global ListIssue */
import { GlLoadingIcon } from '@gitlab/ui';
import { urlParamsToObject } from '~/lib/utils/common_utils'; import { urlParamsToObject } from '~/lib/utils/common_utils';
import boardsStore from '~/boards/stores/boards_store'; import boardsStore from '~/boards/stores/boards_store';
import ModalHeader from './header.vue'; import ModalHeader from './header.vue';
@ -7,7 +8,6 @@ import ModalList from './list.vue';
import ModalFooter from './footer.vue'; import ModalFooter from './footer.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import ModalStore from '../../stores/modal_store'; import ModalStore from '../../stores/modal_store';
import { GlLoadingIcon } from '@gitlab/ui';
export default { export default {
components: { components: {

View file

@ -1,9 +1,9 @@
<script> <script>
import { __ } from '~/locale';
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import Icon from '~/vue_shared/components/icon.vue';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import Api from '../../api'; import Api from '../../api';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants'; import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';

View file

@ -4,6 +4,8 @@ export const ListType = {
backlog: 'backlog', backlog: 'backlog',
closed: 'closed', closed: 'closed',
label: 'label', label: 'label',
promotion: 'promotion',
blank: 'blank',
}; };
export default { export default {

View file

@ -1,33 +1,11 @@
import $ from 'jquery'; import $ from 'jquery';
import Vue from 'vue'; import Vue from 'vue';
import Flash from '~/flash';
import { __ } from '~/locale';
import './models/label';
import './models/assignee';
import FilteredSearchBoards from '~/boards/filtered_search_boards';
import eventHub from '~/boards/eventhub';
import sidebarEventHub from '~/sidebar/event_hub';
import 'ee_else_ce/boards/models/issue'; import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list'; import 'ee_else_ce/boards/models/list';
import '~/boards/models/milestone';
import '~/boards/models/project';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store';
import ModalStore from '~/boards/stores/modal_store';
import BoardService from 'ee_else_ce/boards/services/board_service';
import modalMixin from '~/boards/mixins/modal_mixins';
import '~/boards/filters/due_date_filters';
import Board from 'ee_else_ce/boards/components/board'; import Board from 'ee_else_ce/boards/components/board';
import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar'; import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar';
import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown'; import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
import BoardAddIssuesModal from '~/boards/components/modal/index.vue';
import {
NavigationType,
convertObjectPropsToCamelCase,
parseBoolean,
} from '~/lib/utils/common_utils';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle'; import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
import toggleFocusMode from 'ee_else_ce/boards/toggle_focus'; import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
import toggleLabels from 'ee_else_ce/boards/toggle_labels'; import toggleLabels from 'ee_else_ce/boards/toggle_labels';
@ -38,6 +16,28 @@ import {
getMilestoneTitle, getMilestoneTitle,
getBoardsModalData, getBoardsModalData,
} from 'ee_else_ce/boards/ee_functions'; } from 'ee_else_ce/boards/ee_functions';
import Flash from '~/flash';
import { __ } from '~/locale';
import './models/label';
import './models/assignee';
import FilteredSearchBoards from '~/boards/filtered_search_boards';
import eventHub from '~/boards/eventhub';
import sidebarEventHub from '~/sidebar/event_hub';
import '~/boards/models/milestone';
import '~/boards/models/project';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store';
import ModalStore from '~/boards/stores/modal_store';
import modalMixin from '~/boards/mixins/modal_mixins';
import '~/boards/filters/due_date_filters';
import BoardAddIssuesModal from '~/boards/components/modal/index.vue';
import {
NavigationType,
convertObjectPropsToCamelCase,
parseBoolean,
} from '~/lib/utils/common_utils';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher'; import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
let issueBoardsApp; let issueBoardsApp;
@ -68,6 +68,8 @@ export default () => {
Board, Board,
BoardSidebar, BoardSidebar,
BoardAddIssuesModal, BoardAddIssuesModal,
BoardSettingsSidebar: () =>
import('ee_component/boards/components/board_settings_sidebar.vue'),
}, },
store, store,
data: { data: {
@ -97,7 +99,6 @@ export default () => {
bulkUpdatePath: this.bulkUpdatePath, bulkUpdatePath: this.bulkUpdatePath,
boardId: this.boardId, boardId: this.boardId,
}); });
gl.boardService = new BoardService();
boardsStore.rootPath = this.boardsEndpoint; boardsStore.rootPath = this.boardsEndpoint;
eventHub.$on('updateTokens', this.updateTokens); eventHub.$on('updateTokens', this.updateTokens);
@ -116,7 +117,7 @@ export default () => {
this.filterManager.setup(); this.filterManager.setup();
boardsStore.disabled = this.disabled; boardsStore.disabled = this.disabled;
gl.boardService boardsStore
.all() .all()
.then(res => res.data) .then(res => res.data)
.then(lists => { .then(lists => {
@ -155,7 +156,8 @@ export default () => {
newIssue.setFetchingState('subscriptions', true); newIssue.setFetchingState('subscriptions', true);
setWeigthFetchingState(newIssue, true); setWeigthFetchingState(newIssue, true);
setEpicFetchingState(newIssue, true); setEpicFetchingState(newIssue, true);
BoardService.getIssueInfo(sidebarInfoEndpoint) boardsStore
.getIssueInfo(sidebarInfoEndpoint)
.then(res => res.data) .then(res => res.data)
.then(data => { .then(data => {
const { const {
@ -166,6 +168,7 @@ export default () => {
humanTotalTimeSpent, humanTotalTimeSpent,
weight, weight,
epic, epic,
assignees,
} = convertObjectPropsToCamelCase(data); } = convertObjectPropsToCamelCase(data);
newIssue.setFetchingState('subscriptions', false); newIssue.setFetchingState('subscriptions', false);
@ -179,6 +182,7 @@ export default () => {
subscribed, subscribed,
weight, weight,
epic, epic,
assignees,
}); });
}) })
.catch(() => { .catch(() => {
@ -211,7 +215,8 @@ export default () => {
const { issue } = boardsStore.detail; const { issue } = boardsStore.detail;
if (issue.id === id && issue.toggleSubscriptionEndpoint) { if (issue.id === id && issue.toggleSubscriptionEndpoint) {
issue.setFetchingState('subscriptions', true); issue.setFetchingState('subscriptions', true);
BoardService.toggleIssueSubscription(issue.toggleSubscriptionEndpoint) boardsStore
.toggleIssueSubscription(issue.toggleSubscriptionEndpoint)
.then(() => { .then(() => {
issue.setFetchingState('subscriptions', false); issue.setFetchingState('subscriptions', false);
issue.updateData({ issue.updateData({

View file

@ -0,0 +1,7 @@
export default {
computed: {
isWipLimitsOn() {
return false;
},
},
};

View file

@ -1,9 +1,9 @@
/* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow */ /* eslint-disable no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow */
import ListIssue from 'ee_else_ce/boards/models/issue';
import { __ } from '~/locale'; import { __ } from '~/locale';
import ListLabel from './label'; import ListLabel from './label';
import ListAssignee from './assignee'; import ListAssignee from './assignee';
import ListIssue from 'ee_else_ce/boards/models/issue';
import { urlParamsToObject } from '~/lib/utils/common_utils'; import { urlParamsToObject } from '~/lib/utils/common_utils';
import flash from '~/flash'; import flash from '~/flash';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
@ -52,6 +52,9 @@ class List {
this.loadingMore = false; this.loadingMore = false;
this.issues = obj.issues || []; this.issues = obj.issues || [];
this.issuesSize = obj.issuesSize ? obj.issuesSize : 0; this.issuesSize = obj.issuesSize ? obj.issuesSize : 0;
this.maxIssueCount = Object.hasOwnProperty.call(obj, 'max_issue_count')
? obj.max_issue_count
: 0;
this.defaultAvatar = defaultAvatar; this.defaultAvatar = defaultAvatar;
if (obj.label) { if (obj.label) {
@ -90,7 +93,7 @@ class List {
entityType = 'milestone_id'; entityType = 'milestone_id';
} }
return gl.boardService return boardsStore
.createList(entity.id, entityType) .createList(entity.id, entityType)
.then(res => res.data) .then(res => res.data)
.then(data => { .then(data => {
@ -108,14 +111,14 @@ class List {
boardsStore.state.lists.splice(index, 1); boardsStore.state.lists.splice(index, 1);
boardsStore.updateNewListDropdown(this.id); boardsStore.updateNewListDropdown(this.id);
gl.boardService.destroyList(this.id).catch(() => { boardsStore.destroyList(this.id).catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
} }
update() { update() {
const collapsed = !this.isExpanded; const collapsed = !this.isExpanded;
return gl.boardService.updateList(this.id, this.position, collapsed).catch(() => { return boardsStore.updateList(this.id, this.position, collapsed).catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
} }
@ -144,7 +147,7 @@ class List {
this.loading = true; this.loading = true;
} }
return gl.boardService return boardsStore
.getIssuesForList(this.id, data) .getIssuesForList(this.id, data)
.then(res => res.data) .then(res => res.data)
.then(data => { .then(data => {
@ -165,7 +168,7 @@ class List {
this.addIssue(issue, null, 0); this.addIssue(issue, null, 0);
this.issuesSize += 1; this.issuesSize += 1;
return gl.boardService return boardsStore
.newIssue(this.id, issue) .newIssue(this.id, issue)
.then(res => res.data) .then(res => res.data)
.then(data => this.onNewIssueResponse(issue, data)); .then(data => this.onNewIssueResponse(issue, data));
@ -273,7 +276,7 @@ class List {
this.issues.splice(oldIndex, 1); this.issues.splice(oldIndex, 1);
this.issues.splice(newIndex, 0, issue); this.issues.splice(newIndex, 0, issue);
gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId).catch(() => { boardsStore.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId).catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
} }
@ -284,7 +287,7 @@ class List {
}); });
this.issues.splice(newIndex, 0, ...issues); this.issues.splice(newIndex, 0, ...issues);
gl.boardService boardsStore
.moveMultipleIssues({ .moveMultipleIssues({
ids: issues.map(issue => issue.id), ids: issues.map(issue => issue.id),
fromListId: null, fromListId: null,
@ -296,15 +299,13 @@ class List {
} }
updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) { updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) {
gl.boardService boardsStore.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId).catch(() => {
.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
.catch(() => {
// TODO: handle request error // TODO: handle request error
}); });
} }
updateMultipleIssues(issues, listFrom, moveBeforeId, moveAfterId) { updateMultipleIssues(issues, listFrom, moveBeforeId, moveAfterId) {
gl.boardService boardsStore
.moveMultipleIssues({ .moveMultipleIssues({
ids: issues.map(issue => issue.id), ids: issues.map(issue => issue.id),
fromListId: listFrom.id, fromListId: listFrom.id,
@ -356,7 +357,7 @@ class List {
if (this.issuesSize > 1) { if (this.issuesSize > 1) {
const moveBeforeId = this.issues[1].id; const moveBeforeId = this.issues[1].id;
gl.boardService.moveIssue(issue.id, null, null, null, moveBeforeId); boardsStore.moveIssue(issue.id, null, null, null, moveBeforeId);
} }
} }
} }

View file

@ -1,98 +0,0 @@
/* eslint-disable class-methods-use-this */
/**
* This file is intended to be deleted.
* The existing functions will removed one by one in favor of using the board store directly.
* see https://gitlab.com/gitlab-org/gitlab-foss/issues/61621
*/
import boardsStore from '~/boards/stores/boards_store';
export default class BoardService {
generateBoardsPath(id) {
return boardsStore.generateBoardsPath(id);
}
generateIssuesPath(id) {
return boardsStore.generateIssuesPath(id);
}
static generateIssuePath(boardId, id) {
return boardsStore.generateIssuePath(boardId, id);
}
all() {
return boardsStore.all();
}
generateDefaultLists() {
return boardsStore.generateDefaultLists();
}
createList(entityId, entityType) {
return boardsStore.createList(entityId, entityType);
}
updateList(id, position, collapsed) {
return boardsStore.updateList(id, position, collapsed);
}
destroyList(id) {
return boardsStore.destroyList(id);
}
getIssuesForList(id, filter = {}) {
return boardsStore.getIssuesForList(id, filter);
}
moveIssue(id, fromListId = null, toListId = null, moveBeforeId = null, moveAfterId = null) {
return boardsStore.moveIssue(id, fromListId, toListId, moveBeforeId, moveAfterId);
}
moveMultipleIssues({
ids,
fromListId = null,
toListId = null,
moveBeforeId = null,
moveAfterId = null,
}) {
return boardsStore.moveMultipleIssues({ ids, fromListId, toListId, moveBeforeId, moveAfterId });
}
newIssue(id, issue) {
return boardsStore.newIssue(id, issue);
}
getBacklog(data) {
return boardsStore.getBacklog(data);
}
bulkUpdate(issueIds, extraData = {}) {
return boardsStore.bulkUpdate(issueIds, extraData);
}
static getIssueInfo(endpoint) {
return boardsStore.getIssueInfo(endpoint);
}
static toggleIssueSubscription(endpoint) {
return boardsStore.toggleIssueSubscription(endpoint);
}
allBoards() {
return boardsStore.allBoards();
}
recentBoards() {
return boardsStore.recentBoards();
}
createBoard(board) {
return boardsStore.createBoard(board);
}
deleteBoard({ id }) {
return boardsStore.deleteBoard({ id });
}
}
window.BoardService = BoardService;

View file

@ -1,3 +1,4 @@
export default () => ({ export default () => ({
isShowingLabels: true, isShowingLabels: true,
activeListId: 0,
}); });

View file

@ -1,7 +1,7 @@
import Visibility from 'visibilityjs'; import Visibility from 'visibilityjs';
import Vue from 'vue'; import Vue from 'vue';
import AccessorUtilities from '~/lib/utils/accessor';
import { GlToast } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import AccessorUtilities from '~/lib/utils/accessor';
import PersistentUserCallout from '../persistent_user_callout'; import PersistentUserCallout from '../persistent_user_callout';
import { s__, sprintf } from '../locale'; import { s__, sprintf } from '../locale';
import Flash from '../flash'; import Flash from '../flash';
@ -12,6 +12,7 @@ import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX, CROSSPLANE } from '
import ClustersService from './services/clusters_service'; import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store'; import ClustersStore from './stores/clusters_store';
import Applications from './components/applications.vue'; import Applications from './components/applications.vue';
import RemoveClusterConfirmation from './components/remove_cluster_confirmation.vue';
import setupToggleButtons from '../toggle_buttons'; import setupToggleButtons from '../toggle_buttons';
import initProjectSelectDropdown from '~/project_select'; import initProjectSelectDropdown from '~/project_select';
@ -144,6 +145,8 @@ export default class Clusters {
() => this.handlePollError(), () => this.handlePollError(),
); );
} }
this.initRemoveClusterActions();
} }
initApplications(type) { initApplications(type) {
@ -205,6 +208,25 @@ export default class Clusters {
}); });
} }
initRemoveClusterActions() {
const el = document.querySelector('#js-cluster-remove-actions');
if (el && el.dataset) {
const { clusterName, clusterPath } = el.dataset;
this.removeClusterAction = new Vue({
el,
render(createElement) {
return createElement(RemoveClusterConfirmation, {
props: {
clusterName,
clusterPath,
},
});
},
});
}
}
handleClusterEnvironmentsSuccess(data) { handleClusterEnvironmentsSuccess(data) {
this.store.toggleFetchEnvironments(false); this.store.toggleFetchEnvironments(false);
this.store.updateEnvironments(data.data); this.store.updateEnvironments(data.data);

View file

@ -1,6 +1,5 @@
<script> <script>
import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { s__ } from '../../locale'; import { s__ } from '../../locale';
export default { export default {
@ -8,7 +7,7 @@ export default {
components: { components: {
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
Icon, GlIcon,
}, },
props: { props: {
stacks: { stacks: {
@ -86,8 +85,9 @@ export default {
href="https://crossplane.io/docs/master/stacks-guide.html" href="https://crossplane.io/docs/master/stacks-guide.html"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
>{{ __('Crossplane') }}</a >{{ __('Crossplane') }}
> <gl-icon name="external-link" class="vertical-align-middle" />
</a>
</p> </p>
</div> </div>
</template> </template>

View file

@ -1,7 +1,7 @@
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue'; import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { APPLICATION_STATUS } from '~/clusters/constants'; import { APPLICATION_STATUS } from '~/clusters/constants';

View file

@ -0,0 +1,168 @@
<script>
import _ from 'underscore';
import SplitButton from '~/vue_shared/components/split_button.vue';
import { GlModal, GlButton, GlFormInput } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import csrf from '~/lib/utils/csrf';
const splitButtonActionItems = [
{
title: s__('ClusterIntegration|Remove integration and resources'),
description: s__(
'ClusterIntegration|Deletes all GitLab resources attached to this cluster during removal',
),
eventName: 'remove-cluster-and-cleanup',
},
{
title: s__('ClusterIntegration|Remove integration'),
description: s__(
'ClusterIntegration|Removes cluster from project but keeps associated resources',
),
eventName: 'remove-cluster',
},
];
export default {
splitButtonActionItems,
components: {
SplitButton,
GlModal,
GlButton,
GlFormInput,
},
props: {
clusterPath: {
type: String,
required: true,
},
clusterName: {
type: String,
required: true,
},
},
data() {
return {
enteredClusterName: '',
confirmCleanup: false,
};
},
computed: {
csrfToken() {
return csrf.token;
},
modalTitle() {
return this.confirmCleanup
? s__('ClusterIntegration|Remove integration and resources?')
: s__('ClusterIntegration|Remove integration?');
},
warningMessage() {
return this.confirmCleanup
? s__(
'ClusterIntegration|You are about to remove your cluster integration and all GitLab-created resources associated with this cluster.',
)
: s__('ClusterIntegration|You are about to remove your cluster integration.');
},
warningToBeRemoved() {
return s__(`ClusterIntegration|
This will permanently delete the following resources:
<ul>
<li>All installed applications and related resources</li>
<li>The <code>gitlab-managed-apps</code> namespace</li>
<li>Any project namespaces</li>
<li><code>clusterroles</code></li>
<li><code>clusterrolebindings</code></li>
</ul>
`);
},
confirmationTextLabel() {
return sprintf(
this.confirmCleanup
? s__(
'ClusterIntegration|To remove your integration and resources, type %{clusterName} to confirm:',
)
: s__('ClusterIntegration|To remove your integration, type %{clusterName} to confirm:'),
{
clusterName: `<code>${_.escape(this.clusterName)}</code>`,
},
false,
);
},
canSubmit() {
return this.enteredClusterName === this.clusterName;
},
},
methods: {
handleClickRemoveCluster(cleanup = false) {
this.confirmCleanup = cleanup;
this.$refs.modal.show();
},
handleCancel() {
this.$refs.modal.hide();
this.enteredClusterName = '';
},
handleSubmit(cleanup = false) {
this.$refs.cleanup.name = cleanup === true ? 'cleanup' : 'no_cleanup';
this.$refs.form.submit();
this.enteredClusterName = '';
},
},
};
</script>
<template>
<div>
<split-button
:action-items="$options.splitButtonActionItems"
menu-class="dropdown-menu-large"
variant="danger"
@remove-cluster="handleClickRemoveCluster(false)"
@remove-cluster-and-cleanup="handleClickRemoveCluster(true)"
/>
<gl-modal
ref="modal"
size="lg"
modal-id="delete-cluster-modal"
:title="modalTitle"
kind="danger"
>
<template>
<p>{{ warningMessage }}</p>
<div v-if="confirmCleanup" v-html="warningToBeRemoved"></div>
<strong v-html="confirmationTextLabel"></strong>
<form ref="form" :action="clusterPath" method="post" class="append-bottom-20">
<input ref="method" type="hidden" name="_method" value="delete" />
<input :value="csrfToken" type="hidden" name="authenticity_token" />
<input ref="cleanup" type="hidden" name="cleanup" value="true" />
<gl-form-input
v-model="enteredClusterName"
autofocus
type="text"
name="confirm_cluster_name_input"
autocomplete="off"
/>
</form>
<span v-if="confirmCleanup">{{
s__(
'ClusterIntegration|If you do not wish to delete all associated GitLab resources, you can simply remove the integration.',
)
}}</span>
</template>
<template slot="modal-footer">
<gl-button variant="secondary" @click="handleCancel">{{ s__('Cancel') }}</gl-button>
<template v-if="confirmCleanup">
<gl-button :disabled="!canSubmit" variant="warning" @click="handleSubmit">{{
s__('ClusterIntegration|Remove integration')
}}</gl-button>
<gl-button :disabled="!canSubmit" variant="danger" @click="handleSubmit(true)">{{
s__('ClusterIntegration|Remove integration and resources')
}}</gl-button>
</template>
<template v-else>
<gl-button :disabled="!canSubmit" variant="danger" @click="handleSubmit">{{
s__('ClusterIntegration|Remove integration')
}}</gl-button>
</template>
</template>
</gl-modal>
</div>
</template>

View file

@ -1,7 +1,7 @@
<script> <script>
import { GlModal } from '@gitlab/ui'; import { GlModal } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import trackUninstallButtonClickMixin from 'ee_else_ce/clusters/mixins/track_uninstall_button_click'; import trackUninstallButtonClickMixin from 'ee_else_ce/clusters/mixins/track_uninstall_button_click';
import { sprintf, s__ } from '~/locale';
import { import {
HELM, HELM,
INGRESS, INGRESS,

View file

@ -1,4 +1,4 @@
/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign */ /* eslint-disable func-names, no-else-return, consistent-return, one-var, no-return-assign */
import $ from 'jquery'; import $ from 'jquery';
@ -51,7 +51,7 @@ export default class ImageFile {
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
initDraggable($el, padding, callback) { initDraggable($el, padding, callback) {
var dragging = false; let dragging = false;
const $body = $('body'); const $body = $('body');
const $offsetEl = $el.parent(); const $offsetEl = $el.parent();
const dragStart = function() { const dragStart = function() {
@ -88,14 +88,12 @@ export default class ImageFile {
} }
static prepareFrames(view) { static prepareFrames(view) {
var maxHeight, maxWidth; let maxWidth = 0;
maxWidth = 0; let maxHeight = 0;
maxHeight = 0;
$('.frame', view) $('.frame', view)
.each((index, frame) => { .each((index, frame) => {
var height, width; const width = $(frame).width();
width = $(frame).width(); const height = $(frame).height();
height = $(frame).height();
maxWidth = width > maxWidth ? width : maxWidth; maxWidth = width > maxWidth ? width : maxWidth;
return (maxHeight = height > maxHeight ? height : maxHeight); return (maxHeight = height > maxHeight ? height : maxHeight);
}) })
@ -110,8 +108,7 @@ export default class ImageFile {
'two-up': function() { 'two-up': function() {
return $('.two-up.view .wrap', this.file).each((index, wrap) => { return $('.two-up.view .wrap', this.file).each((index, wrap) => {
$('img', wrap).each(function() { $('img', wrap).each(function() {
var currentWidth; const currentWidth = $(this).width();
currentWidth = $(this).width();
if (currentWidth > availWidth / 2) { if (currentWidth > availWidth / 2) {
return $(this).width(availWidth / 2); return $(this).width(availWidth / 2);
} }
@ -124,16 +121,14 @@ export default class ImageFile {
}); });
}, },
swipe() { swipe() {
var maxHeight, maxWidth; let maxWidth = 0;
maxWidth = 0; let maxHeight = 0;
maxHeight = 0;
return $('.swipe.view', this.file).each((index, view) => { return $('.swipe.view', this.file).each((index, view) => {
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding;
const ref = ImageFile.prepareFrames(view); const ref = ImageFile.prepareFrames(view);
[maxWidth, maxHeight] = ref; [maxWidth, maxHeight] = ref;
$swipeFrame = $('.swipe-frame', view); const $swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view); const $swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view); const $swipeBar = $('.swipe-bar', view);
$swipeFrame.css({ $swipeFrame.css({
width: maxWidth + 16, width: maxWidth + 16,
@ -148,7 +143,7 @@ export default class ImageFile {
left: 1, left: 1,
}); });
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); const wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
this.initDraggable($swipeBar, wrapPadding, (e, left) => { this.initDraggable($swipeBar, wrapPadding, (e, left) => {
if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) { if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
@ -159,19 +154,17 @@ export default class ImageFile {
}); });
}, },
'onion-skin': function() { 'onion-skin': function() {
var dragTrackWidth, maxHeight, maxWidth; let maxHeight, maxWidth;
maxWidth = 0; maxWidth = 0;
maxHeight = 0; maxHeight = 0;
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); const dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
return $('.onion-skin.view', this.file).each((index, view) => { return $('.onion-skin.view', this.file).each((index, view) => {
var $frame, $track, $dragger, $frameAdded, framePadding;
const ref = ImageFile.prepareFrames(view); const ref = ImageFile.prepareFrames(view);
[maxWidth, maxHeight] = ref; [maxWidth, maxHeight] = ref;
$frame = $('.onion-skin-frame', view); const $frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view); const $frameAdded = $('.frame.added', view);
$track = $('.drag-track', view); const $track = $('.drag-track', view);
$dragger = $('.dragger', $track); const $dragger = $('.dragger', $track);
$frame.css({ $frame.css({
width: maxWidth + 16, width: maxWidth + 16,
@ -186,10 +179,10 @@ export default class ImageFile {
}); });
$frameAdded.css('opacity', 1); $frameAdded.css('opacity', 1);
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); const framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
this.initDraggable($dragger, framePadding, (e, left) => { this.initDraggable($dragger, framePadding, (e, left) => {
var opacity = left / dragTrackWidth; const opacity = left / dragTrackWidth;
if (opacity >= 0 && opacity <= 1) { if (opacity >= 0 && opacity <= 1) {
$dragger.css('left', left); $dragger.css('left', left);

View file

@ -4,6 +4,7 @@ import 'core-js/es/array/find';
import 'core-js/es/array/find-index'; import 'core-js/es/array/find-index';
import 'core-js/es/array/from'; import 'core-js/es/array/from';
import 'core-js/es/array/includes'; import 'core-js/es/array/includes';
import 'core-js/es/number/is-integer';
import 'core-js/es/object/assign'; import 'core-js/es/object/assign';
import 'core-js/es/object/values'; import 'core-js/es/object/values';
import 'core-js/es/object/entries'; import 'core-js/es/object/entries';

View file

@ -1,6 +1,6 @@
<script> <script>
import { GlLink } from '@gitlab/ui'; import { GlLink, GlSprintf } from '@gitlab/ui';
import { __, sprintf } from '../../locale'; import { __ } from '../../locale';
import createFlash from '../../flash'; import createFlash from '../../flash';
import Api from '../../api'; import Api from '../../api';
import state from '../state'; import state from '../state';
@ -9,6 +9,7 @@ import Dropdown from './dropdown.vue';
export default { export default {
components: { components: {
GlLink, GlLink,
GlSprintf,
Dropdown, Dropdown,
}, },
props: { props: {
@ -38,15 +39,6 @@ export default {
selectedProject() { selectedProject() {
return state.selectedProject; return state.selectedProject;
}, },
noForkText() {
return sprintf(
__(
"To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visibility to private.",
),
{ link_start: `<a href="${this.newForkPath}" class="help-link">`, link_end: '</a>' },
false,
);
},
}, },
mounted() { mounted() {
this.fetchProjects(); this.fetchProjects();
@ -123,8 +115,20 @@ export default {
}} }}
</template> </template>
<template v-else> <template v-else>
{{ __('No forks available to you.') }}<br /> {{ __('No forks are available to you.') }}<br />
<span v-html="noForkText"></span> <gl-sprintf
:message="
__(
`To protect this issue's confidentiality, %{forkLink} and set the fork's visibility to private.`,
)
"
>
<template #forkLink>
<a :href="newForkPath" target="_blank" class="help-link">{{
__('fork this project')
}}</a>
</template>
</gl-sprintf>
</template> </template>
<gl-link <gl-link
:href="helpPagePath" :href="helpPagePath"

View file

@ -1,21 +1,19 @@
import $ from 'jquery'; import $ from 'jquery';
import { rstrip } from './lib/utils/common_utils'; import { rstrip } from './lib/utils/common_utils';
function openConfirmDangerModal($form, text) { function openConfirmDangerModal($form, $modal, text) {
const $input = $('.js-confirm-danger-input'); const $input = $('.js-confirm-danger-input', $modal);
$input.val(''); $input.val('');
$('.js-confirm-text').text(text || ''); $('.js-confirm-text', $modal).text(text || '');
$('#modal-confirm-danger').modal('show'); $modal.modal('show');
const confirmTextMatch = $('.js-confirm-danger-match').text(); const confirmTextMatch = $('.js-confirm-danger-match', $modal).text();
const $submit = $('.js-confirm-danger-submit'); const $submit = $('.js-confirm-danger-submit', $modal);
$submit.disable(); $submit.disable();
$input.focus(); $input.focus();
$('.js-confirm-danger-input') $input.off('input').on('input', function handleInput() {
.off('input')
.on('input', function handleInput() {
const confirmText = rstrip($(this).val()); const confirmText = rstrip($(this).val());
if (confirmText === confirmTextMatch) { if (confirmText === confirmTextMatch) {
$submit.enable(); $submit.enable();
@ -23,17 +21,34 @@ function openConfirmDangerModal($form, text) {
$submit.disable(); $submit.disable();
} }
}); });
$('.js-confirm-danger-submit') $('.js-confirm-danger-submit', $modal)
.off('click') .off('click')
.on('click', () => $form.submit()); .on('click', () => $form.submit());
} }
function getModal($btn) {
const $modal = $btn.prev('.modal');
if ($modal.length) {
return $modal;
}
return $('#modal-confirm-danger');
}
export default function initConfirmDangerModal() { export default function initConfirmDangerModal() {
$(document).on('click', '.js-confirm-danger', e => { $(document).on('click', '.js-confirm-danger', e => {
e.preventDefault();
const $btn = $(e.target); const $btn = $(e.target);
const checkFieldName = $btn.data('checkFieldName');
const checkFieldCompareValue = $btn.data('checkCompareValue');
const checkFieldVal = parseInt($(`[name="${checkFieldName}"]`).val(), 10);
if (!checkFieldName || checkFieldVal < checkFieldCompareValue) {
e.preventDefault();
const $form = $btn.closest('form'); const $form = $btn.closest('form');
const $modal = getModal($btn);
const text = $btn.data('confirmDangerMessage'); const text = $btn.data('confirmDangerMessage');
openConfirmDangerModal($form, text); openConfirmDangerModal($form, $modal, text);
}
}); });
} }

View file

@ -1,9 +1,9 @@
<script> <script>
import { __ } from '~/locale';
import _ from 'underscore'; import _ from 'underscore';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import { GlAreaChart } from '@gitlab/ui/dist/charts'; import { GlAreaChart } from '@gitlab/ui/dist/charts';
import { __ } from '~/locale';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { getDatesInRange } from '~/lib/utils/datetime_utility'; import { getDatesInRange } from '~/lib/utils/datetime_utility';
import { xAxisLabelFormatter, dateFormatter } from '../utils'; import { xAxisLabelFormatter, dateFormatter } from '../utils';

View file

@ -1,8 +1,9 @@
<script> <script>
import $ from 'jquery';
import { GlIcon } from '@gitlab/ui';
import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue'; import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue'; import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue'; import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
import { GlIcon } from '@gitlab/ui';
const toArray = value => [].concat(value); const toArray = value => [].concat(value);
const itemsProp = (items, prop) => items.map(item => item[prop]); const itemsProp = (items, prop) => items.map(item => item[prop]);
@ -106,6 +107,7 @@ export default {
data() { data() {
return { return {
searchQuery: '', searchQuery: '',
focusOnSearch: false,
}; };
}, },
computed: { computed: {
@ -141,6 +143,18 @@ export default {
return itemsProp(this.selectedItems, this.valueProperty).join(', '); return itemsProp(this.selectedItems, this.valueProperty).join(', ');
}, },
}, },
mounted() {
$(this.$refs.dropdown)
.on('shown.bs.dropdown', () => {
this.focusOnSearch = true;
})
.on('hidden.bs.dropdown', () => {
this.focusOnSearch = false;
});
},
beforeDestroy() {
$(this.$refs.dropdown).off();
},
methods: { methods: {
getItemsOrEmptyList() { getItemsOrEmptyList() {
return this.items || []; return this.items || [];
@ -170,7 +184,7 @@ export default {
<template> <template>
<div> <div>
<div class="js-gcp-machine-type-dropdown dropdown"> <div ref="dropdown" class="dropdown">
<dropdown-hidden-input :name="fieldName" :value="selectedItemsValues" /> <dropdown-hidden-input :name="fieldName" :value="selectedItemsValues" />
<dropdown-button <dropdown-button
:class="{ 'border-danger': hasErrors }" :class="{ 'border-danger': hasErrors }"
@ -179,7 +193,11 @@ export default {
:toggle-text="toggleText" :toggle-text="toggleText"
/> />
<div class="dropdown-menu dropdown-select"> <div class="dropdown-menu dropdown-select">
<dropdown-search-input v-model="searchQuery" :placeholder-text="searchFieldPlaceholder" /> <dropdown-search-input
v-model="searchQuery"
:focused="focusOnSearch"
:placeholder-text="searchFieldPlaceholder"
/>
<div class="dropdown-content"> <div class="dropdown-content">
<ul> <ul>
<li v-if="!results.length"> <li v-if="!results.length">

View file

@ -1,8 +1,8 @@
<script> <script>
import { createNamespacedHelpers, mapState, mapActions } from 'vuex'; import { createNamespacedHelpers, mapState, mapActions } from 'vuex';
import { sprintf, s__ } from '~/locale';
import _ from 'underscore'; import _ from 'underscore';
import { GlFormInput, GlFormCheckbox } from '@gitlab/ui'; import { GlFormInput, GlFormCheckbox } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import ClusterFormDropdown from './cluster_form_dropdown.vue'; import ClusterFormDropdown from './cluster_form_dropdown.vue';
import { KUBERNETES_VERSIONS } from '../constants'; import { KUBERNETES_VERSIONS } from '../constants';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
@ -22,10 +22,7 @@ const {
mapState: mapSecurityGroupsState, mapState: mapSecurityGroupsState,
mapActions: mapSecurityGroupsActions, mapActions: mapSecurityGroupsActions,
} = createNamespacedHelpers('securityGroups'); } = createNamespacedHelpers('securityGroups');
const { const { mapState: mapInstanceTypesState } = createNamespacedHelpers('instanceTypes');
mapState: mapInstanceTypesState,
mapActions: mapInstanceTypesActions,
} = createNamespacedHelpers('instanceTypes');
export default { export default {
components: { components: {
@ -265,12 +262,10 @@ export default {
mounted() { mounted() {
this.fetchRegions(); this.fetchRegions();
this.fetchRoles(); this.fetchRoles();
this.fetchInstanceTypes();
}, },
methods: { methods: {
...mapActions([ ...mapActions([
'createCluster', 'createCluster',
'signOut',
'setClusterName', 'setClusterName',
'setEnvironmentScope', 'setEnvironmentScope',
'setKubernetesVersion', 'setKubernetesVersion',
@ -290,7 +285,6 @@ export default {
...mapRolesActions({ fetchRoles: 'fetchItems' }), ...mapRolesActions({ fetchRoles: 'fetchItems' }),
...mapKeyPairsActions({ fetchKeyPairs: 'fetchItems' }), ...mapKeyPairsActions({ fetchKeyPairs: 'fetchItems' }),
...mapSecurityGroupsActions({ fetchSecurityGroups: 'fetchItems' }), ...mapSecurityGroupsActions({ fetchSecurityGroups: 'fetchItems' }),
...mapInstanceTypesActions({ fetchInstanceTypes: 'fetchItems' }),
setRegionAndFetchVpcsAndKeyPairs(region) { setRegionAndFetchVpcsAndKeyPairs(region) {
this.setRegion({ region }); this.setRegion({ region });
this.setVpc({ vpc: null }); this.setVpc({ vpc: null });
@ -316,11 +310,6 @@ export default {
{{ s__('ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster') }} {{ s__('ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster') }}
</h2> </h2>
<div class="mb-3" v-html="kubernetesIntegrationHelpText"></div> <div class="mb-3" v-html="kubernetesIntegrationHelpText"></div>
<div class="mb-3">
<button class="btn btn-link js-sign-out" @click.prevent="signOut()">
{{ s__('ClusterIntegration|Select a different AWS role') }}
</button>
</div>
<div class="form-group"> <div class="form-group">
<label class="label-bold" for="eks-cluster-name">{{ <label class="label-bold" for="eks-cluster-name">{{
s__('ClusterIntegration|Kubernetes cluster name') s__('ClusterIntegration|Kubernetes cluster name')

View file

@ -1,8 +1,8 @@
<script> <script>
import { GlFormInput } from '@gitlab/ui'; import { GlFormInput } from '@gitlab/ui';
import { sprintf, s__, __ } from '~/locale';
import _ from 'underscore'; import _ from 'underscore';
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { sprintf, s__, __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
@ -28,7 +28,7 @@ export default {
}, },
data() { data() {
return { return {
roleArn: '', roleArn: this.$store.state.roleArn,
}; };
}, },
computed: { computed: {

View file

@ -12,20 +12,14 @@ export default el => {
kubernetesIntegrationHelpPath, kubernetesIntegrationHelpPath,
accountAndExternalIdsHelpPath, accountAndExternalIdsHelpPath,
createRoleArnHelpPath, createRoleArnHelpPath,
getRolesPath,
getRegionsPath,
getKeyPairsPath,
getVpcsPath,
getSubnetsPath,
getSecurityGroupsPath,
getInstanceTypesPath,
externalId, externalId,
accountId, accountId,
instanceTypes,
hasCredentials, hasCredentials,
createRolePath, createRolePath,
createClusterPath, createClusterPath,
signOutPath,
externalLinkIcon, externalLinkIcon,
roleArn,
} = el.dataset; } = el.dataset;
return new Vue({ return new Vue({
@ -35,18 +29,10 @@ export default el => {
hasCredentials: parseBoolean(hasCredentials), hasCredentials: parseBoolean(hasCredentials),
externalId, externalId,
accountId, accountId,
instanceTypes: JSON.parse(instanceTypes),
createRolePath, createRolePath,
createClusterPath, createClusterPath,
signOutPath, roleArn,
},
apiPaths: {
getRolesPath,
getRegionsPath,
getKeyPairsPath,
getVpcsPath,
getSubnetsPath,
getSecurityGroupsPath,
getInstanceTypesPath,
}, },
}), }),
components: { components: {

View file

@ -1,58 +1,98 @@
import axios from '~/lib/utils/axios_utils'; import AWS from 'aws-sdk/global';
import EC2 from 'aws-sdk/clients/ec2';
import IAM from 'aws-sdk/clients/iam';
export default apiPaths => ({ const lookupVpcName = ({ Tags: tags, VpcId: id }) => {
fetchRoles() { const nameTag = tags.find(({ Key: key }) => key === 'Name');
return axios
.get(apiPaths.getRolesPath) return nameTag ? nameTag.Value : id;
.then(({ data: { roles } }) => };
roles.map(({ role_name: name, arn: value }) => ({ name, value })),
); export const DEFAULT_REGION = 'us-east-2';
},
fetchKeyPairs({ region }) { export const setAWSConfig = ({ awsCredentials }) => {
return axios AWS.config = {
.get(apiPaths.getKeyPairsPath, { params: { region } }) ...awsCredentials,
.then(({ data: { key_pairs: keyPairs } }) => region: DEFAULT_REGION,
keyPairs.map(({ key_name }) => ({ name: key_name, value: key_name })), };
); };
},
fetchRegions() { export const fetchRoles = () => {
return axios.get(apiPaths.getRegionsPath).then(({ data: { regions } }) => const iam = new IAM();
regions.map(({ region_name }) => ({
name: region_name, return iam
value: region_name, .listRoles()
.promise()
.then(({ Roles: roles }) => roles.map(({ RoleName: name, Arn: value }) => ({ name, value })));
};
export const fetchRegions = () => {
const ec2 = new EC2();
return ec2
.describeRegions()
.promise()
.then(({ Regions: regions }) =>
regions.map(({ RegionName: name }) => ({
name,
value: name,
})), })),
); );
}, };
fetchVpcs({ region }) {
return axios.get(apiPaths.getVpcsPath, { params: { region } }).then(({ data: { vpcs } }) => export const fetchKeyPairs = ({ region }) => {
vpcs.map(({ vpc_id }) => ({ const ec2 = new EC2({ region });
value: vpc_id,
name: vpc_id, return ec2
.describeKeyPairs()
.promise()
.then(({ KeyPairs: keyPairs }) => keyPairs.map(({ KeyName: name }) => ({ name, value: name })));
};
export const fetchVpcs = ({ region }) => {
const ec2 = new EC2({ region });
return ec2
.describeVpcs()
.promise()
.then(({ Vpcs: vpcs }) =>
vpcs.map(vpc => ({
value: vpc.VpcId,
name: lookupVpcName(vpc),
})), })),
); );
};
export const fetchSubnets = ({ vpc, region }) => {
const ec2 = new EC2({ region });
return ec2
.describeSubnets({
Filters: [
{
Name: 'vpc-id',
Values: [vpc],
}, },
fetchSubnets({ vpc, region }) { ],
return axios })
.get(apiPaths.getSubnetsPath, { params: { vpc_id: vpc, region } }) .promise()
.then(({ data: { subnets } }) => .then(({ Subnets: subnets }) => subnets.map(({ SubnetId: id }) => ({ value: id, name: id })));
subnets.map(({ subnet_id }) => ({ name: subnet_id, value: subnet_id })), };
export const fetchSecurityGroups = ({ region, vpc }) => {
const ec2 = new EC2({ region });
return ec2
.describeSecurityGroups({
Filters: [
{
Name: 'vpc-id',
Values: [vpc],
},
],
})
.promise()
.then(({ SecurityGroups: securityGroups }) =>
securityGroups.map(({ GroupName: name, GroupId: value }) => ({ name, value })),
); );
}, };
fetchSecurityGroups({ vpc, region }) {
return axios
.get(apiPaths.getSecurityGroupsPath, { params: { vpc_id: vpc, region } })
.then(({ data: { security_groups: securityGroups } }) =>
securityGroups.map(({ group_name: name, group_id: value }) => ({ name, value })),
);
},
fetchInstanceTypes() {
return axios
.get(apiPaths.getInstanceTypesPath)
.then(({ data: { instance_types: instanceTypes } }) =>
instanceTypes.map(({ instance_type_name }) => ({
name: instance_type_name,
value: instance_type_name,
})),
);
},
});

View file

@ -1,6 +1,8 @@
import * as types from './mutation_types'; import * as types from './mutation_types';
import { setAWSConfig } from '../services/aws_services_facade';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
const getErrorMessage = data => { const getErrorMessage = data => {
const errorKey = Object.keys(data)[0]; const errorKey = Object.keys(data)[0];
@ -28,7 +30,7 @@ export const createRole = ({ dispatch, state: { createRolePath } }, payload) =>
role_arn: payload.roleArn, role_arn: payload.roleArn,
role_external_id: payload.externalId, role_external_id: payload.externalId,
}) })
.then(() => dispatch('createRoleSuccess')) .then(({ data }) => dispatch('createRoleSuccess', convertObjectPropsToCamelCase(data)))
.catch(error => dispatch('createRoleError', { error })); .catch(error => dispatch('createRoleError', { error }));
}; };
@ -36,7 +38,8 @@ export const requestCreateRole = ({ commit }) => {
commit(types.REQUEST_CREATE_ROLE); commit(types.REQUEST_CREATE_ROLE);
}; };
export const createRoleSuccess = ({ commit }) => { export const createRoleSuccess = ({ commit }, awsCredentials) => {
setAWSConfig({ awsCredentials });
commit(types.CREATE_ROLE_SUCCESS); commit(types.CREATE_ROLE_SUCCESS);
}; };
@ -117,9 +120,3 @@ export const setInstanceType = ({ commit }, payload) => {
export const setNodeCount = ({ commit }, payload) => { export const setNodeCount = ({ commit }, payload) => {
commit(types.SET_NODE_COUNT, payload); commit(types.SET_NODE_COUNT, payload);
}; };
export const signOut = ({ commit, state: { signOutPath } }) =>
axios
.delete(signOutPath)
.then(() => commit(types.SIGN_OUT))
.catch(({ response: { data } }) => createFlash(getErrorMessage(data)));

View file

@ -3,11 +3,11 @@ import actions from './actions';
import mutations from './mutations'; import mutations from './mutations';
import state from './state'; import state from './state';
const createStore = fetchFn => ({ const createStore = ({ fetchFn, initialState }) => ({
actions: actions(fetchFn), actions: actions(fetchFn),
getters, getters,
mutations, mutations,
state: state(), state: Object.assign(state(), initialState || {}),
}); });
export default createStore; export default createStore;

View file

@ -6,12 +6,17 @@ import state from './state';
import clusterDropdownStore from './cluster_dropdown'; import clusterDropdownStore from './cluster_dropdown';
import awsServicesFactory from '../services/aws_services_facade'; import {
fetchRoles,
fetchRegions,
fetchKeyPairs,
fetchVpcs,
fetchSubnets,
fetchSecurityGroups,
} from '../services/aws_services_facade';
const createStore = ({ initialState, apiPaths }) => { const createStore = ({ initialState }) =>
const awsServices = awsServicesFactory(apiPaths); new Vuex.Store({
return new Vuex.Store({
actions, actions,
getters, getters,
mutations, mutations,
@ -19,34 +24,33 @@ const createStore = ({ initialState, apiPaths }) => {
modules: { modules: {
roles: { roles: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchRoles), ...clusterDropdownStore({ fetchFn: fetchRoles }),
}, },
regions: { regions: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchRegions), ...clusterDropdownStore({ fetchFn: fetchRegions }),
}, },
keyPairs: { keyPairs: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchKeyPairs), ...clusterDropdownStore({ fetchFn: fetchKeyPairs }),
}, },
vpcs: { vpcs: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchVpcs), ...clusterDropdownStore({ fetchFn: fetchVpcs }),
}, },
subnets: { subnets: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchSubnets), ...clusterDropdownStore({ fetchFn: fetchSubnets }),
}, },
securityGroups: { securityGroups: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchSecurityGroups), ...clusterDropdownStore({ fetchFn: fetchSecurityGroups }),
}, },
instanceTypes: { instanceTypes: {
namespaced: true, namespaced: true,
...clusterDropdownStore(awsServices.fetchInstanceTypes), ...clusterDropdownStore({ initialState: { items: initialState.instanceTypes } }),
}, },
}, },
}); });
};
export default createStore; export default createStore;

View file

@ -13,7 +13,6 @@ export const SET_GITLAB_MANAGED_CLUSTER = 'SET_GITLAB_MANAGED_CLUSTER';
export const REQUEST_CREATE_ROLE = 'REQUEST_CREATE_ROLE'; export const REQUEST_CREATE_ROLE = 'REQUEST_CREATE_ROLE';
export const CREATE_ROLE_SUCCESS = 'CREATE_ROLE_SUCCESS'; export const CREATE_ROLE_SUCCESS = 'CREATE_ROLE_SUCCESS';
export const CREATE_ROLE_ERROR = 'CREATE_ROLE_ERROR'; export const CREATE_ROLE_ERROR = 'CREATE_ROLE_ERROR';
export const SIGN_OUT = 'SIGN_OUT';
export const REQUEST_CREATE_CLUSTER = 'REQUEST_CREATE_CLUSTER'; export const REQUEST_CREATE_CLUSTER = 'REQUEST_CREATE_CLUSTER';
export const CREATE_CLUSTER_SUCCESS = 'CREATE_CLUSTER_SUCCESS'; export const CREATE_CLUSTER_SUCCESS = 'CREATE_CLUSTER_SUCCESS';
export const CREATE_CLUSTER_ERROR = 'CREATE_CLUSTER_ERROR'; export const CREATE_CLUSTER_ERROR = 'CREATE_CLUSTER_ERROR';

View file

@ -60,7 +60,4 @@ export default {
state.isCreatingCluster = false; state.isCreatingCluster = false;
state.createClusterError = error; state.createClusterError = error;
}, },
[types.SIGN_OUT](state) {
state.hasCredentials = false;
},
}; };

View file

@ -12,6 +12,8 @@ export default () => ({
accountId: '', accountId: '',
externalId: '', externalId: '',
roleArn: '',
clusterName: '', clusterName: '',
environmentScope: '*', environmentScope: '*',
kubernetesVersion, kubernetesVersion,

View file

@ -1,8 +1,8 @@
import _ from 'underscore'; import _ from 'underscore';
import { GlLoadingIcon } from '@gitlab/ui';
import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue'; import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue'; import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue'; import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import store from '../store'; import store from '../store';

View file

@ -1,6 +1,6 @@
<script> <script>
import { sprintf, s__ } from '~/locale';
import { mapState, mapGetters, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import { sprintf, s__ } from '~/locale';
import gkeDropdownMixin from './gke_dropdown_mixin'; import gkeDropdownMixin from './gke_dropdown_mixin';

View file

@ -1,7 +1,7 @@
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { s__, sprintf } from '~/locale';
import { mapState, mapGetters, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import { s__, sprintf } from '~/locale';
import gkeDropdownMixin from './gke_dropdown_mixin'; import gkeDropdownMixin from './gke_dropdown_mixin';

View file

@ -1,6 +1,6 @@
<script> <script>
import { sprintf, s__ } from '~/locale';
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { sprintf, s__ } from '~/locale';
import gkeDropdownMixin from './gke_dropdown_mixin'; import gkeDropdownMixin from './gke_dropdown_mixin';

View file

@ -6,7 +6,7 @@ const newClusterViews = [':clusters:new', ':clusters:create_gcp', ':clusters:cre
const isProjectLevelCluster = page => page.startsWith('project:clusters'); const isProjectLevelCluster = page => page.startsWith('project:clusters');
export default (document, gon) => { export default document => {
const { page } = document.body.dataset; const { page } = document.body.dataset;
const isNewClusterView = newClusterViews.some(view => page.endsWith(view)); const isNewClusterView = newClusterViews.some(view => page.endsWith(view));
@ -19,7 +19,6 @@ export default (document, gon) => {
initGkeDropdowns(); initGkeDropdowns();
if (gon.features.createEksClusters) {
import(/* webpackChunkName: 'eks_cluster' */ '~/create_cluster/eks_cluster') import(/* webpackChunkName: 'eks_cluster' */ '~/create_cluster/eks_cluster')
.then(({ default: initCreateEKSCluster }) => { .then(({ default: initCreateEKSCluster }) => {
const el = document.querySelector('.js-create-eks-cluster-form-container'); const el = document.querySelector('.js-create-eks-cluster-form-container');
@ -29,7 +28,6 @@ export default (document, gon) => {
} }
}) })
.catch(() => {}); .catch(() => {});
}
if (isProjectLevelCluster(page)) { if (isProjectLevelCluster(page)) {
initGkeNamespace(); initGkeNamespace();

View file

@ -311,6 +311,7 @@ export default class CreateMergeRequestDropdown {
} }
onChangeInput(event) { onChangeInput(event) {
this.disable();
let target; let target;
let value; let value;

View file

@ -1,6 +1,6 @@
<script> <script>
import Icon from '~/vue_shared/components/icon.vue';
import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg'; import iconCycleAnalyticsSplash from 'icons/_icon_cycle_analytics_splash.svg';
import Icon from '~/vue_shared/components/icon.vue';
export default { export default {
components: { components: {

View file

@ -1,4 +1,5 @@
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Flash from '~/flash'; import Flash from '~/flash';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue'; import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
@ -6,7 +7,6 @@ import eventHub from '../eventhub';
import DeployKeysService from '../service'; import DeployKeysService from '../service';
import DeployKeysStore from '../store'; import DeployKeysStore from '../store';
import KeysPanel from './keys_panel.vue'; import KeysPanel from './keys_panel.vue';
import { GlLoadingIcon } from '@gitlab/ui';
export default { export default {
components: { components: {
@ -133,7 +133,7 @@ export default {
:keys="keys[currentTab]" :keys="keys[currentTab]"
:store="store" :store="store"
:endpoint="endpoint" :endpoint="endpoint"
class="qa-project-deploy-keys" data-qa-selector="project_deploy_keys"
/> />
</template> </template>
</div> </div>

View file

@ -159,7 +159,7 @@ export default {
<div role="rowheader" class="table-mobile-header">{{ __('Created') }}</div> <div role="rowheader" class="table-mobile-header">{{ __('Created') }}</div>
<div class="table-mobile-content text-secondary key-created-at"> <div class="table-mobile-content text-secondary key-created-at">
<span v-tooltip :title="tooltipTitle(deployKey.created_at)"> <span v-tooltip :title="tooltipTitle(deployKey.created_at)">
<icon name="calendar" /> <span>{{ timeFormated(deployKey.created_at) }}</span> <icon name="calendar" /> <span>{{ timeFormatted(deployKey.created_at) }}</span>
</span> </span>
</div> </div>
</div> </div>

View file

@ -1,11 +1,12 @@
<script> <script>
import { mapState, mapGetters, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import Mousetrap from 'mousetrap';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale'; import { __ } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { GlLoadingIcon } from '@gitlab/ui';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue'; import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import Mousetrap from 'mousetrap'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import eventHub from '../../notes/event_hub'; import eventHub from '../../notes/event_hub';
import CompareVersions from './compare_versions.vue'; import CompareVersions from './compare_versions.vue';
import DiffFile from './diff_file.vue'; import DiffFile from './diff_file.vue';
@ -36,11 +37,20 @@ export default {
GlLoadingIcon, GlLoadingIcon,
PanelResizer, PanelResizer,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
endpoint: { endpoint: {
type: String, type: String,
required: true, required: true,
}, },
endpointMetadata: {
type: String,
required: true,
},
endpointBatch: {
type: String,
required: true,
},
projectPath: { projectPath: {
type: String, type: String,
required: true, required: true,
@ -92,6 +102,7 @@ export default {
computed: { computed: {
...mapState({ ...mapState({
isLoading: state => state.diffs.isLoading, isLoading: state => state.diffs.isLoading,
isBatchLoading: state => state.diffs.isBatchLoading,
diffFiles: state => state.diffs.diffFiles, diffFiles: state => state.diffs.diffFiles,
diffViewType: state => state.diffs.diffViewType, diffViewType: state => state.diffs.diffViewType,
mergeRequestDiffs: state => state.diffs.mergeRequestDiffs, mergeRequestDiffs: state => state.diffs.mergeRequestDiffs,
@ -133,6 +144,9 @@ export default {
isLimitedContainer() { isLimitedContainer() {
return !this.showTreeList && !this.isParallelView && !this.isFluidLayout; return !this.showTreeList && !this.isParallelView && !this.isFluidLayout;
}, },
shouldSetDiscussions() {
return this.isNotesFetched && !this.assignedDiscussions && !this.isLoading;
},
}, },
watch: { watch: {
diffViewType() { diffViewType() {
@ -149,13 +163,21 @@ export default {
}, },
isLoading: 'adjustView', isLoading: 'adjustView',
showTreeList: 'adjustView', showTreeList: 'adjustView',
shouldSetDiscussions(newVal) {
if (newVal) {
this.setDiscussions();
}
},
}, },
mounted() { mounted() {
this.setBaseConfig({ this.setBaseConfig({
endpoint: this.endpoint, endpoint: this.endpoint,
endpointMetadata: this.endpointMetadata,
endpointBatch: this.endpointBatch,
projectPath: this.projectPath, projectPath: this.projectPath,
dismissEndpoint: this.dismissEndpoint, dismissEndpoint: this.dismissEndpoint,
showSuggestPopover: this.showSuggestPopover, showSuggestPopover: this.showSuggestPopover,
useSingleDiffStyle: this.glFeatures.singleMrDiffView,
}); });
if (this.shouldShow) { if (this.shouldShow) {
@ -185,6 +207,8 @@ export default {
...mapActions('diffs', [ ...mapActions('diffs', [
'setBaseConfig', 'setBaseConfig',
'fetchDiffFiles', 'fetchDiffFiles',
'fetchDiffFilesMeta',
'fetchDiffFilesBatch',
'startRenderDiffsQueue', 'startRenderDiffsQueue',
'assignDiscussionsToDiff', 'assignDiscussionsToDiff',
'setHighlightedRow', 'setHighlightedRow',
@ -196,7 +220,32 @@ export default {
this.assignedDiscussions = false; this.assignedDiscussions = false;
this.fetchData(false); this.fetchData(false);
}, },
startDiffRendering() {
requestIdleCallback(
() => {
this.startRenderDiffsQueue();
},
{ timeout: 1000 },
);
},
fetchData(toggleTree = true) { fetchData(toggleTree = true) {
if (this.glFeatures.diffsBatchLoad) {
this.fetchDiffFilesMeta()
.then(() => {
if (toggleTree) this.hideTreeListIfJustOneFile();
this.startDiffRendering();
})
.catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
});
this.fetchDiffFilesBatch()
.then(() => this.startDiffRendering())
.catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
});
} else {
this.fetchDiffFiles() this.fetchDiffFiles()
.then(() => { .then(() => {
if (toggleTree) { if (toggleTree) {
@ -205,7 +254,6 @@ export default {
requestIdleCallback( requestIdleCallback(
() => { () => {
this.setDiscussions();
this.startRenderDiffsQueue(); this.startRenderDiffsQueue();
}, },
{ timeout: 1000 }, { timeout: 1000 },
@ -214,13 +262,14 @@ export default {
.catch(() => { .catch(() => {
createFlash(__('Something went wrong on our end. Please try again!')); createFlash(__('Something went wrong on our end. Please try again!'));
}); });
}
if (!this.isNotesFetched) { if (!this.isNotesFetched) {
eventHub.$emit('fetchNotesData'); eventHub.$emit('fetchNotesData');
} }
}, },
setDiscussions() { setDiscussions() {
if (this.isNotesFetched && !this.assignedDiscussions && !this.isLoading) { if (this.shouldSetDiscussions) {
this.assignedDiscussions = true; this.assignedDiscussions = true;
requestIdleCallback( requestIdleCallback(
@ -324,7 +373,8 @@ export default {
}" }"
> >
<commit-widget v-if="commit" :commit="commit" /> <commit-widget v-if="commit" :commit="commit" />
<template v-if="renderDiffFiles"> <div v-if="isBatchLoading" class="loading"><gl-loading-icon /></div>
<template v-else-if="renderDiffFiles">
<diff-file <diff-file
v-for="file in diffFiles" v-for="file in diffFiles"
:key="file.newPath" :key="file.newPath"

View file

@ -1,7 +1,7 @@
<script> <script>
import { mapState, mapActions } from 'vuex';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { mapState, mapActions } from 'vuex';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { UNFOLD_COUNT } from '../constants'; import { UNFOLD_COUNT } from '../constants';
import * as utils from '../store/utils'; import * as utils from '../store/utils';
@ -226,7 +226,7 @@ export default {
<icon :size="12" name="expand-up" aria-hidden="true" /> <icon :size="12" name="expand-up" aria-hidden="true" />
</a> </a>
<a class="mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()"> <a class="mx-2 cursor-pointer js-unfold-all" @click="handleExpandLines()">
<span>{{ s__('Diffs|Show all lines') }}</span> <span>{{ s__('Diffs|Show unchanged lines') }}</span>
</a> </a>
<a <a
v-if="canExpandDown" v-if="canExpandDown"

View file

@ -1,9 +1,9 @@
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import _ from 'underscore'; import _ from 'underscore';
import { GlLoadingIcon } from '@gitlab/ui';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { GlLoadingIcon } from '@gitlab/ui';
import eventHub from '../../notes/event_hub'; import eventHub from '../../notes/event_hub';
import DiffFileHeader from './diff_file_header.vue'; import DiffFileHeader from './diff_file_header.vue';
import DiffContent from './diff_content.vue'; import DiffContent from './diff_content.vue';

View file

@ -1,17 +1,17 @@
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import { polyfillSticky, stickyMonitor } from '~/lib/utils/sticky'; import { GlButton, GlTooltipDirective, GlTooltip, GlLoadingIcon } from '@gitlab/ui';
import { polyfillSticky } from '~/lib/utils/sticky';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue';
import { GlButton, GlTooltipDirective, GlTooltip, GlLoadingIcon } from '@gitlab/ui';
import { truncateSha } from '~/lib/utils/text_utility'; import { truncateSha } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { diffViewerModes } from '~/ide/constants'; import { diffViewerModes } from '~/ide/constants';
import EditButton from './edit_button.vue'; import EditButton from './edit_button.vue';
import DiffStats from './diff_stats.vue'; import DiffStats from './diff_stats.vue';
import { scrollToElement, contentTop } from '~/lib/utils/common_utils'; import { scrollToElement } from '~/lib/utils/common_utils';
export default { export default {
components: { components: {
@ -127,8 +127,6 @@ export default {
}, },
mounted() { mounted() {
polyfillSticky(this.$refs.header); polyfillSticky(this.$refs.header);
const fileHeaderHeight = this.$refs.header.clientHeight;
stickyMonitor(this.$refs.header, contentTop() - fileHeaderHeight - 1, false);
}, },
methods: { methods: {
...mapActions('diffs', [ ...mapActions('diffs', [

View file

@ -1,9 +1,9 @@
<script> <script>
import { GlTooltipDirective } from '@gitlab/ui';
import { n__ } from '~/locale'; import { n__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { truncate } from '~/lib/utils/text_utility'; import { truncate } from '~/lib/utils/text_utility';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import { GlTooltipDirective } from '@gitlab/ui';
import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants'; import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants';
export default { export default {

View file

@ -72,7 +72,7 @@ export default {
lineCode() { lineCode() {
return ( return (
this.line.line_code || this.line.line_code ||
(this.line.left && this.line.line.left.line_code) || (this.line.left && this.line.left.line_code) ||
(this.line.right && this.line.right.line_code) (this.line.right && this.line.right.line_code)
); );
}, },

View file

@ -1,7 +1,7 @@
<script> <script>
import { mapState, mapGetters, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import { s__ } from '~/locale';
import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form'; import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form';
import { s__ } from '~/locale';
import noteForm from '../../notes/components/note_form.vue'; import noteForm from '../../notes/components/note_form.vue';
import autosave from '../../notes/mixins/autosave'; import autosave from '../../notes/mixins/autosave';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';

View file

@ -57,3 +57,4 @@ export const MIN_RENDERING_MS = 2;
export const START_RENDERING_INDEX = 200; export const START_RENDERING_INDEX = 200;
export const INLINE_DIFF_LINES_KEY = 'highlighted_diff_lines'; export const INLINE_DIFF_LINES_KEY = 'highlighted_diff_lines';
export const PARALLEL_DIFF_LINES_KEY = 'parallel_diff_lines'; export const PARALLEL_DIFF_LINES_KEY = 'parallel_diff_lines';
export const DIFFS_PER_PAGE = 20;

View file

@ -67,6 +67,8 @@ export default function initDiffsApp(store) {
return { return {
endpoint: dataset.endpoint, endpoint: dataset.endpoint,
endpointMetadata: dataset.endpointMetadata || '',
endpointBatch: dataset.endpointBatch || '',
projectPath: dataset.projectPath, projectPath: dataset.projectPath,
helpPagePath: dataset.helpPagePath, helpPagePath: dataset.helpPagePath,
currentUser: JSON.parse(dataset.currentUserData) || {}, currentUser: JSON.parse(dataset.currentUserData) || {},
@ -100,6 +102,8 @@ export default function initDiffsApp(store) {
return createElement('diffs-app', { return createElement('diffs-app', {
props: { props: {
endpoint: this.endpoint, endpoint: this.endpoint,
endpointMetadata: this.endpointMetadata,
endpointBatch: this.endpointBatch,
currentUser: this.currentUser, currentUser: this.currentUser,
projectPath: this.projectPath, projectPath: this.projectPath,
helpPagePath: this.helpPagePath, helpPagePath: this.helpPagePath,

View file

@ -1,6 +1,6 @@
import Vue from 'vue'; import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { handleLocationHash, historyPushState, scrollToElement } from '~/lib/utils/common_utils'; import { handleLocationHash, historyPushState, scrollToElement } from '~/lib/utils/common_utils';
@ -13,6 +13,7 @@ import {
convertExpandLines, convertExpandLines,
idleCallback, idleCallback,
allDiscussionWrappersExpanded, allDiscussionWrappersExpanded,
prepareDiffData,
} from './utils'; } from './utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { import {
@ -33,16 +34,83 @@ import {
START_RENDERING_INDEX, START_RENDERING_INDEX,
INLINE_DIFF_LINES_KEY, INLINE_DIFF_LINES_KEY,
PARALLEL_DIFF_LINES_KEY, PARALLEL_DIFF_LINES_KEY,
DIFFS_PER_PAGE,
} from '../constants'; } from '../constants';
import { diffViewerModes } from '~/ide/constants'; import { diffViewerModes } from '~/ide/constants';
export const setBaseConfig = ({ commit }, options) => { export const setBaseConfig = ({ commit }, options) => {
const { endpoint, projectPath, dismissEndpoint, showSuggestPopover } = options; const {
commit(types.SET_BASE_CONFIG, { endpoint, projectPath, dismissEndpoint, showSuggestPopover }); endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
useSingleDiffStyle,
} = options;
commit(types.SET_BASE_CONFIG, {
endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
useSingleDiffStyle,
});
}; };
export const fetchDiffFiles = ({ state, commit }) => { export const fetchDiffFiles = ({ state, commit }) => {
const worker = new TreeWorker(); const worker = new TreeWorker();
const urlParams = {
w: state.showWhitespace ? '0' : '1',
};
commit(types.SET_LOADING, true);
worker.addEventListener('message', ({ data }) => {
commit(types.SET_TREE_DATA, data);
worker.terminate();
});
return axios
.get(mergeUrlParams(urlParams, state.endpoint))
.then(res => {
commit(types.SET_LOADING, false);
commit(types.SET_MERGE_REQUEST_DIFFS, res.data.merge_request_diffs || []);
commit(types.SET_DIFF_DATA, res.data);
worker.postMessage(state.diffFiles);
return Vue.nextTick();
})
.then(handleLocationHash)
.catch(() => worker.terminate());
};
export const fetchDiffFilesBatch = ({ commit, state }) => {
commit(types.SET_BATCH_LOADING, true);
const getBatch = page =>
axios
.get(state.endpointBatch, {
params: { page, per_page: DIFFS_PER_PAGE, w: state.showWhitespace ? '0' : '1' },
})
.then(({ data: { pagination, diff_files } }) => {
commit(types.SET_DIFF_DATA_BATCH, { diff_files });
commit(types.SET_BATCH_LOADING, false);
return pagination.next_page;
})
.then(nextPage => nextPage && getBatch(nextPage));
return getBatch()
.then(handleLocationHash)
.catch(() => null);
};
export const fetchDiffFilesMeta = ({ commit, state }) => {
const worker = new TreeWorker();
commit(types.SET_LOADING, true); commit(types.SET_LOADING, true);
@ -53,17 +121,17 @@ export const fetchDiffFiles = ({ state, commit }) => {
}); });
return axios return axios
.get(mergeUrlParams({ w: state.showWhitespace ? '0' : '1' }, state.endpoint)) .get(state.endpointMetadata)
.then(res => { .then(({ data }) => {
const strippedData = { ...data };
delete strippedData.diff_files;
commit(types.SET_LOADING, false); commit(types.SET_LOADING, false);
commit(types.SET_MERGE_REQUEST_DIFFS, res.data.merge_request_diffs || []); commit(types.SET_MERGE_REQUEST_DIFFS, data.merge_request_diffs || []);
commit(types.SET_DIFF_DATA, res.data); commit(types.SET_DIFF_DATA, strippedData);
worker.postMessage(state.diffFiles); prepareDiffData(data);
worker.postMessage(data.diff_files);
return Vue.nextTick();
}) })
.then(handleLocationHash)
.catch(() => worker.terminate()); .catch(() => worker.terminate());
}; };

View file

@ -8,6 +8,7 @@ const defaultViewType = INLINE_DIFF_VIEW_TYPE;
export default () => ({ export default () => ({
isLoading: true, isLoading: true,
isBatchLoading: false,
addedLines: null, addedLines: null,
removedLines: null, removedLines: null,
endpoint: '', endpoint: '',
@ -30,4 +31,5 @@ export default () => ({
fileFinderVisible: false, fileFinderVisible: false,
dismissEndpoint: '', dismissEndpoint: '',
showSuggestPopover: true, showSuggestPopover: true,
useSingleDiffStyle: false,
}); });

View file

@ -1,6 +1,8 @@
export const SET_BASE_CONFIG = 'SET_BASE_CONFIG'; export const SET_BASE_CONFIG = 'SET_BASE_CONFIG';
export const SET_LOADING = 'SET_LOADING'; export const SET_LOADING = 'SET_LOADING';
export const SET_BATCH_LOADING = 'SET_BATCH_LOADING';
export const SET_DIFF_DATA = 'SET_DIFF_DATA'; export const SET_DIFF_DATA = 'SET_DIFF_DATA';
export const SET_DIFF_DATA_BATCH = 'SET_DIFF_DATA_BATCH';
export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE'; export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE';
export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS'; export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM'; export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM';

View file

@ -12,22 +12,57 @@ import * as types from './mutation_types';
export default { export default {
[types.SET_BASE_CONFIG](state, options) { [types.SET_BASE_CONFIG](state, options) {
const { endpoint, projectPath, dismissEndpoint, showSuggestPopover } = options; const {
Object.assign(state, { endpoint, projectPath, dismissEndpoint, showSuggestPopover }); endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
useSingleDiffStyle,
} = options;
Object.assign(state, {
endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
useSingleDiffStyle,
});
}, },
[types.SET_LOADING](state, isLoading) { [types.SET_LOADING](state, isLoading) {
Object.assign(state, { isLoading }); Object.assign(state, { isLoading });
}, },
[types.SET_BATCH_LOADING](state, isBatchLoading) {
Object.assign(state, { isBatchLoading });
},
[types.SET_DIFF_DATA](state, data) { [types.SET_DIFF_DATA](state, data) {
if (
!(
gon &&
gon.features &&
gon.features.diffsBatchLoad &&
window.location.search.indexOf('diff_id') === -1
)
) {
prepareDiffData(data); prepareDiffData(data);
}
Object.assign(state, { Object.assign(state, {
...convertObjectPropsToCamelCase(data), ...convertObjectPropsToCamelCase(data),
}); });
}, },
[types.SET_DIFF_DATA_BATCH](state, data) {
prepareDiffData(data);
state.diffFiles.push(...data.diff_files);
},
[types.RENDER_FILE](state, file) { [types.RENDER_FILE](state, file) {
Object.assign(file, { Object.assign(file, {
renderIt: true, renderIt: true,

View file

@ -252,10 +252,11 @@ export function prepareDiffData(diffData) {
showingLines += file.parallel_diff_lines.length; showingLines += file.parallel_diff_lines.length;
} }
const name = (file.viewer && file.viewer.name) || diffViewerModes.text;
Object.assign(file, { Object.assign(file, {
renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY, renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
collapsed: collapsed: name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED,
file.viewer.name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED,
isShowingFullFile: false, isShowingFullFile: false,
isLoadingFullFile: false, isLoadingFullFile: false,
discussions: [], discussions: [],
@ -497,7 +498,7 @@ export const allDiscussionWrappersExpanded = diff => {
} }
}); });
} else if (diff.highlighted_diff_lines) { } else if (diff.highlighted_diff_lines) {
diff.parallel_diff_lines.forEach(line => { diff.highlighted_diff_lines.forEach(line => {
if (line.discussions.length) { if (line.discussions.length) {
discussionsExpandedArray.push(line.discussionsExpanded); discussionsExpandedArray.push(line.discussionsExpanded);
} }

View file

@ -1,5 +1,5 @@
import { __ } from '~/locale';
import emojiRegex from 'emoji-regex'; import emojiRegex from 'emoji-regex';
import { __ } from '~/locale';
import InputValidator from '../validators/input_validator'; import InputValidator from '../validators/input_validator';
export default class NoEmojiValidator extends InputValidator { export default class NoEmojiValidator extends InputValidator {

View file

@ -1,7 +1,7 @@
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import containerMixin from 'ee_else_ce/environments/mixins/container_mixin'; import containerMixin from 'ee_else_ce/environments/mixins/container_mixin';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import EnvironmentTable from '../components/environments_table.vue'; import EnvironmentTable from '../components/environments_table.vue';
export default { export default {

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