New upstream version 12.5.4
This commit is contained in:
parent
6212feb931
commit
510528bb68
2910 changed files with 92827 additions and 25855 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -59,6 +59,7 @@ eslint-report.html
|
|||
/public/uploads.*
|
||||
/public/uploads/
|
||||
/shared/artifacts/
|
||||
/spec/examples.txt
|
||||
/rails_best_practices_output.html
|
||||
/tags
|
||||
/vendor/bundle/*
|
||||
|
@ -82,3 +83,4 @@ jsdoc/
|
|||
**/tmp/rubocop_cache/**
|
||||
.overcommit.yml
|
||||
.projections.json
|
||||
/qa/.rakeTasks
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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"
|
||||
|
||||
stages:
|
||||
- sync
|
||||
- prepare
|
||||
- quick-test
|
||||
- test
|
||||
|
@ -8,7 +9,6 @@ stages:
|
|||
- review
|
||||
- qa
|
||||
- post-test
|
||||
- notification
|
||||
- pages
|
||||
|
||||
variables:
|
||||
|
@ -33,7 +33,6 @@ include:
|
|||
- local: .gitlab/ci/frontend.gitlab-ci.yml
|
||||
- local: .gitlab/ci/global.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/qa.gitlab-ci.yml
|
||||
- local: .gitlab/ci/reports.gitlab-ci.yml
|
||||
|
@ -42,3 +41,4 @@ include:
|
|||
- local: .gitlab/ci/setup.gitlab-ci.yml
|
||||
- local: .gitlab/ci/test-metadata.gitlab-ci.yml
|
||||
- local: .gitlab/ci/yaml.gitlab-ci.yml
|
||||
- local: .gitlab/ci/releases.gitlab-ci.yml
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
*.rake @gitlab-org/maintainers/rails-backend
|
||||
|
||||
# Technical writing team are the default reviewers for everything in `doc/`
|
||||
/doc/ @axil @marcia @eread @mikelewis
|
||||
/doc/ @gl-docsteam
|
||||
|
||||
# Frontend maintainers should see everything in `app/assets/`
|
||||
app/assets/ @ClemMakesApps @fatihacet @filipa @mikegreiling @timzallmann @kushalpandya @pslaughter @wortschi @ntepluhina
|
||||
*.scss @annabeldunstone @ClemMakesApps @fatihacet @filipa @mikegreiling @timzallmann @kushalpandya @pslaughter @wortschi @ntepluhina
|
||||
app/assets/ @gitlab-org/maintainers/frontend
|
||||
*.scss @annabeldunstone @gitlab-org/maintainers/frontend
|
||||
/scripts/frontend/ @gitlab-org/maintainers/frontend
|
||||
|
||||
# Database maintainers should review changes in `db/`
|
||||
db/ @gitlab-org/maintainers/database
|
||||
|
@ -32,4 +33,5 @@ lib/gitlab/github_import/ @gitlab-org/maintainers/database
|
|||
/.gitlab/ci/ @gl-quality/eng-prod
|
||||
Dangerfile @gl-quality/eng-prod
|
||||
/danger/ @gl-quality/eng-prod
|
||||
/lib/gitlab/danger/ @gl-quality/eng-prod
|
||||
/scripts/ @gl-quality/eng-prod
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
cloud-native-image:
|
||||
extends: .only:variables-canonical-dot-com
|
||||
image: ruby:2.6-alpine
|
||||
dependencies: []
|
||||
stage: post-test
|
||||
|
@ -12,5 +13,3 @@ cloud-native-image:
|
|||
only:
|
||||
refs:
|
||||
- tags
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
|
||||
|
|
|
@ -2,12 +2,11 @@
|
|||
extends:
|
||||
- .default-tags
|
||||
- .default-retry
|
||||
- .only-docs-changes
|
||||
- .only:variables-canonical-dot-com
|
||||
- .only:changes-docs
|
||||
only:
|
||||
refs:
|
||||
- merge_requests
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
|
||||
image: ruby:2.6-alpine
|
||||
stage: review
|
||||
dependencies: []
|
||||
|
@ -50,7 +49,7 @@ docs lint:
|
|||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-docs-changes
|
||||
- .only:changes-docs
|
||||
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-docs-lint"
|
||||
stage: test
|
||||
dependencies: []
|
||||
|
@ -68,7 +67,7 @@ docs lint:
|
|||
# Check the internal anchor links
|
||||
- bundle exec nanoc check internal_anchors
|
||||
|
||||
graphql-docs-verify:
|
||||
graphql-reference-verify:
|
||||
extends:
|
||||
- .only-ee
|
||||
- .default-tags
|
||||
|
@ -76,10 +75,10 @@ graphql-docs-verify:
|
|||
- .default-cache
|
||||
- .default-only
|
||||
- .default-before_script
|
||||
- .only-graphql-changes
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
- .only:changes-code-backstage-qa
|
||||
- .use-pg9
|
||||
stage: test
|
||||
needs: ["setup-test-env"]
|
||||
script:
|
||||
- bundle exec rake gitlab:graphql:check_docs
|
||||
- bundle exec rake gitlab:graphql:check_schema
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
- .default-only
|
||||
- .default-before_script
|
||||
- .assets-compile-cache
|
||||
- .only-code-qa-changes
|
||||
- .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
|
||||
stage: test
|
||||
dependencies: ["setup-test-env"]
|
||||
|
@ -73,7 +73,7 @@ gitlab:assets:compile pull-cache:
|
|||
- .default-only
|
||||
- .default-before_script
|
||||
- .assets-compile-cache
|
||||
- .only-code-qa-changes
|
||||
- .only:changes-code-backstage-qa
|
||||
- .use-pg9
|
||||
stage: prepare
|
||||
script:
|
||||
|
@ -128,7 +128,7 @@ compile-assets pull-cache foss:
|
|||
- .default-cache
|
||||
- .default-only
|
||||
- .default-before_script
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
- .use-pg9
|
||||
stage: test
|
||||
needs: ["setup-test-env", "compile-assets pull-cache"]
|
||||
|
@ -205,7 +205,7 @@ jest-foss:
|
|||
- .default-retry
|
||||
- .default-cache
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
stage: test
|
||||
dependencies: []
|
||||
cache:
|
||||
|
@ -238,7 +238,7 @@ webpack-dev-server:
|
|||
- .default-retry
|
||||
- .default-cache
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
stage: test
|
||||
needs: ["setup-test-env", "compile-assets pull-cache"]
|
||||
dependencies: ["setup-test-env", "compile-assets pull-cache"]
|
||||
|
|
|
@ -40,87 +40,160 @@
|
|||
- merge_requests
|
||||
- tags
|
||||
|
||||
.only-code-changes:
|
||||
only:
|
||||
changes:
|
||||
- ".gitlab/ci/**/*"
|
||||
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
|
||||
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
|
||||
- ".csscomb.json"
|
||||
- "Dangerfile"
|
||||
- "Dockerfile.assets"
|
||||
- "*_VERSION"
|
||||
- "Gemfile{,.lock}"
|
||||
- "Rakefile"
|
||||
- "{babel.config,jest.config}.js"
|
||||
- "config.ru"
|
||||
- "{package.json,yarn.lock}"
|
||||
- "{app,bin,config,danger,db,ee,fixtures,haml_lint,lib,locale,public,rubocop,scripts,spec,symbol,vendor}/**/*"
|
||||
- "doc/README.md" # Some RSpec test rely on this file
|
||||
|
||||
.only-qa-changes:
|
||||
only:
|
||||
changes:
|
||||
- ".dockerignore"
|
||||
- "qa/**/*"
|
||||
|
||||
.only-docs-changes:
|
||||
only:
|
||||
changes:
|
||||
- ".gitlab/route-map.yml"
|
||||
- "doc/**/*"
|
||||
- ".markdownlint.json"
|
||||
|
||||
.only-graphql-changes:
|
||||
only:
|
||||
changes:
|
||||
- "{,ee/}app/graphql/**/*"
|
||||
- "{,ee/}lib/gitlab/graphql/**/*"
|
||||
|
||||
.only-code-qa-changes:
|
||||
only:
|
||||
changes:
|
||||
- ".gitlab/ci/**/*"
|
||||
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
|
||||
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
|
||||
- ".csscomb.json"
|
||||
- "Dangerfile"
|
||||
- "Dockerfile.assets"
|
||||
- "*_VERSION"
|
||||
- "Gemfile{,.lock}"
|
||||
- "Rakefile"
|
||||
- "{babel.config,jest.config}.js"
|
||||
- "config.ru"
|
||||
- "{package.json,yarn.lock}"
|
||||
- "{app,bin,config,danger,db,ee,fixtures,haml_lint,lib,locale,public,rubocop,scripts,spec,symbol,vendor}/**/*"
|
||||
- "doc/README.md" # Some RSpec test rely on this file
|
||||
- ".dockerignore"
|
||||
- "qa/**/*"
|
||||
|
||||
.only-review:
|
||||
.only:variables-canonical-dot-com:
|
||||
only:
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
|
||||
kubernetes: active
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
- /^\d+-\d+-auto-deploy-\d+$/
|
||||
- /^[\d-]+-stable(-ee)?$/
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/ # Matches the gitlab-org group or its subgroups
|
||||
|
||||
.only-review-schedules:
|
||||
.only:variables_refs-canonical-dot-com-schedules:
|
||||
extends: .only:variables-canonical-dot-com
|
||||
only:
|
||||
refs:
|
||||
- schedules
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
|
||||
|
||||
.except:refs-deploy:
|
||||
except:
|
||||
refs:
|
||||
- /^\d+-\d+-auto-deploy-\d+$/
|
||||
|
||||
.except:refs-master-tags-stable-deploy:
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
- tags
|
||||
- /^[\d-]+-stable(-ee)?$/
|
||||
- /^\d+-\d+-auto-deploy-\d+$/
|
||||
|
||||
.only:kubernetes:
|
||||
only:
|
||||
kubernetes: active
|
||||
|
||||
.only-canonical-schedules:
|
||||
.only-review:
|
||||
extends:
|
||||
- .only:variables-canonical-dot-com
|
||||
- .only:kubernetes
|
||||
- .except:refs-master-tags-stable-deploy
|
||||
|
||||
.only-review-schedules:
|
||||
extends:
|
||||
- .only:variables_refs-canonical-dot-com-schedules
|
||||
- .only:kubernetes
|
||||
- .except:refs-deploy
|
||||
|
||||
.code-patterns: &code-patterns
|
||||
- ".gitlab/ci/**/*"
|
||||
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
|
||||
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
|
||||
- ".csscomb.json"
|
||||
- "Dockerfile.assets"
|
||||
- "*_VERSION"
|
||||
- "Gemfile{,.lock}"
|
||||
- "Rakefile"
|
||||
- "{babel.config,jest.config}.js"
|
||||
- "config.ru"
|
||||
- "{package.json,yarn.lock}"
|
||||
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/**/*"
|
||||
|
||||
.backstage-patterns: &backstage-patterns
|
||||
- "Dangerfile"
|
||||
- "danger/**/*"
|
||||
- "{,ee/}fixtures/**/*"
|
||||
- "{,ee/}rubocop/**/*"
|
||||
- "{,ee/}spec/**/*"
|
||||
- "doc/README.md" # Some RSpec test rely on this file
|
||||
|
||||
.qa-patterns: &qa-patterns
|
||||
- ".dockerignore"
|
||||
- "qa/**/*"
|
||||
|
||||
.docs-patterns: &docs-patterns
|
||||
- ".gitlab/route-map.yml"
|
||||
- "doc/**/*"
|
||||
- ".markdownlint.json"
|
||||
|
||||
.only:changes-code:
|
||||
only:
|
||||
refs:
|
||||
- schedules@gitlab-org/gitlab
|
||||
- schedules@gitlab-org/gitlab-foss
|
||||
changes: *code-patterns
|
||||
|
||||
.only:changes-qa:
|
||||
only:
|
||||
changes: *qa-patterns
|
||||
|
||||
.only:changes-docs:
|
||||
only:
|
||||
changes: *docs-patterns
|
||||
|
||||
.only:changes-code-backstage:
|
||||
only:
|
||||
changes:
|
||||
- ".gitlab/ci/**/*"
|
||||
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
|
||||
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
|
||||
- ".csscomb.json"
|
||||
- "Dockerfile.assets"
|
||||
- "*_VERSION"
|
||||
- "Gemfile{,.lock}"
|
||||
- "Rakefile"
|
||||
- "{babel.config,jest.config}.js"
|
||||
- "config.ru"
|
||||
- "{package.json,yarn.lock}"
|
||||
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/**/*"
|
||||
# Backstage changes
|
||||
- "Dangerfile"
|
||||
- "danger/**/*"
|
||||
- "{,ee/}fixtures/**/*"
|
||||
- "{,ee/}rubocop/**/*"
|
||||
- "{,ee/}spec/**/*"
|
||||
- "doc/README.md" # Some RSpec test rely on this file
|
||||
|
||||
.only:changes-code-qa:
|
||||
only:
|
||||
changes:
|
||||
- ".gitlab/ci/**/*"
|
||||
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
|
||||
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
|
||||
- ".csscomb.json"
|
||||
- "Dockerfile.assets"
|
||||
- "*_VERSION"
|
||||
- "Gemfile{,.lock}"
|
||||
- "Rakefile"
|
||||
- "{babel.config,jest.config}.js"
|
||||
- "config.ru"
|
||||
- "{package.json,yarn.lock}"
|
||||
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/**/*"
|
||||
# QA changes
|
||||
- ".dockerignore"
|
||||
- "qa/**/*"
|
||||
|
||||
.only:changes-code-backstage-qa:
|
||||
only:
|
||||
changes:
|
||||
- ".gitlab/ci/**/*"
|
||||
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
|
||||
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
|
||||
- ".csscomb.json"
|
||||
- "Dockerfile.assets"
|
||||
- "*_VERSION"
|
||||
- "Gemfile{,.lock}"
|
||||
- "Rakefile"
|
||||
- "{babel.config,jest.config}.js"
|
||||
- "config.ru"
|
||||
- "{package.json,yarn.lock}"
|
||||
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
|
||||
- "doc/api/graphql/**/*"
|
||||
# Backstage changes
|
||||
- "Dangerfile"
|
||||
- "danger/**/*"
|
||||
- "{,ee/}fixtures/**/*"
|
||||
- "{,ee/}rubocop/**/*"
|
||||
- "{,ee/}spec/**/*"
|
||||
- "doc/README.md" # Some RSpec test rely on this file
|
||||
# QA changes
|
||||
- ".dockerignore"
|
||||
- "qa/**/*"
|
||||
|
||||
.use-pg9:
|
||||
services:
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
- .default-cache
|
||||
- .default-only
|
||||
- .default-before_script
|
||||
- .only-code-changes
|
||||
- .only:changes-code
|
||||
|
||||
memory-static:
|
||||
extends: .only-code-memory-job-base
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
.notify:
|
||||
image: alpine
|
||||
stage: notification
|
||||
dependencies: []
|
||||
cache: {}
|
||||
before_script:
|
||||
- apk update && apk add git curl bash
|
||||
|
||||
schedule:package-and-qa:notify-success:
|
||||
extends:
|
||||
- .only-canonical-schedules
|
||||
- .notify
|
||||
variables:
|
||||
COMMIT_NOTES_URL: "https://$CI_SERVER_HOST/$CI_PROJECT_PATH/commit/$CI_COMMIT_SHA#notes-list"
|
||||
script:
|
||||
- 'scripts/notify-slack qa-master ":tada: Scheduled QA against master passed! :tada: See $CI_PIPELINE_URL. For downstream pipelines, see $COMMIT_NOTES_URL" ci_passing'
|
||||
needs: ["schedule:package-and-qa"]
|
||||
when: on_success
|
||||
|
||||
schedule:package-and-qa:notify-failure:
|
||||
extends:
|
||||
- .only-canonical-schedules
|
||||
- .notify
|
||||
variables:
|
||||
COMMIT_NOTES_URL: "https://$CI_SERVER_HOST/$CI_PROJECT_PATH/commit/$CI_COMMIT_SHA#notes-list"
|
||||
script:
|
||||
- 'scripts/notify-slack qa-master ":skull_and_crossbones: Scheduled QA against master failed! :skull_and_crossbones: See $CI_PIPELINE_URL. For downstream pipelines, see $COMMIT_NOTES_URL" ci_failing'
|
||||
needs: ["schedule:package-and-qa"]
|
||||
when: on_failure
|
|
@ -4,12 +4,11 @@ pages:
|
|||
- .default-retry
|
||||
- .default-cache
|
||||
- .default-only
|
||||
- .only-code-qa-changes
|
||||
- .only:variables-canonical-dot-com
|
||||
- .only:changes-code-backstage-qa
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
|
||||
stage: pages
|
||||
dependencies: ["coverage", "karma", "gitlab:assets:compile pull-cache"]
|
||||
script:
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-qa-changes
|
||||
- .only:changes-code-qa
|
||||
stage: test
|
||||
dependencies: []
|
||||
cache:
|
||||
|
@ -31,7 +31,6 @@ qa:selectors-foss:
|
|||
- .only-ee-as-if-foss
|
||||
|
||||
.package-and-qa-base:
|
||||
extends: .default-only
|
||||
image: ruby:2.6-alpine
|
||||
stage: qa
|
||||
dependencies: []
|
||||
|
@ -40,35 +39,31 @@ qa:selectors-foss:
|
|||
- source scripts/utils.sh
|
||||
- install_gitlab_gem
|
||||
- ./scripts/trigger-build omnibus
|
||||
only:
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/)/ # Matches the gitlab-org group or its subgroups
|
||||
|
||||
package-and-qa-manual:
|
||||
extends:
|
||||
- .package-and-qa-base
|
||||
- .only-code-changes
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
- /^\d+-\d+-auto-deploy-\d+$/
|
||||
- .default-only
|
||||
- .only:variables-canonical-dot-com
|
||||
- .except:refs-deploy
|
||||
- .only:changes-code
|
||||
when: manual
|
||||
needs: ["build-qa-image", "gitlab:assets:compile pull-cache"]
|
||||
|
||||
package-and-qa:
|
||||
extends:
|
||||
- .package-and-qa-base
|
||||
- .only-qa-changes
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
- /^\d+-\d+-auto-deploy-\d+$/
|
||||
- .default-only
|
||||
- .only:variables-canonical-dot-com
|
||||
- .except:refs-master-tags-stable-deploy
|
||||
- .only:changes-qa
|
||||
needs: ["build-qa-image", "gitlab:assets:compile pull-cache"]
|
||||
allow_failure: true
|
||||
|
||||
schedule:package-and-qa:
|
||||
extends:
|
||||
- .package-and-qa-base
|
||||
- .only-code-qa-changes
|
||||
- .only-canonical-schedules
|
||||
- .default-only
|
||||
- .only:variables_refs-canonical-dot-com-schedules
|
||||
needs: ["build-qa-image", "gitlab:assets:compile pull-cache"]
|
||||
allow_failure: true
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
- .default-cache
|
||||
- .default-only
|
||||
- .default-before_script
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
|
||||
.only-code-qa-rails-job-base:
|
||||
extends:
|
||||
|
@ -31,7 +31,7 @@
|
|||
- .default-cache
|
||||
- .default-only
|
||||
- .default-before_script
|
||||
- .only-code-qa-changes
|
||||
- .only:changes-code-backstage-qa
|
||||
|
||||
setup-test-env:
|
||||
extends:
|
||||
|
@ -92,6 +92,14 @@ setup-test-env:
|
|||
- .use-pg10
|
||||
- .only-master
|
||||
|
||||
rspec migration pg9:
|
||||
extends: .rspec-base-pg9
|
||||
parallel: 4
|
||||
|
||||
rspec migration pg9-foss:
|
||||
extends: .rspec-base-pg9-foss
|
||||
parallel: 4
|
||||
|
||||
rspec unit pg9:
|
||||
extends: .rspec-base-pg9
|
||||
parallel: 20
|
||||
|
@ -140,9 +148,13 @@ rspec system pg10:
|
|||
- .only-ee
|
||||
- .use-pg10-ee
|
||||
|
||||
rspec-ee migration pg9:
|
||||
extends: .rspec-ee-base-pg9
|
||||
parallel: 2
|
||||
|
||||
rspec-ee unit pg9:
|
||||
extends: .rspec-ee-base-pg9
|
||||
parallel: 7
|
||||
parallel: 5
|
||||
|
||||
rspec-ee integration pg9:
|
||||
extends: .rspec-ee-base-pg9
|
||||
|
@ -152,11 +164,17 @@ rspec-ee system pg9:
|
|||
extends: .rspec-ee-base-pg9
|
||||
parallel: 5
|
||||
|
||||
rspec-ee migration pg10:
|
||||
extends:
|
||||
- .rspec-ee-base-pg10
|
||||
- .only-master
|
||||
parallel: 2
|
||||
|
||||
rspec-ee unit pg10:
|
||||
extends:
|
||||
- .rspec-ee-base-pg10
|
||||
- .only-master
|
||||
parallel: 7
|
||||
parallel: 5
|
||||
|
||||
rspec-ee integration pg10:
|
||||
extends:
|
||||
|
@ -239,6 +257,7 @@ static-analysis:
|
|||
dependencies: ["setup-test-env", "compile-assets pull-cache"]
|
||||
variables:
|
||||
SETUP_DB: "false"
|
||||
parallel: 2
|
||||
script:
|
||||
- scripts/static-analysis
|
||||
cache:
|
||||
|
@ -251,13 +270,8 @@ static-analysis:
|
|||
downtime_check:
|
||||
extends:
|
||||
- .rake-exec
|
||||
- .only-code-changes
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
- tags
|
||||
variables:
|
||||
- $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/
|
||||
- .only:changes-code-backstage
|
||||
- .except:refs-master-tags-stable-deploy
|
||||
stage: test
|
||||
needs: ["setup-test-env"]
|
||||
dependencies: ["setup-test-env"]
|
||||
|
|
22
.gitlab/ci/releases.gitlab-ci.yml
Normal file
22
.gitlab/ci/releases.gitlab-ci.yml
Normal file
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
|
||||
# Syncs any changes pushed to a stable branch to the corresponding CE stable
|
||||
# branch. We run this prior to any tests so that random failures don't prevent a
|
||||
# sync.
|
||||
sync-stable-branch:
|
||||
# We don't need/want any global before/after commands, so we overwrite these
|
||||
# settings.
|
||||
image: alpine:edge
|
||||
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:
|
||||
- apk add --no-cache --update curl bash
|
||||
after_script: []
|
||||
script:
|
||||
- bash scripts/sync-stable-branch.sh
|
|
@ -11,7 +11,7 @@ code_quality:
|
|||
extends:
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
stage: test
|
||||
image: docker:stable
|
||||
allow_failure: true
|
||||
|
@ -50,7 +50,7 @@ sast:
|
|||
extends:
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage-qa
|
||||
stage: test
|
||||
image: docker:stable
|
||||
variables:
|
||||
|
@ -132,7 +132,7 @@ dependency_scanning:
|
|||
extends:
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage-qa
|
||||
stage: test
|
||||
image: docker:stable
|
||||
variables:
|
||||
|
@ -195,7 +195,7 @@ dast:
|
|||
extends:
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-qa-changes
|
||||
- .only:changes-code-qa
|
||||
- .only-review
|
||||
stage: qa
|
||||
needs: ["review-deploy"]
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
.except-deploys:
|
||||
except:
|
||||
refs:
|
||||
- /^\d+-\d+-auto-deploy-\d+$/
|
||||
|
||||
.review-docker:
|
||||
extends:
|
||||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .except-deploys
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-qa-alpine
|
||||
services:
|
||||
- docker:19.03.0-dind
|
||||
|
@ -23,10 +17,9 @@
|
|||
build-qa-image:
|
||||
extends:
|
||||
- .review-docker
|
||||
- .only-code-qa-changes
|
||||
only:
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
|
||||
- .only:variables-canonical-dot-com
|
||||
- .except:refs-deploy
|
||||
- .only:changes-code-qa
|
||||
stage: prepare
|
||||
script:
|
||||
- '[[ ! -d "ee/" ]] || export GITLAB_EDITION="ee"'
|
||||
|
@ -35,14 +28,11 @@ build-qa-image:
|
|||
- echo "${CI_JOB_TOKEN}" | docker login --username gitlab-ci-token --password-stdin ${CI_REGISTRY}
|
||||
- time docker push ${QA_IMAGE}
|
||||
|
||||
schedule:review-cleanup:
|
||||
.base-review-cleanup:
|
||||
extends:
|
||||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-qa-changes
|
||||
- .only-review-schedules
|
||||
- .except-deploys
|
||||
stage: prepare
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
|
||||
allow_failure: true
|
||||
|
@ -55,11 +45,22 @@ schedule:review-cleanup:
|
|||
script:
|
||||
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
|
||||
|
||||
schedule:review-cleanup:
|
||||
extends:
|
||||
- .base-review-cleanup
|
||||
- .only-review-schedules
|
||||
|
||||
manual:review-cleanup:
|
||||
extends:
|
||||
- .base-review-cleanup
|
||||
- .only:changes-code-qa
|
||||
when: manual
|
||||
|
||||
.review-build-cng-base:
|
||||
extends:
|
||||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-qa-changes
|
||||
- .except-deploys
|
||||
image: ruby:2.6-alpine
|
||||
stage: review-prepare
|
||||
before_script:
|
||||
|
@ -74,6 +75,7 @@ review-build-cng:
|
|||
extends:
|
||||
- .review-build-cng-base
|
||||
- .only-review
|
||||
- .only:changes-code-qa
|
||||
needs: ["gitlab:assets:compile pull-cache"]
|
||||
|
||||
schedule:review-build-cng:
|
||||
|
@ -82,26 +84,30 @@ schedule:review-build-cng:
|
|||
- .only-review-schedules
|
||||
needs: ["gitlab:assets:compile pull-cache"]
|
||||
|
||||
.review-deploy-base:
|
||||
.review-workflow-base:
|
||||
extends:
|
||||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-qa-changes
|
||||
- .except-deploys
|
||||
stage: review
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
|
||||
dependencies: []
|
||||
allow_failure: true
|
||||
variables:
|
||||
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
|
||||
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
|
||||
GITLAB_HELM_CHART_REF: "v2.3.7"
|
||||
# v2.4.4 + two improvements:
|
||||
# - 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"
|
||||
environment:
|
||||
name: review/${CI_COMMIT_REF_NAME}
|
||||
url: https://gitlab-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}
|
||||
on_stop: review-stop
|
||||
|
||||
.review-deploy-base:
|
||||
extends: .review-workflow-base
|
||||
stage: review
|
||||
allow_failure: true
|
||||
before_script:
|
||||
- '[[ ! -d "ee/" ]] || export GITLAB_EDITION="ee"'
|
||||
- export GITLAB_SHELL_VERSION=$(<GITLAB_SHELL_VERSION)
|
||||
|
@ -112,21 +118,13 @@ schedule:review-build-cng:
|
|||
- install_api_client_dependencies_with_apk
|
||||
- source scripts/review_apps/review-apps.sh
|
||||
script:
|
||||
- date
|
||||
- check_kube_domain
|
||||
- date
|
||||
- ensure_namespace
|
||||
- date
|
||||
- install_tiller
|
||||
- date
|
||||
- install_external_dns
|
||||
- date
|
||||
- download_chart
|
||||
- date
|
||||
- deploy || (display_deployment_debug && exit 1)
|
||||
- date
|
||||
- add_license
|
||||
- date
|
||||
artifacts:
|
||||
paths: [review_app_url.txt]
|
||||
expire_in: 2 days
|
||||
|
@ -136,6 +134,7 @@ review-deploy:
|
|||
extends:
|
||||
- .review-deploy-base
|
||||
- .only-review
|
||||
- .only:changes-code-qa
|
||||
needs: ["review-build-cng"]
|
||||
|
||||
schedule:review-deploy:
|
||||
|
@ -144,11 +143,11 @@ schedule:review-deploy:
|
|||
- .only-review-schedules
|
||||
needs: ["schedule:review-build-cng"]
|
||||
|
||||
review-stop:
|
||||
.base-review-stop:
|
||||
extends:
|
||||
- .review-deploy-base
|
||||
- .review-workflow-base
|
||||
- .only-review
|
||||
when: manual
|
||||
- .only:changes-code-qa
|
||||
environment:
|
||||
action: stop
|
||||
variables:
|
||||
|
@ -161,24 +160,26 @@ review-stop:
|
|||
- wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/utils.sh
|
||||
- source utils.sh
|
||||
- source review-apps.sh
|
||||
script:
|
||||
- delete_release
|
||||
artifacts:
|
||||
paths: []
|
||||
|
||||
review-cleanup-failed-deployment:
|
||||
extends: review-stop
|
||||
review-stop-failed-deployment:
|
||||
extends: .base-review-stop
|
||||
stage: prepare
|
||||
when: on_success
|
||||
allow_failure: false
|
||||
script:
|
||||
- delete_failed_release
|
||||
|
||||
review-stop:
|
||||
extends: .base-review-stop
|
||||
stage: review
|
||||
when: manual
|
||||
allow_failure: true
|
||||
script:
|
||||
- delete_release
|
||||
|
||||
.review-qa-base:
|
||||
extends:
|
||||
- .review-docker
|
||||
- .only-review
|
||||
- .only-code-qa-changes
|
||||
- .only:changes-code-qa
|
||||
stage: qa
|
||||
allow_failure: true
|
||||
variables:
|
||||
|
@ -223,9 +224,7 @@ review-qa-all:
|
|||
- gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}" -- --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml --format html --out tmp/rspec.htm --color --format documentation
|
||||
|
||||
.review-performance-base:
|
||||
extends:
|
||||
- .review-docker
|
||||
- .only-code-qa-changes
|
||||
extends: .review-docker
|
||||
stage: qa
|
||||
allow_failure: true
|
||||
before_script:
|
||||
|
@ -248,6 +247,7 @@ review-performance:
|
|||
extends:
|
||||
- .review-performance-base
|
||||
- .only-review
|
||||
- .only:changes-code-qa
|
||||
needs: ["review-deploy"]
|
||||
dependencies: ["review-deploy"]
|
||||
before_script:
|
||||
|
@ -277,9 +277,8 @@ parallel-spec-reports:
|
|||
extends:
|
||||
- .default-tags
|
||||
- .default-only
|
||||
- .only-code-qa-changes
|
||||
- .only-review
|
||||
- .except-deploys
|
||||
- .only:changes-code-qa
|
||||
image: ruby:2.6-alpine
|
||||
stage: post-test
|
||||
dependencies: ["review-qa-all"]
|
||||
|
@ -310,18 +309,13 @@ danger-review:
|
|||
- .default-retry
|
||||
- .default-cache
|
||||
- .default-only
|
||||
- .except:refs-master-tags-stable-deploy
|
||||
image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
|
||||
stage: test
|
||||
dependencies: []
|
||||
only:
|
||||
variables:
|
||||
- $DANGER_GITLAB_API_TOKEN
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
variables:
|
||||
- $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/
|
||||
- $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/
|
||||
script:
|
||||
- git version
|
||||
- node --version
|
||||
|
|
|
@ -6,7 +6,8 @@ cache gems:
|
|||
- .default-retry
|
||||
- .default-cache
|
||||
- .default-before_script
|
||||
- .only-code-qa-changes
|
||||
- .only:variables-canonical-dot-com
|
||||
- .only:changes-code-backstage-qa
|
||||
stage: test
|
||||
dependencies: ["setup-test-env"]
|
||||
needs: ["setup-test-env"]
|
||||
|
@ -21,15 +22,13 @@ cache gems:
|
|||
refs:
|
||||
- master
|
||||
- tags
|
||||
variables:
|
||||
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
|
||||
|
||||
.minimal-job:
|
||||
extends:
|
||||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
dependencies: []
|
||||
|
||||
gitlab_git_test:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.tests-metadata-state:
|
||||
extends:
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
variables:
|
||||
TESTS_METADATA_S3_BUCKET: "gitlab-ce-cache"
|
||||
before_script:
|
||||
|
@ -48,7 +48,7 @@ flaky-examples-check:
|
|||
- .default-tags
|
||||
- .default-retry
|
||||
- .default-only
|
||||
- .only-code-changes
|
||||
- .only:changes-code-backstage
|
||||
image: ruby:2.6-alpine
|
||||
stage: post-test
|
||||
variables:
|
||||
|
|
|
@ -29,7 +29,7 @@ Set the title to: `Description of the original issue`
|
|||
|
||||
#### Documentation and final details
|
||||
|
||||
- [ ] Check the topic on #security to see when the next release is going to happen and add a link to the [links section](#links)
|
||||
- [ ] Check the topic on #releases to see when the next release is going to happen and add a link to the [links section](#links)
|
||||
- [ ] Add links to this issue and your MRs in the description of the security release issue
|
||||
- [ ] Find out the versions affected (the Git history of the files affected may help you with this) and add them to the [details section](#details)
|
||||
- [ ] Fill in any upgrade notes that users may need to take into account in the [details section](#details)
|
||||
|
|
|
@ -34,7 +34,7 @@ All reviewers can help ensure accuracy, clarity, completeness, and adherence to
|
|||
**3. Maintainer**
|
||||
|
||||
1. [ ] Review by assigned maintainer, who can always request/require the above reviews. Maintainer's review can occur before or after a technical writer review.
|
||||
1. [ ] Ensure a release milestone is set and that you merge the equivalent EE MR before the CE MR if both exist.
|
||||
1. [ ] Ensure a release milestone is set.
|
||||
1. [ ] If there has not been a technical writer review, [create an issue for one using the Doc Review template](https://gitlab.com/gitlab-org/gitlab/issues/new?issuable_template=Doc%20Review).
|
||||
|
||||
/label ~documentation
|
||||
|
|
|
@ -416,9 +416,6 @@ linters:
|
|||
- 'app/views/u2f/_register.html.haml'
|
||||
- 'app/views/users/_deletion_guidance.html.haml'
|
||||
- 'ee/app/views/admin/_namespace_plan_info.html.haml'
|
||||
- 'ee/app/views/admin/application_settings/_elasticsearch_form.html.haml'
|
||||
- 'ee/app/views/admin/application_settings/_slack.html.haml'
|
||||
- 'ee/app/views/admin/application_settings/_snowplow.html.haml'
|
||||
- 'ee/app/views/admin/application_settings/_templates.html.haml'
|
||||
- 'ee/app/views/admin/audit_logs/index.html.haml'
|
||||
- 'ee/app/views/admin/dashboard/stats.html.haml'
|
||||
|
@ -495,7 +492,6 @@ linters:
|
|||
- 'ee/app/views/projects/services/prometheus/_metrics.html.haml'
|
||||
- 'ee/app/views/projects/settings/slacks/edit.html.haml'
|
||||
- 'ee/app/views/shared/_additional_email_text.html.haml'
|
||||
- 'ee/app/views/shared/_geo_info_modal.html.haml'
|
||||
- 'ee/app/views/shared/_mirror_update_button.html.haml'
|
||||
- 'ee/app/views/shared/_shared_runners_minutes_limit.html.haml'
|
||||
- 'ee/app/views/shared/audit_events/_event_table.html.haml'
|
||||
|
|
|
@ -16,10 +16,25 @@
|
|||
# Uncomment the following lines to make the configuration take effect.
|
||||
|
||||
PreCommit:
|
||||
AuthorName:
|
||||
enabled: false
|
||||
EsLint:
|
||||
enabled: true
|
||||
# https://github.com/sds/overcommit/issues/338
|
||||
command: './node_modules/eslint/bin/eslint.js'
|
||||
HamlLint:
|
||||
enabled: true
|
||||
MergeConflicts:
|
||||
enabled: true
|
||||
exclude:
|
||||
- '**/conflict/file_spec.rb'
|
||||
- '**/git/conflict/parser_spec.rb'
|
||||
# prettier? https://github.com/sds/overcommit/issues/614 https://github.com/sds/overcommit/issues/390#issuecomment-495703284
|
||||
RuboCop:
|
||||
enabled: true
|
||||
# on_warn: fail # Treat all warnings as failures
|
||||
#
|
||||
ScssLint:
|
||||
enabled: true
|
||||
#PostCheckout:
|
||||
# ALL: # Special hook name that customizes all hooks of this type
|
||||
# quiet: true # Change all post-checkout hooks to only display output on failure
|
||||
|
|
|
@ -56,7 +56,7 @@ Style/FrozenStringLiteralComment:
|
|||
- 'qa/**/*'
|
||||
- 'rubocop/**/*'
|
||||
- 'scripts/**/*'
|
||||
- 'spec/**/*'
|
||||
- 'spec/lib/gitlab/**/*'
|
||||
|
||||
RSpec/FilePath:
|
||||
Exclude:
|
||||
|
@ -297,3 +297,6 @@ Graphql/Descriptions:
|
|||
Include:
|
||||
- 'app/graphql/**/*'
|
||||
- 'ee/app/graphql/**/*'
|
||||
|
||||
RSpec/AnyInstanceOf:
|
||||
Enabled: false
|
||||
|
|
|
@ -401,13 +401,6 @@ Rails/FilePath:
|
|||
Rails/HasManyOrHasOneDependent:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 40
|
||||
# Cop supports --auto-correct.
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: numeric, symbolic
|
||||
Rails/HttpStatus:
|
||||
Enabled: false
|
||||
|
||||
# Offense count: 2
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/controllers/**/*.rb
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
Please view this file on the master branch, on stable branches it's out of date.
|
||||
|
||||
## 12.4.5
|
||||
## 12.5.3
|
||||
|
||||
### Performance (1 change)
|
||||
|
||||
- Geo - Improve query performance to determine job artifacts to sync when selective sync is enabled. !19583
|
||||
|
||||
### Other (1 change)
|
||||
|
||||
- Geo - Does not schedule duplicated jobs while backfilling uploads, LFS objects and job artifacts. !20324
|
||||
|
||||
|
||||
## 12.5.2
|
||||
|
||||
- No changes.
|
||||
|
||||
## 12.4.4
|
||||
## 12.5.1
|
||||
|
||||
### Security (6 changes)
|
||||
|
||||
|
@ -16,12 +27,90 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
- Prevent IDOR when adding users to protected environments.
|
||||
|
||||
|
||||
## 12.4.3
|
||||
## 12.5.0
|
||||
|
||||
### Fixed (2 changes)
|
||||
### Security (5 changes)
|
||||
|
||||
- Fixes a Open Redirect issue in `InternalRedirect`.
|
||||
- Filter out packages the user does'nt have permission to see at group level.
|
||||
- Do not show private cross references in epic notes.
|
||||
- Redact search results based on Ability.allowed?.
|
||||
- Do not index system notes for issue update.
|
||||
|
||||
### Removed (2 changes, 1 of them is from the community)
|
||||
|
||||
- Remove the Geo Clone Modal. !18897 (Zack Cuddy)
|
||||
- Remove Pendo Snippet. !19400
|
||||
|
||||
### Fixed (17 changes)
|
||||
|
||||
- Fix notification button size in notification settings. !16672
|
||||
- Don't store full blob path in ES filename field. !18470
|
||||
- Add messages to warn and stop users when attempting to change the path of projects with NPM packages. !18515
|
||||
- Pass pipeline variables when expanding Bridge downstream variables. !18875
|
||||
- Fix equality operator for Prometheus alerts. !18919
|
||||
- Fix rake task to rollback Geo migrations. !18975
|
||||
- Default current user to mirror user when creating pipelines for GitHub pull requests. !19072
|
||||
- Fix overlapping `Skip Trial` block. !19218
|
||||
- Fix Dependency List is empty if last pipeline is retried. !19241
|
||||
- SCIM pagination startIndex handles string input. !19331
|
||||
- Display packages with multiple licenses. !19333
|
||||
- Expose commit sha on Vulnerabilities::Occurrence. !19668
|
||||
- Fix admin welcome image not found. !19676
|
||||
- Revert ES support for public/internal project snippets. !19715
|
||||
- Updated View documentation link on cluster page. !19780
|
||||
- Enable pod logs nav menu only for maintainers in projects with k8s environments. !19927
|
||||
- Hide labels from issue board cards. !20072
|
||||
|
||||
### Changed (13 changes)
|
||||
|
||||
- Inherit children epics start and due dates. !14366
|
||||
- Update the frontend diffing code to support v2 license scan reports. !18105
|
||||
- Implement pod logs page using Vue. !18567
|
||||
- Move DAST reports logic for the Merge Request widget to the backend. !18660
|
||||
- Add created_before/after filter to audit events. !19035
|
||||
- Get rid of unnecessary duplication of alert’s title from Alert Details. !19214
|
||||
- Hashed storage is now a requirement for Design Management. !19259
|
||||
- Expose epic in issues API. !19300
|
||||
- SCIM GET /Users supports requests without a filter. !19421
|
||||
- Enable Cycle Analytics Feature by default. !19484
|
||||
- Enforce a max size accepted for sentry issues list. !19649
|
||||
- Limit input size for Prometheus alert JSON payload. !19940
|
||||
- Adds in a URL field for DAST reports modal data. !20162
|
||||
|
||||
### Performance (2 changes)
|
||||
|
||||
- Fix new project page load performance. !18180
|
||||
- Geo - Improve query performance to determine LFS objects to sync when selective sync is enabled. !19051
|
||||
|
||||
### Added (17 changes, 1 of them is from the community)
|
||||
|
||||
- Add filter for dismissed vulnerabilities on security dashboards. !16692
|
||||
- Data API endpoint for tasks by type chart within the analytics workspace. !17944
|
||||
- Hide labels from issue board cards. !18533
|
||||
- Skip Onboarding feedback when tracking is disabled. !18671
|
||||
- API endpoint to list the packages of a group. !18871
|
||||
- Allow to create epics with GraphQL. !19030
|
||||
- CI_JOB_TOKEN can be accepted with 'Bearer ' prefix to allow for NPM registry usage. !19059
|
||||
- Add issue IID to a title of generic alerts with a default title. !19086
|
||||
- Update sidebar to differentiate between groups, subgroups, and projects. !19158
|
||||
- SCIM can be used to manage group membership. !19329
|
||||
- Expose number of sub-epics and epic issues in GraphQL API. !19450
|
||||
- Add logs menu item to the sidebar. !19471
|
||||
- Add public API for Feature Flags. !19547
|
||||
- Ignore project_ci_cd_settings.merge_trains_enabled column. !19695
|
||||
- Add a usage ping metric for number of activated Alert Services. !19765
|
||||
- New discussions on designs will generate a system note on the issue. !19990
|
||||
- Expose SHA of squashed commit via API when fast-forward merge is enabled. (minghuan lei)
|
||||
|
||||
### Other (6 changes, 1 of them is from the community)
|
||||
|
||||
- Migrated contributors charts to echarts. !16677
|
||||
- Added autogenerated Markdown support for Vulnerability title and description. !18283
|
||||
- Rename user_id to author_id in design_management_versions table. !18506
|
||||
- Revert notification for updated privacy policy. !18900
|
||||
- Remove plaintext tokens for feature flags clients. !18923
|
||||
- Remove IIFEs from jira_connect.js file. !19248 (nuwe1)
|
||||
|
||||
|
||||
## 12.4.2
|
||||
|
@ -4205,7 +4294,7 @@ Please view this file on the master branch, on stable branches it's out of date.
|
|||
- Show hook errors for fast-forward merges. !1375
|
||||
- Allow all parameters of group webhooks to be set through the UI. !1376
|
||||
- Fix Elasticsearch queries when a group_id is specified. !1423
|
||||
- Check the right index mapping based on Rails environment for rake gitlab:elastic:add_feature_visiblity_levels_to_project. !1473
|
||||
- Check the right index mapping based on Rails environment for rake gitlab:elastic:add_feature_visibility_levels_to_project. !1473
|
||||
- Fix issues with another milestone that has a matching list label could not be added to a board.
|
||||
- Only admins or group owners can set LDAP overrides.
|
||||
- Add support for load balancing database queries.
|
||||
|
|
379
CHANGELOG.md
379
CHANGELOG.md
|
@ -2,38 +2,397 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 12.4.6
|
||||
## 12.5.4
|
||||
|
||||
- No changes.
|
||||
|
||||
## 12.4.5
|
||||
## 12.5.3
|
||||
|
||||
- No changes.
|
||||
### Fixed (4 changes)
|
||||
|
||||
## 12.4.4
|
||||
- Fix project creation with templates using /projects/user/:id API. !20590
|
||||
- Fix merging merge requests from push options. !20639
|
||||
- Fix Crossplane help link in cluster applications page. !20668
|
||||
- Fixes job log not scrolling to the bottom.
|
||||
|
||||
### Security (12 changes)
|
||||
### Changed (1 change)
|
||||
|
||||
- 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
|
||||
|
||||
### Security (11 changes)
|
||||
|
||||
- Do not create todos for approvers without access. !1442
|
||||
- Limit potential for DNS rebind SSRF in chat notifications.
|
||||
- Hide commit counts from guest users in Cycle Analytics.
|
||||
- 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.
|
||||
- Limit potential for DNS rebind SSRF in chat notifications.
|
||||
- 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.4.3
|
||||
## 12.5.0
|
||||
|
||||
### Fixed (2 changes)
|
||||
### Security (15 changes)
|
||||
|
||||
- Enable the HttpOnly flag for experimentation_subject_id cookie. !19189
|
||||
- Update incrementing of failed logins to be thread-safe. !19614
|
||||
- Sanitize all wiki markup formats with GitLab sanitization pipelines.
|
||||
- Sanitize search text to prevent XSS.
|
||||
- Remove deploy access level when project/group link is deleted.
|
||||
- Mask sentry auth token in Error Tracking dashboard.
|
||||
- Return 404 on LFS request if project doesn't exist.
|
||||
- Don't leak private members in project member autocomplete suggestions.
|
||||
- Require Maintainer permission on group where project is transferred to.
|
||||
- Don't allow maintainers of a target project to delete the source branch of a merge request from a fork.
|
||||
- Disallow unprivileged users from commenting on private repository commits.
|
||||
- Analyze incoming GraphQL queries and check for recursion.
|
||||
- Show cross-referenced label and milestones in issues' activities only to authorized users.
|
||||
- Do not display project labels that are not visible for user accessing group labels.
|
||||
- Standardize error response when route is missing.
|
||||
|
||||
### Fixed (99 changes, 14 of them are from the community)
|
||||
|
||||
- Fix incorrect selection of custom templates. !17205
|
||||
- Smaller width for design comments layout, truncate image title. !17547
|
||||
- Correctly cleanup orphan job artifacts. !17679 (Adam Mulvany)
|
||||
- Add Infinite scroll to Add Projects modal in the operations dashboard. !17842
|
||||
- Allow emojis to be linkable. !18014
|
||||
- Enable image link and lazy loading in AsciiDoc documents. !18164 (Guillaume Grossetie)
|
||||
- Expose prometheus status to monitor dashboard. !18289
|
||||
- Time limit the database lock when rebasing a merge request. !18481
|
||||
- Fix missing admin mode UI buttons on bigger screen sizes. !18585 (Diego Louzán)
|
||||
- Abort only MWPS when FF only merge is impossible. !18591
|
||||
- Remove pointer cursor from MemoryUsage chart on MR widget deployment. !18599
|
||||
- Fix keyboard shortcuts in header search autocomplete. !18685
|
||||
- Fix empty chart in collapsed sections. !18699
|
||||
- Fix error when viewing group billing page. !18740
|
||||
- Fix query validation in custom metrics form. !18769
|
||||
- Fix Gitaly call duration measurements. !18785
|
||||
- Resolve Error when uploading a few designs in a row. !18811
|
||||
- Block MR with OMIPS on skipped pipelines. !18838
|
||||
- Pipeline vulnerability dashboard sort vulnerabilities by severity then confidence. !18863
|
||||
- Remove empty Github service templates from database. !18868
|
||||
- Fix broken images when previewing markdown files in Web IDE. !18899
|
||||
- fixed #27164 Image cannot be collapsed on merge request changes tab. !18917 (Jannik Lehmann)
|
||||
- Let ANSI \r code replace the current job log line. !18933
|
||||
- Fix serverless function descriptions not showing on Knative 0.7. !18973
|
||||
- Fix "project or group was moved" alerts showing up in the wrong pages. !18985
|
||||
- Add missing breadcrumb in Project > Settings > Integrations. !18990
|
||||
- Fixed admin geo collapsed sidebar fly out not showing. !19012
|
||||
- Serialize short sha as nil if head commit is blank. !19014
|
||||
- Add max width on manifest file attachment input. !19028
|
||||
- Do not generate To-Dos additional when editing group mentions. !19037
|
||||
- Fix previewing quick actions for epics. !19042
|
||||
- Fix errors in GraphQL Todos API due to missing TargetTypeEnum values. !19052
|
||||
- Hashed Storage Migration: Handle failed attachment migrations with existing target path. !19061
|
||||
- Set shorter TTL for all unauthenticated requests. !19064
|
||||
- Fix Todo IDs in GraphQL API. !19068
|
||||
- Triggers the correct endpoint on licence approval. !19078
|
||||
- Fix search button height on 404 page. !19080
|
||||
- Fix Kubernetes help text link. !19121
|
||||
- Make `jobs/request` to be resillient. !19150
|
||||
- Disable pull mirror if repository is in read-only state. !19182
|
||||
- Only enable protected paths for POST requests. !19184
|
||||
- Enforce default, global project and snippet visibilities. !19188
|
||||
- Make Bitbucket Cloud superseded pull requests as closed. !19193
|
||||
- Fix crash when docker fails deleting tags. !19208
|
||||
- Fix environment name in rollback dialog. !19209
|
||||
- Fixed a typo in the "Keyboard Shortcuts" pop-up. !19217 (Manuel Stein)
|
||||
- Fix unable to expand or collapse files in merge request by clicking caret. !19222 (Brian T)
|
||||
- Allow release block edit button to be visible. !19226
|
||||
- Fix double escaping in /tableflip quick action. !19271 (Brian T)
|
||||
- Add missing bottom padding in CI/CD settings. !19284 (George Tsiolis)
|
||||
- Prevents console warning on design upload. !19297
|
||||
- Resolve: Web IDE does not create POSIX Compliant Files. !19339
|
||||
- Use initial commit SHA instead of branch id to request IDE files and contents. !19348 (David Palubin)
|
||||
- Resolve: Web IDE Throws Error When Viewing Diff for Renamed Files. !19348
|
||||
- Fix project service API 500 error. !19367
|
||||
- Fix cluster feature highlight popover image. !19372
|
||||
- Fix template selector filename bug. !19376
|
||||
- Fixes mobile styling issues on security modals. !19391
|
||||
- Only move repos for legacy project storage. !19410
|
||||
- Show correct total number of commit diff's changes. !19424
|
||||
- Increase the timeout for GitLab-managed cert-manager installation to 90 seconds (was 30 seconds). !19447
|
||||
- Fix uninitialized constant SystemDashboardService. !19453
|
||||
- Properly handle exceptions in StuckCiJobsWorker. !19465
|
||||
- Fix user popover not being displayed when the user has a status message. !19519
|
||||
- Update omniauth_openid_connect to v0.3.3. !19525
|
||||
- Fix project clone dropdown button width. !19551 (George Tsiolis)
|
||||
- Do not escape HTML tags in Ansi2json as they are escaped in the frontend. !19610
|
||||
- [Geo] Fix: undefined Gitlab::BackgroundMigration::PruneOrphanedGeoEvents. !19638
|
||||
- Revert btn-xs styling in projects scss. !19640
|
||||
- Fix canary badge and favicon inconsistency. !19645
|
||||
- Use fingerprint when comparing security reports in MR widget. !19654
|
||||
- Update GCP credit URLs. !19683
|
||||
- Update squash_commit_sha only on successful merge. !19688
|
||||
- Fix import of snippets having `award_emoji` (Project Export/Import). !19690
|
||||
- Allow admins to administer personal snippets. !19693 (Oren Kanner)
|
||||
- Re-add missing file sizes in 2-Up diff file viewer. !19710
|
||||
- Fix checking task item when previous tasks contain only spaces. !19724
|
||||
- Fix Bitbucket Cloud importer pull request state. !19734
|
||||
- Fix merge train is not refreshed when the system aborts/drops a merge request. !19763
|
||||
- Resolve Hide Delete selected in designs when viewing an old version. !19889
|
||||
- Use new trial registration URL in billing. !19978
|
||||
- Helm v2.16.1. !19981
|
||||
- Ensure milestone titles are never empty. !19985
|
||||
- Remove unused image/screenshot. !20030 (Lee Tickett)
|
||||
- Remove local qualifier from geo sync indicators. !20034 (Lee Tickett)
|
||||
- Fixed the scale of embedded videos to fit the page. !20056
|
||||
- Fix broken monitor cluster health dashboard. !20120
|
||||
- Fix expanding collapsed threads when reference link clicked. !20148
|
||||
- Fix sub group export to export direct children. !20172
|
||||
- Remove update hook from date filter to prevent js from getting stuck. !20215
|
||||
- Prevent Dropzone.js initialisation error by checking target element existence. !20256 (Fabio Huser)
|
||||
- Fix style reset in job log when empty ANSI sequence is encoutered. !20367
|
||||
- Add productivity analytics merge date filtering limit. !32052
|
||||
- Fix productivity analytics listing with multiple labels. !33182
|
||||
- Fix closed board list loading issue.
|
||||
- Apply correctly the limit of 10 designs per upload.
|
||||
- Only allow confirmed users to run pipelines.
|
||||
- Fix scroll to bottom with new job log.
|
||||
- Fixed protected branches flash styling.
|
||||
|
||||
### Deprecated (2 changes)
|
||||
|
||||
- Ignore deprecated column and remove references to it. !18911
|
||||
- Move some project routes under - scope. !19954
|
||||
|
||||
### Changed (56 changes, 6 of them are from the community)
|
||||
|
||||
- Upgrade design/copy for issue weights locked feature. !17352
|
||||
- Reduce new MR page redundancy by moving the source/target branch selector to the top. !17559
|
||||
- Replace raven-js with @sentry/browser. !17715
|
||||
- Ask if the user is setting up GitLab for a company during signup. !17999
|
||||
- When a user views a file's blame or blob and switches to a branch where the current file does not exist, they will now be redirected to the root of the repository. !18169 (Jesse Hall @jessehall3)
|
||||
- Propagate custom environment variables to SAST analyzers. !18193
|
||||
- Fix any approver project rule records. !18265
|
||||
- Minor UX improvements to Environments Dashboard page. !18280
|
||||
- Reduce the allocated IP for Cluster and Services. !18341
|
||||
- Update flash messages color sitewide. !18369
|
||||
- Add modsecurity template for ingress-controller. !18485
|
||||
- Hide projects without access to admin user when admin mode is disabled. !18530 (Diego Louzán)
|
||||
- Update Runners Settings Text + Link to Docs. !18534
|
||||
- Store Zoom URLs in a table rather than in the issue description. !18620
|
||||
- Improve admin dashboard features. !18666
|
||||
- Drop `id` column from `ci_build_trace_sections` table. !18741
|
||||
- Truncate recommended branch name to a sane length. !18821
|
||||
- Add support for YAML anchors in CI scripts. !18849
|
||||
- Save dashboard changes by the user into the vuex store. !18862
|
||||
- Update expired trial status copy. !18962
|
||||
- Can directly add approvers to approval rule. !18965
|
||||
- Rename Vulnerabilities API to Vulnerability Findings API. !19029
|
||||
- Improve clarity of text for merge train position. !19031
|
||||
- Updated Auto-DevOps to kubectl v1.13.12 and helm v2.15.1. !19054 (Leo Antunes)
|
||||
- Refactor maximum user counts in license. !19071 (briankabiro)
|
||||
- Change return type of getDateInPast to Date. !19081
|
||||
- Show approval required status in license compliance. !19114
|
||||
- Handle new Container Scanning report format. !19123
|
||||
- Allow container scanning to run offline by specifying the Clair DB image to use. !19161
|
||||
- Add maven cli opts flag to maven security analyzer (part of dependency scanning). !19174
|
||||
- Added report_type attribute to Vulnerabilities. !19179
|
||||
- Migrate enabled flag on grafana_integrations table. !19234
|
||||
- Improve handling of gpg-agent processes. !19311
|
||||
- Update help text of "Tag name" field on Edit Release page. !19321
|
||||
- Add user filtering to abuse reports page. !19365
|
||||
- Move add license button to project buttons. !19370
|
||||
- Update to Mermaid v8.4.2 to support more graph types. !19444
|
||||
- Move release meta-data into footer on Releases page. !19451
|
||||
- Expose subscribed field in issue lists queried with GraphQL. !19458 (briankabiro)
|
||||
- [Geo] Fix: rake gitlab:geo:check on the primary is cluttered. !19460
|
||||
- Hide trial banner for namespaces with expired trials. !19510
|
||||
- Hide repeated trial offers on self-hosted instances. !19511
|
||||
- Add loading icon to error tracking settings page. !19539
|
||||
- Upgrade to Gitaly v1.71.0. !19611
|
||||
- Make role required when editing profile. !19636
|
||||
- Made `name` optional parameter of Release entity. !19705
|
||||
- Vulnerabilities history chart - use sparklines. !19745
|
||||
- Add event tracking to container registry. !19772
|
||||
- Update SaaS trial header to include the tier Gold. !19970
|
||||
- Update start a trial option in top right drop down to include Gold. !19971
|
||||
- Improve merge request description placeholder. !20032 (Jacopo Beschi @jacopo-beschi)
|
||||
- Add backtrace to production_json.log. !20122
|
||||
- Change the default concurrency factor of merge train to 20. !20201
|
||||
- Upgrade to Gitaly v1.72.0.
|
||||
- Require explicit null parameters to remove pages domain certificate and allow to use Let's Encrypt certificates through API.
|
||||
- Replace wording trace with log.
|
||||
|
||||
### Performance (13 changes)
|
||||
|
||||
- Record latencies for Sidekiq failures. !18909
|
||||
- Fix N+1 for group container repositories view. !18979
|
||||
- Do not render links in commit message on blame page. !19128
|
||||
- Puma only: database connection pool now always >= number of worker threads. !19286
|
||||
- Run check_mergeability only if merge status requires it. !19364
|
||||
- Execute limited request for diff commits instead of preloading. !19485
|
||||
- Improve performance of admin/abuse_reports page. !19630
|
||||
- Remove N+1 DB calls from branches API. !19661
|
||||
- Improve performance of linking LFS objects during import. !19709
|
||||
- Optimize MergeRequest#mergeable_discussions_state? method. !19988
|
||||
- Add index for unauthenticated requests to projects API default endpoint. !19989
|
||||
- Add index for authenticated requests to projects API default endpoint. !19993
|
||||
- Increase PumaWorkerKiller memory limit in development environment. !20039
|
||||
|
||||
### Added (83 changes, 8 of them are from the community)
|
||||
|
||||
- Adds Application Settings and ui settings in the integration admin area for Pendo. !15086
|
||||
- Add endpoint for a group's vulnerable projects. !15317
|
||||
- Added new chart component to display an anomaly boundary. !16530
|
||||
- Add links to associated releases on the Milestones page. !16558
|
||||
- Merge Details Page and Edit Page for Page Domains. !16687
|
||||
- Share groups with groups. !17117
|
||||
- Add links to associated release(s) to the milestone detail page. !17278
|
||||
- New group path uniqueness check. !17394
|
||||
- Unify html email layout for member html emails. !17699 (Diego Louzán)
|
||||
- The Security Dashboard displays DAST vulnerabilities for all the scanned sites, not just the first. !17779
|
||||
- Create table for elastic stack. !18015
|
||||
- Allow to define a default CI configuration path for new projects. !18073 (Mathieu Parent)
|
||||
- Issues queried in GraphQL now sortable by due date. !18094
|
||||
- Add cleanup status to clusters. !18144
|
||||
- Added Tests tab to pipeline detail that contains a UI for browsing test reports produced by JUnit. !18255
|
||||
- Users can verify SAML configuration and view SamlResponse XML. !18362
|
||||
- Support Enable/Disable operations in Feature Flag API. !18368
|
||||
- Expose arbitrary job artifacts in Merge Request widget. !18385
|
||||
- Add project option for deleting source branch. !18408 (Zsolt Kovari)
|
||||
- Adds ability to set management project for cluster via API. !18429
|
||||
- Close issues on Prometheus alert recovery. !18431
|
||||
- Add ApplicationSetting for snowplow_iglu_registry_url. !18449
|
||||
- Allow Grafana charts to be embedded in Gitlab Flavored Markdown. !18486
|
||||
- Mark todo done by GraphQL API. !18581
|
||||
- Create a users_security_dashboard_projects table to store the projects a user has added to their personal security dashboard. !18708
|
||||
- New API endpoint for creating anonymous merge request discussions from Visual Review Tools. !18710
|
||||
- Enable the color chip in AsciiDoc documents. !18723
|
||||
- Add prevent_ldap_sign_in option so LDAP can be used exclusively for sync. !18749
|
||||
- Show inherited group variables in project view. !18759
|
||||
- Add "release" filter to issue search page. !18761
|
||||
- Search list of Sentry errors by title in Gitlab. !18772
|
||||
- Add migrations and changes for soft-delete for projects. !18791
|
||||
- Support for Crossplane as a managed app. !18797 (Mahendra Bagul)
|
||||
- Bump Auto-Deploy image to v0.3.0. !18809
|
||||
- Set X-GitLab-NotificationReason header if notification reason is explicit subscription. !18812
|
||||
- Add issues, MRs, participants, and labels tabs in group milestone page. !18818
|
||||
- Add ability to reorder projects on operations dashboard. !18855
|
||||
- Make `Job`, `Bridge` and `Default` inheritable. !18867
|
||||
- Show epic events on group activity page. !18869
|
||||
- Detail view of Sentry error in GitLab. !18878
|
||||
- Expose mergeable state of a merge request. !18888 (briankabiro)
|
||||
- Add ability to select a Cluster management project. !18928
|
||||
- Add a Slack slash command to add a comment to an issue. !18946
|
||||
- Added installation commands for npm and yarn packages to package detail page. !18999
|
||||
- Show start and end dates in Epics list page. !19006
|
||||
- Populate new pipeline CI vars from params. !19023
|
||||
- Add warnings about pages access control settings. !19067
|
||||
- Graphql mutation for (un)subscribing to an epic. !19083
|
||||
- API for stack trace & detail view of Sentry error in GitLab. !19137
|
||||
- Add grafana integration active status checkbox. !19255
|
||||
- GraphQL: Add Merge Request milestone mutation. !19257
|
||||
- Add MergeRequestSetAssignees GraphQL mutation. !19272
|
||||
- Add edit button to metrics dashboard. !19279
|
||||
- Add "release" filter to merge request search page. !19315
|
||||
- Add dead jobs to Sidekiq metrics API. !19350 (Marco Peterseil)
|
||||
- Add pipeline information to dependency list header. !19352
|
||||
- Build CI cache key from commit SHAs that changed given files. !19392
|
||||
- Adding support for searching tags using '^' and '$'. !19435 (Cauhx Milloy)
|
||||
- Sentry error stacktrace. !19492
|
||||
- Add an `error_code` attribute to the API response when a cherry-pick or revert fails. !19518
|
||||
- Add documentation for sign-in application setting. !19561 (Horatiu Eugen Vlad)
|
||||
- Create AWS EKS cluster. !19578
|
||||
- Add modsecurity logging sidecar to ingress controller. !19600
|
||||
- Add start a trial option in the top-right user dropdown. !19632
|
||||
- Manage and display labels from epic in the GraphQL API. !19642
|
||||
- Allow order_by updated_at in Deployments API. !19658
|
||||
- Add can_edit and project_blob_path to metrics_dashboard endpoint. !19663
|
||||
- Add usage ping data for project services. !19687
|
||||
- Graphql query for issues can now be sorted by relative_position. !19713
|
||||
- Add API endpoint to trigger Group Structure Export. !19779
|
||||
- Show Tree UI containing child Epics and Issues within an Epic. !19812
|
||||
- Enable environments dashboard by default. !19838
|
||||
- Update the DB schema to allow linking between Vulnerabilities and Issues. !19852
|
||||
- Add Group Audit Events API. !19868
|
||||
- Adds a copy button next to package metadata on the details page. !19881
|
||||
- GraphQL: Create MR mutations needed for the sidebar. !19913
|
||||
- Add id_before, id_after filter param to projects API. !19949
|
||||
- Add modsecurity feature flag to usage ping. !20194
|
||||
- Specify management project for a Kubernetes cluster. !20216
|
||||
- Upgrade pages to 1.12.0. !20217
|
||||
- Support template_project_id parameter in project creation API. !20258
|
||||
- Add heatmap chart support. !32424
|
||||
- Add template for Serverless Framework/JS. !33805
|
||||
|
||||
### Other (59 changes, 26 of them are from the community)
|
||||
|
||||
- Add EKS cluster count to usage data. !17059
|
||||
- Track the starting and stopping of the current signup flow and the experimental signup flow. !17521
|
||||
- Attribute Sidekiq workers according to their workloads. !18066
|
||||
- Add ApplicationSetting entries for EKS integration. !18307
|
||||
- Geo: Add resigns-related fields to Geo Node Status table. !18379
|
||||
- Allow adding requests to performance bar manually. !18464
|
||||
- Removes `export_designs` feature flag. !18507 (nate geslin)
|
||||
- Update AWS SDK to 2.11.374. !18601
|
||||
- Remove required dependecy of Postgresql for Gitaly. !18659
|
||||
- Add deployment_merge_requests table. !18755
|
||||
- Bump Gitaly to 1.70.0 and remove cache invalidation feature flag. !18766
|
||||
- Update gRPC to v1.24.0. !18837
|
||||
- Update GitLab Runner Helm Chart to 0.10.0. !18879
|
||||
- Adds a Sidekiq queue duration metric. !19005
|
||||
- Create explicit Default and Free plans. !19033
|
||||
- Improve instance mirroring help text. !19047
|
||||
- Add Codesandbox metrics to usage ping. !19075
|
||||
- Add internal_socket_dir to gitaly config in setup helper. !19170
|
||||
- Use Rails 5.2 Redis caching store. !19202
|
||||
- Update GitLab Runner Helm Chart to 0.10.1. !19232
|
||||
- Rename snowplow_site_id to snowplow_app_id in application_settings table. !19252
|
||||
- Removed IIFEs from network.js file. !19254 (nuwe1)
|
||||
- Remove IIFEs from project_select.js. !19288 (minghuan lei)
|
||||
- Remove IIFEs from merge_request.js. !19294 (minghuan lei)
|
||||
- Make snippet list easier to scan. !19490
|
||||
- Removed IIFEs from image_file.js. !19548 (nuwe1)
|
||||
- Fix api docs for deleting project cluster. !19558
|
||||
- Change blob edit view button styling. !19566
|
||||
- Include exception and backtrace in API logs. !19671
|
||||
- Add index on marked_for_deletion_at in projects table. !19788
|
||||
- Visual design for edit buttons in blob view. !19932
|
||||
- Refactor disabled sidebar notifications to Vue. !20007 (minghuan lei)
|
||||
- Remove IIFEs from branch_graph.js. !20008 (minghuan lei)
|
||||
- Remove IIFEs from new_branch_form.js. !20009 (minghuan lei)
|
||||
- Remove duplication from slugifyWithUnderscore function. !20016 (Arun Kumar Mohan)
|
||||
- Update registry.gitlab.com/gitlab-org/security-products/codequality to 12-5-stable. !20046 (Takuya Noguchi)
|
||||
- Add mb-2 class to global alerts. !20081 (2knal)
|
||||
- Remove var from syntax_highlight_spec.js. !20086 (Lee Tickett)
|
||||
- Remove var from merge_request_tabs_spec.js. !20087 (Lee Tickett)
|
||||
- Remove var from bootstrap_jquery_spec.js. !20089 (Lee Tickett)
|
||||
- Remove var from project_select.js. !20091 (Lee Tickett)
|
||||
- Remove var from new_commit_form.js. !20095 (Lee Tickett)
|
||||
- Remove var from issue.js. !20098 (Lee Tickett)
|
||||
- Remove var from new_branch_form.js. !20099 (Lee Tickett)
|
||||
- Remove var from tree.js. !20103 (Lee Tickett)
|
||||
- Remove var from line_highlighter.js. !20108 (Lee Tickett)
|
||||
- Remove var from preview_markdown.js. !20115 (Lee Tickett)
|
||||
- remove all references of BoardService in boards_selector.vue. !20147 (nuwe1)
|
||||
- Remove all references to BoardsService in index.vue. !20152 (nuwe1)
|
||||
- Remove var from labels_select.js. !20153 (Lee Tickett)
|
||||
- Remove all reference to BoardService in board_form.vue. !20158 (nuwe1)
|
||||
- Remove calendar icon from personal access tokens. !20183
|
||||
- Move margin-top from flash container to flash. !20211
|
||||
- Bump Auto DevOps deploy image to v0.7.0. !20250
|
||||
- Make 'Sidekiq::Testing.fake!' mode as default. !31662 (@blackst0ne)
|
||||
- Replace task-done icon with list-task icon to better align with other toolbar list icons.
|
||||
- Dependency Scanning template that doesn't rely on Docker-in-Docker.
|
||||
- Adding dropdown arrow icon and updated text alignment.
|
||||
- Change selects from default browser style to custom style.
|
||||
|
||||
|
||||
## 12.4.2
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.67.1
|
||||
1.72.1
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.4.0
|
||||
1.5.0
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.11.0
|
||||
1.12.0
|
||||
|
|
47
Gemfile
47
Gemfile
|
@ -8,12 +8,12 @@ gem 'bootsnap', '~> 1.4'
|
|||
gem 'nakayoshi_fork', '~> 0.0.4'
|
||||
|
||||
# Responders respond_to and respond_with
|
||||
gem 'responders', '~> 2.0'
|
||||
gem 'responders', '~> 3.0'
|
||||
|
||||
gem 'sprockets', '~> 3.7.0'
|
||||
|
||||
# Default values for AR models
|
||||
gem 'default_value_for', '~> 3.2.0'
|
||||
gem 'default_value_for', '~> 3.3.0'
|
||||
|
||||
# Supported DBs
|
||||
gem 'pg', '~> 1.1'
|
||||
|
@ -42,7 +42,7 @@ gem 'omniauth-shibboleth', '~> 1.3.0'
|
|||
gem 'omniauth-twitter', '~> 1.4'
|
||||
gem 'omniauth_crowd', '~> 2.2.0'
|
||||
gem 'omniauth-authentiq', '~> 0.3.3'
|
||||
gem 'omniauth_openid_connect', '~> 0.3.1'
|
||||
gem 'omniauth_openid_connect', '~> 0.3.3'
|
||||
gem "omniauth-ultraauth", '~> 0.0.2'
|
||||
gem 'omniauth-salesforce', '~> 1.0.5'
|
||||
gem 'rack-oauth2', '~> 1.9.3'
|
||||
|
@ -64,7 +64,7 @@ gem 'u2f', '~> 0.2.1'
|
|||
|
||||
# GitLab Pages
|
||||
gem 'validates_hostname', '~> 1.0.6'
|
||||
gem 'rubyzip', '~> 1.2.2', require: 'zip'
|
||||
gem 'rubyzip', '~> 1.3.0', require: 'zip'
|
||||
# GitLab Pages letsencrypt support
|
||||
gem 'acme-client', '~> 2.0.2'
|
||||
|
||||
|
@ -72,7 +72,7 @@ gem 'acme-client', '~> 2.0.2'
|
|||
gem 'browser', '~> 2.5'
|
||||
|
||||
# GPG
|
||||
gem 'gpgme', '~> 2.0.18'
|
||||
gem 'gpgme', '~> 2.0.19'
|
||||
|
||||
# LDAP Auth
|
||||
# GitLab fork with several improvements to original library. For full list of changes
|
||||
|
@ -136,7 +136,7 @@ gem 'faraday_middleware-aws-signers-v4'
|
|||
|
||||
# Markdown and HTML processing
|
||||
gem 'html-pipeline', '~> 2.8'
|
||||
gem 'deckar01-task_list', '2.2.0'
|
||||
gem 'deckar01-task_list', '2.2.1'
|
||||
gem 'gitlab-markup', '~> 1.7.0'
|
||||
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
|
||||
gem 'commonmarker', '~> 0.17'
|
||||
|
@ -151,7 +151,7 @@ gem 'asciidoctor-plantuml', '0.0.9'
|
|||
gem 'rouge', '~> 3.11.0'
|
||||
gem 'truncato', '~> 0.7.11'
|
||||
gem 'bootstrap_form', '~> 4.2.0'
|
||||
gem 'nokogiri', '~> 1.10.4'
|
||||
gem 'nokogiri', '~> 1.10.5'
|
||||
gem 'escape_utils', '~> 1.1'
|
||||
|
||||
# Calendar rendering
|
||||
|
@ -159,6 +159,7 @@ gem 'icalendar'
|
|||
|
||||
# Diffs
|
||||
gem 'diffy', '~> 3.1.0'
|
||||
gem 'diff_match_patch', '~> 0.1.0'
|
||||
|
||||
# Application server
|
||||
gem 'rack', '~> 2.0.7'
|
||||
|
@ -175,7 +176,7 @@ group :puma do
|
|||
end
|
||||
|
||||
# State machine
|
||||
gem 'state_machines-activerecord', '~> 0.5.1'
|
||||
gem 'state_machines-activerecord', '~> 0.6.0'
|
||||
|
||||
# Issue tags
|
||||
gem 'acts-as-taggable-on', '~> 6.0'
|
||||
|
@ -259,9 +260,6 @@ gem 'loofah', '~> 2.2'
|
|||
# Working with license
|
||||
gem 'licensee', '~> 8.9'
|
||||
|
||||
# Protect against bruteforcing
|
||||
gem 'rack-attack', '~> 4.4.1'
|
||||
|
||||
# Ace editor
|
||||
gem 'ace-rails-ap', '~> 4.1.0'
|
||||
|
||||
|
@ -293,10 +291,13 @@ gem 'base32', '~> 0.3.0'
|
|||
|
||||
gem "gitlab-license", "~> 1.0"
|
||||
|
||||
# Protect against bruteforcing
|
||||
gem 'rack-attack', '~> 6.2.0'
|
||||
|
||||
# Sentry integration
|
||||
gem 'sentry-raven', '~> 2.9'
|
||||
|
||||
gem 'premailer-rails', '~> 1.9.7'
|
||||
gem 'premailer-rails', '~> 1.10.3'
|
||||
|
||||
# LabKit: Tracing and Correlation
|
||||
gem 'gitlab-labkit', '~> 0.5'
|
||||
|
@ -331,7 +332,6 @@ group :metrics do
|
|||
end
|
||||
|
||||
group :development do
|
||||
gem 'foreman', '~> 0.84.0'
|
||||
gem 'brakeman', '~> 4.2', require: false
|
||||
gem 'danger', '~> 6.0', require: false
|
||||
|
||||
|
@ -388,7 +388,6 @@ group :development, :test do
|
|||
|
||||
gem 'benchmark-ips', '~> 2.3.0', require: false
|
||||
|
||||
gem 'license_finder', '~> 5.4', require: false
|
||||
gem 'knapsack', '~> 1.17'
|
||||
|
||||
gem 'stackprof', '~> 0.2.10', require: false
|
||||
|
@ -398,6 +397,11 @@ group :development, :test do
|
|||
gem 'timecop', '~> 0.8.0'
|
||||
end
|
||||
|
||||
# Gems required in omnibus-gitlab pipeline
|
||||
group :development, :test, :omnibus do
|
||||
gem 'license_finder', '~> 5.4', require: false
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'shoulda-matchers', '~> 4.0.1', require: false
|
||||
gem 'email_spec', '~> 2.2.0'
|
||||
|
@ -407,6 +411,7 @@ group :test do
|
|||
gem 'concurrent-ruby', '~> 1.1'
|
||||
gem 'test-prof', '~> 0.10.0'
|
||||
gem 'rspec_junit_formatter'
|
||||
gem 'guard-rspec'
|
||||
end
|
||||
|
||||
gem 'octokit', '~> 4.9'
|
||||
|
@ -446,18 +451,18 @@ group :ed25519 do
|
|||
end
|
||||
|
||||
# Gitaly GRPC protocol definitions
|
||||
gem 'gitaly', '~> 1.65.0'
|
||||
gem 'gitaly', '~> 1.70.0'
|
||||
|
||||
gem 'grpc', '~> 1.19.0'
|
||||
gem 'grpc', '~> 1.24.0'
|
||||
|
||||
gem 'google-protobuf', '~> 3.7.1'
|
||||
gem 'google-protobuf', '~> 3.8.0'
|
||||
|
||||
gem 'toml-rb', '~> 1.0.0', require: false
|
||||
|
||||
# Feature toggles
|
||||
gem 'flipper', '~> 0.13.0'
|
||||
gem 'flipper-active_record', '~> 0.13.0'
|
||||
gem 'flipper-active_support_cache_store', '~> 0.13.0'
|
||||
gem 'flipper', '~> 0.17.1'
|
||||
gem 'flipper-active_record', '~> 0.17.1'
|
||||
gem 'flipper-active_support_cache_store', '~> 0.17.1'
|
||||
gem 'unleash', '~> 0.1.5'
|
||||
|
||||
# Structured logging
|
||||
|
@ -469,3 +474,5 @@ gem 'gitlab-net-dns', '~> 0.9.1'
|
|||
|
||||
# Countries list
|
||||
gem 'countries', '~> 3.0'
|
||||
|
||||
gem 'retriable', '~> 3.1.2'
|
||||
|
|
196
Gemfile.lock
196
Gemfile.lock
|
@ -50,8 +50,8 @@ GEM
|
|||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
acts-as-taggable-on (6.0.0)
|
||||
activerecord (~> 5.0)
|
||||
acts-as-taggable-on (6.5.0)
|
||||
activerecord (>= 5.0, < 6.1)
|
||||
adamantium (0.2.0)
|
||||
ice_nine (~> 0.11.0)
|
||||
memoizable (~> 0.4.0)
|
||||
|
@ -80,14 +80,16 @@ GEM
|
|||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.1)
|
||||
awesome_print (1.8.0)
|
||||
aws-sdk (2.9.32)
|
||||
aws-sdk-resources (= 2.9.32)
|
||||
aws-sdk-core (2.9.32)
|
||||
aws-eventstream (1.0.3)
|
||||
aws-sdk (2.11.374)
|
||||
aws-sdk-resources (= 2.11.374)
|
||||
aws-sdk-core (2.11.374)
|
||||
aws-sigv4 (~> 1.0)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-resources (2.9.32)
|
||||
aws-sdk-core (= 2.9.32)
|
||||
aws-sigv4 (1.0.0)
|
||||
aws-sdk-resources (2.11.374)
|
||||
aws-sdk-core (= 2.11.374)
|
||||
aws-sigv4 (1.1.0)
|
||||
aws-eventstream (~> 1.0, >= 1.0.2)
|
||||
axiom-types (0.1.1)
|
||||
descendants_tracker (~> 0.0.4)
|
||||
ice_nine (~> 0.11.0)
|
||||
|
@ -171,9 +173,9 @@ GEM
|
|||
unicode_utils (~> 1.4)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.4)
|
||||
crass (1.0.5)
|
||||
creole (0.5.0)
|
||||
css_parser (1.5.0)
|
||||
css_parser (1.7.0)
|
||||
addressable
|
||||
daemons (1.2.6)
|
||||
danger (6.0.9)
|
||||
|
@ -192,12 +194,12 @@ GEM
|
|||
database_cleaner (1.7.0)
|
||||
debug_inspector (0.0.3)
|
||||
debugger-ruby_core_source (1.3.8)
|
||||
deckar01-task_list (2.2.0)
|
||||
deckar01-task_list (2.2.1)
|
||||
html-pipeline
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
default_value_for (3.2.0)
|
||||
activerecord (>= 3.2.0, < 6.0)
|
||||
default_value_for (3.3.0)
|
||||
activerecord (>= 3.2.0, < 6.1)
|
||||
derailed_benchmarks (1.3.5)
|
||||
benchmark-ips (~> 2)
|
||||
get_process_mem (~> 0)
|
||||
|
@ -222,6 +224,7 @@ GEM
|
|||
railties
|
||||
rotp (~> 2.0)
|
||||
diff-lcs (1.3)
|
||||
diff_match_patch (0.1.0)
|
||||
diffy (3.1.0)
|
||||
discordrb-webhooks-blackst0ne (3.3.0)
|
||||
rest-client (~> 2.0)
|
||||
|
@ -285,13 +288,13 @@ GEM
|
|||
fast_gettext (1.6.0)
|
||||
ffaker (2.10.0)
|
||||
ffi (1.11.1)
|
||||
flipper (0.13.0)
|
||||
flipper-active_record (0.13.0)
|
||||
activerecord (>= 3.2, < 6)
|
||||
flipper (~> 0.13.0)
|
||||
flipper-active_support_cache_store (0.13.0)
|
||||
activesupport (>= 3.2, < 6)
|
||||
flipper (~> 0.13.0)
|
||||
flipper (0.17.1)
|
||||
flipper-active_record (0.17.1)
|
||||
activerecord (>= 4.2, < 7)
|
||||
flipper (~> 0.17.1)
|
||||
flipper-active_support_cache_store (0.17.1)
|
||||
activesupport (>= 4.2, < 7)
|
||||
flipper (~> 0.17.1)
|
||||
flowdock (0.7.1)
|
||||
httparty (~> 0.7)
|
||||
multi_json
|
||||
|
@ -332,10 +335,8 @@ GEM
|
|||
fog-xml (0.1.3)
|
||||
fog-core
|
||||
nokogiri (>= 1.5.11, < 2.0.0)
|
||||
font-awesome-rails (4.7.0.4)
|
||||
railties (>= 3.2, < 6.0)
|
||||
foreman (0.84.0)
|
||||
thor (~> 0.19.1)
|
||||
font-awesome-rails (4.7.0.5)
|
||||
railties (>= 3.2, < 6.1)
|
||||
formatador (0.2.5)
|
||||
fugit (1.2.1)
|
||||
et-orbi (~> 1.1, >= 1.1.8)
|
||||
|
@ -358,12 +359,12 @@ GEM
|
|||
po_to_json (>= 1.0.0)
|
||||
rails (>= 3.2.0)
|
||||
git (1.5.0)
|
||||
gitaly (1.65.0)
|
||||
gitaly (1.70.0)
|
||||
grpc (~> 1.0)
|
||||
github-markup (1.7.0)
|
||||
gitlab-labkit (0.5.2)
|
||||
actionpack (~> 5)
|
||||
activesupport (~> 5)
|
||||
gitlab-labkit (0.7.0)
|
||||
actionpack (>= 5.0.0, < 6.1.0)
|
||||
activesupport (>= 5.0.0, < 6.1.0)
|
||||
grpc (~> 1.19)
|
||||
jaeger-client (~> 0.10)
|
||||
opentracing (~> 0.4)
|
||||
|
@ -400,7 +401,7 @@ GEM
|
|||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
google-protobuf (3.7.1)
|
||||
google-protobuf (3.8.0)
|
||||
googleapis-common-protos-types (1.0.4)
|
||||
google-protobuf (~> 3.0)
|
||||
googleauth (0.6.6)
|
||||
|
@ -410,7 +411,7 @@ GEM
|
|||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.7)
|
||||
gpgme (2.0.18)
|
||||
gpgme (2.0.19)
|
||||
mini_portile2 (~> 2.3)
|
||||
grape (1.1.0)
|
||||
activesupport
|
||||
|
@ -440,11 +441,25 @@ GEM
|
|||
graphql (~> 1.6)
|
||||
html-pipeline (~> 2.8)
|
||||
sass (~> 3.4)
|
||||
grpc (1.19.0)
|
||||
google-protobuf (~> 3.1)
|
||||
googleapis-common-protos-types (~> 1.0.0)
|
||||
grpc (1.24.0)
|
||||
google-protobuf (~> 3.8)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
gssapi (1.2.0)
|
||||
ffi (>= 1.0.1)
|
||||
guard (2.15.1)
|
||||
formatador (>= 0.2.4)
|
||||
listen (>= 2.7, < 4.0)
|
||||
lumberjack (>= 1.0.12, < 2.0)
|
||||
nenv (~> 0.1)
|
||||
notiffany (~> 0.0)
|
||||
pry (>= 0.9.12)
|
||||
shellany (~> 0.0)
|
||||
thor (>= 0.18.1)
|
||||
guard-compat (1.2.1)
|
||||
guard-rspec (4.7.3)
|
||||
guard (~> 2.1)
|
||||
guard-compat (~> 1.1)
|
||||
rspec (>= 2.99.0, < 4.0)
|
||||
haml (5.0.4)
|
||||
temple (>= 0.8.0)
|
||||
tilt
|
||||
|
@ -508,7 +523,7 @@ GEM
|
|||
atlassian-jwt
|
||||
multipart-post
|
||||
oauth (~> 0.5, >= 0.5.0)
|
||||
jmespath (1.3.1)
|
||||
jmespath (1.4.0)
|
||||
js_regex (3.1.1)
|
||||
character_set (~> 1.1)
|
||||
regexp_parser (~> 1.1)
|
||||
|
@ -560,15 +575,20 @@ GEM
|
|||
xml-simple
|
||||
licensee (8.9.2)
|
||||
rugged (~> 0.24)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
locale (2.1.2)
|
||||
lograge (0.10.0)
|
||||
actionpack (>= 4)
|
||||
activesupport (>= 4)
|
||||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
loofah (2.3.0)
|
||||
loofah (2.3.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
lumberjack (1.0.13)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
mail_room (0.9.1)
|
||||
|
@ -584,7 +604,7 @@ GEM
|
|||
mime-types-data (3.2019.0331)
|
||||
mimemagic (0.3.2)
|
||||
mini_magick (4.9.5)
|
||||
mini_mime (1.0.1)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.11.3)
|
||||
msgpack (1.3.1)
|
||||
|
@ -597,16 +617,20 @@ GEM
|
|||
mustermann (~> 1.0.0)
|
||||
nakayoshi_fork (0.0.4)
|
||||
nap (1.1.0)
|
||||
nenv (0.3.0)
|
||||
net-ldap (0.16.0)
|
||||
net-ntp (2.1.3)
|
||||
net-ssh (5.2.0)
|
||||
netrc (0.11.0)
|
||||
nio4r (2.3.1)
|
||||
no_proxy_fix (0.1.2)
|
||||
nokogiri (1.10.4)
|
||||
nokogiri (1.10.5)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
nokogumbo (1.5.0)
|
||||
nokogiri
|
||||
notiffany (0.1.3)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
numerizer (0.1.1)
|
||||
oauth (0.5.4)
|
||||
oauth2 (1.4.1)
|
||||
|
@ -675,12 +699,12 @@ GEM
|
|||
activesupport
|
||||
nokogiri (>= 1.4.4)
|
||||
omniauth (~> 1.0)
|
||||
omniauth_openid_connect (0.3.1)
|
||||
omniauth_openid_connect (0.3.3)
|
||||
addressable (~> 2.5)
|
||||
omniauth (~> 1.3)
|
||||
omniauth (~> 1.9)
|
||||
openid_connect (~> 1.1)
|
||||
open4 (1.3.4)
|
||||
openid_connect (1.1.6)
|
||||
openid_connect (1.1.8)
|
||||
activemodel
|
||||
attr_required (>= 1.0.0)
|
||||
json-jwt (>= 1.5.0)
|
||||
|
@ -703,12 +727,12 @@ GEM
|
|||
pg (1.1.4)
|
||||
po_to_json (1.0.1)
|
||||
json (>= 1.6.0)
|
||||
premailer (1.10.4)
|
||||
premailer (1.11.1)
|
||||
addressable
|
||||
css_parser (>= 1.4.10)
|
||||
css_parser (>= 1.6.0)
|
||||
htmlentities (>= 4.0.0)
|
||||
premailer-rails (1.9.7)
|
||||
actionmailer (>= 3, < 6)
|
||||
premailer-rails (1.10.3)
|
||||
actionmailer (>= 3)
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
proc_to_ast (0.1.0)
|
||||
coderay
|
||||
|
@ -724,7 +748,7 @@ GEM
|
|||
pry (~> 0.10)
|
||||
pry-rails (0.3.6)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (3.1.0)
|
||||
public_suffix (3.1.1)
|
||||
puma (3.12.0)
|
||||
puma_worker_killer (0.1.0)
|
||||
get_process_mem (~> 0.2)
|
||||
|
@ -734,8 +758,8 @@ GEM
|
|||
rack (2.0.7)
|
||||
rack-accept (0.4.5)
|
||||
rack (>= 0.4)
|
||||
rack-attack (4.4.1)
|
||||
rack
|
||||
rack-attack (6.2.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-cors (1.0.2)
|
||||
rack-oauth2 (1.9.3)
|
||||
activesupport
|
||||
|
@ -763,10 +787,10 @@ GEM
|
|||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.3)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.2)
|
||||
actionpack (~> 5.x, >= 5.0.1)
|
||||
actionview (~> 5.x, >= 5.0.1)
|
||||
activesupport (~> 5.x)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
actionview (>= 5.0.1.x)
|
||||
activesupport (>= 5.0.1.x)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
|
@ -798,25 +822,25 @@ GEM
|
|||
recaptcha (4.13.1)
|
||||
json
|
||||
recursive-open-struct (1.1.0)
|
||||
redis (4.1.2)
|
||||
redis-actionpack (5.0.2)
|
||||
actionpack (>= 4.0, < 6)
|
||||
redis (4.1.3)
|
||||
redis-actionpack (5.1.0)
|
||||
actionpack (>= 4.0, < 7)
|
||||
redis-rack (>= 1, < 3)
|
||||
redis-store (>= 1.1.0, < 2)
|
||||
redis-activesupport (5.0.7)
|
||||
activesupport (>= 3, < 6)
|
||||
redis-activesupport (5.2.0)
|
||||
activesupport (>= 3, < 7)
|
||||
redis-store (>= 1.3, < 2)
|
||||
redis-namespace (1.6.0)
|
||||
redis (>= 3.0.4)
|
||||
redis-rack (2.0.5)
|
||||
redis-rack (2.0.6)
|
||||
rack (>= 1.5, < 3)
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-rails (5.0.2)
|
||||
redis-actionpack (>= 5.0, < 6)
|
||||
redis-activesupport (>= 5.0, < 6)
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-store (1.6.0)
|
||||
redis (>= 2.2, < 5)
|
||||
redis-store (1.8.1)
|
||||
redis (>= 4, < 5)
|
||||
regexp_parser (1.5.1)
|
||||
regexp_property_values (0.3.4)
|
||||
representable (3.0.4)
|
||||
|
@ -824,9 +848,9 @@ GEM
|
|||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
request_store (1.3.1)
|
||||
responders (2.4.1)
|
||||
actionpack (>= 4.2.0, < 6.0)
|
||||
railties (>= 4.2.0, < 6.0)
|
||||
responders (3.0.0)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
rest-client (2.0.2)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 4.0)
|
||||
|
@ -897,11 +921,12 @@ GEM
|
|||
ruby-progressbar (1.10.1)
|
||||
ruby-saml (1.7.2)
|
||||
nokogiri (>= 1.5.10)
|
||||
ruby_dep (1.5.0)
|
||||
ruby_parser (3.13.1)
|
||||
sexp_processor (~> 4.9)
|
||||
rubyntlm (0.6.2)
|
||||
rubypants (0.2.0)
|
||||
rubyzip (1.2.2)
|
||||
rubyzip (1.3.0)
|
||||
rugged (0.28.3.1)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (4.6.6)
|
||||
|
@ -938,6 +963,7 @@ GEM
|
|||
faraday (>= 0.7.6, < 1.0)
|
||||
settingslogic (2.0.9)
|
||||
sexp_processor (4.12.0)
|
||||
shellany (0.0.1)
|
||||
shoulda-matchers (4.0.1)
|
||||
activesupport (>= 4.2.0)
|
||||
sidekiq (5.2.7)
|
||||
|
@ -978,11 +1004,11 @@ GEM
|
|||
sshkey (2.0.0)
|
||||
stackprof (0.2.10)
|
||||
state_machines (0.5.0)
|
||||
state_machines-activemodel (0.5.1)
|
||||
activemodel (>= 4.1, < 6.0)
|
||||
state_machines-activemodel (0.7.1)
|
||||
activemodel (>= 4.1)
|
||||
state_machines (>= 0.5.0)
|
||||
state_machines-activerecord (0.5.1)
|
||||
activerecord (>= 4.1, < 6.0)
|
||||
state_machines-activerecord (0.6.0)
|
||||
activerecord (>= 4.1)
|
||||
state_machines-activemodel (>= 0.5.0)
|
||||
swd (1.1.2)
|
||||
activesupport (>= 3)
|
||||
|
@ -1127,12 +1153,13 @@ DEPENDENCIES
|
|||
creole (~> 0.5.0)
|
||||
danger (~> 6.0)
|
||||
database_cleaner (~> 1.7.0)
|
||||
deckar01-task_list (= 2.2.0)
|
||||
default_value_for (~> 3.2.0)
|
||||
deckar01-task_list (= 2.2.1)
|
||||
default_value_for (~> 3.3.0)
|
||||
derailed_benchmarks
|
||||
device_detector
|
||||
devise (~> 4.6)
|
||||
devise-two-factor (~> 3.0.0)
|
||||
diff_match_patch (~> 0.1.0)
|
||||
diffy (~> 3.1.0)
|
||||
discordrb-webhooks-blackst0ne (~> 3.3)
|
||||
doorkeeper (~> 4.3)
|
||||
|
@ -1149,9 +1176,9 @@ DEPENDENCIES
|
|||
faraday_middleware-aws-signers-v4
|
||||
fast_blank
|
||||
ffaker (~> 2.10)
|
||||
flipper (~> 0.13.0)
|
||||
flipper-active_record (~> 0.13.0)
|
||||
flipper-active_support_cache_store (~> 0.13.0)
|
||||
flipper (~> 0.17.1)
|
||||
flipper-active_record (~> 0.17.1)
|
||||
flipper-active_support_cache_store (~> 0.17.1)
|
||||
flowdock (~> 0.7)
|
||||
fog-aliyun (~> 0.3)
|
||||
fog-aws (~> 3.5)
|
||||
|
@ -1161,14 +1188,13 @@ DEPENDENCIES
|
|||
fog-openstack (~> 1.0)
|
||||
fog-rackspace (~> 0.1.1)
|
||||
font-awesome-rails (~> 4.7)
|
||||
foreman (~> 0.84.0)
|
||||
fugit (~> 1.2.1)
|
||||
fuubar (~> 2.2.0)
|
||||
gemojione (~> 3.3)
|
||||
gettext (~> 3.2.2)
|
||||
gettext_i18n_rails (~> 1.8.0)
|
||||
gettext_i18n_rails_js (~> 1.3)
|
||||
gitaly (~> 1.65.0)
|
||||
gitaly (~> 1.70.0)
|
||||
github-markup (~> 1.7.0)
|
||||
gitlab-labkit (~> 0.5)
|
||||
gitlab-license (~> 1.0)
|
||||
|
@ -1181,8 +1207,8 @@ DEPENDENCIES
|
|||
gitlab_omniauth-ldap (~> 2.1.1)
|
||||
gon (~> 6.2)
|
||||
google-api-client (~> 0.23)
|
||||
google-protobuf (~> 3.7.1)
|
||||
gpgme (~> 2.0.18)
|
||||
google-protobuf (~> 3.8.0)
|
||||
gpgme (~> 2.0.19)
|
||||
grape (~> 1.1.0)
|
||||
grape-entity (~> 0.7.1)
|
||||
grape-path-helpers (~> 1.1)
|
||||
|
@ -1190,8 +1216,9 @@ DEPENDENCIES
|
|||
graphiql-rails (~> 1.4.10)
|
||||
graphql (~> 1.9.11)
|
||||
graphql-docs (~> 1.6.0)
|
||||
grpc (~> 1.19.0)
|
||||
grpc (~> 1.24.0)
|
||||
gssapi
|
||||
guard-rspec
|
||||
haml_lint (~> 0.31.0)
|
||||
hamlit (~> 2.8.8)
|
||||
hangouts-chat (~> 0.0.5)
|
||||
|
@ -1226,7 +1253,7 @@ DEPENDENCIES
|
|||
net-ldap
|
||||
net-ntp
|
||||
net-ssh (~> 5.2)
|
||||
nokogiri (~> 1.10.4)
|
||||
nokogiri (~> 1.10.5)
|
||||
oauth2 (~> 1.4)
|
||||
octokit (~> 4.9)
|
||||
omniauth (~> 1.8)
|
||||
|
@ -1246,17 +1273,17 @@ DEPENDENCIES
|
|||
omniauth-twitter (~> 1.4)
|
||||
omniauth-ultraauth (~> 0.0.2)
|
||||
omniauth_crowd (~> 2.2.0)
|
||||
omniauth_openid_connect (~> 0.3.1)
|
||||
omniauth_openid_connect (~> 0.3.3)
|
||||
org-ruby (~> 0.9.12)
|
||||
pg (~> 1.1)
|
||||
premailer-rails (~> 1.9.7)
|
||||
premailer-rails (~> 1.10.3)
|
||||
prometheus-client-mmap (~> 0.9.10)
|
||||
pry-byebug (~> 3.5.1)
|
||||
pry-rails (~> 0.3.4)
|
||||
puma (~> 3.12)
|
||||
puma_worker_killer
|
||||
rack (~> 2.0.7)
|
||||
rack-attack (~> 4.4.1)
|
||||
rack-attack (~> 6.2.0)
|
||||
rack-cors (~> 1.0.0)
|
||||
rack-oauth2 (~> 1.9.3)
|
||||
rack-proxy (~> 0.6.0)
|
||||
|
@ -1275,7 +1302,8 @@ DEPENDENCIES
|
|||
redis-namespace (~> 1.6.0)
|
||||
redis-rails (~> 5.0.2)
|
||||
request_store (~> 1.3)
|
||||
responders (~> 2.0)
|
||||
responders (~> 3.0)
|
||||
retriable (~> 3.1.2)
|
||||
rouge (~> 3.11.0)
|
||||
rqrcode-rails3 (~> 0.1.7)
|
||||
rspec-parameterized
|
||||
|
@ -1291,7 +1319,7 @@ DEPENDENCIES
|
|||
ruby-prof (~> 1.0.0)
|
||||
ruby-progressbar
|
||||
ruby_parser (~> 3.8)
|
||||
rubyzip (~> 1.2.2)
|
||||
rubyzip (~> 1.3.0)
|
||||
rugged (~> 0.28)
|
||||
sanitize (~> 4.6)
|
||||
sassc-rails (~> 2.1.0)
|
||||
|
@ -1312,7 +1340,7 @@ DEPENDENCIES
|
|||
sprockets (~> 3.7.0)
|
||||
sshkey (~> 2.0)
|
||||
stackprof (~> 0.2.10)
|
||||
state_machines-activerecord (~> 0.5.1)
|
||||
state_machines-activerecord (~> 0.6.0)
|
||||
sys-filesystem (~> 1.1.6)
|
||||
test-prof (~> 0.10.0)
|
||||
thin (~> 1.7.0)
|
||||
|
|
43
Guardfile
Normal file
43
Guardfile
Normal file
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
cmd = ENV['SPRING'] ? 'spring rspec' : 'bundle exec rspec'
|
||||
|
||||
guard :rspec, cmd: cmd do
|
||||
require "guard/rspec/dsl"
|
||||
dsl = Guard::RSpec::Dsl.new(self)
|
||||
|
||||
directories %w(app ee lib spec)
|
||||
|
||||
# RSpec files
|
||||
rspec = dsl.rspec
|
||||
watch(rspec.spec_helper) { rspec.spec_dir }
|
||||
watch(rspec.spec_support) { rspec.spec_dir }
|
||||
watch(rspec.spec_files)
|
||||
|
||||
# Ruby files
|
||||
ruby = dsl.ruby
|
||||
dsl.watch_spec_files_for(ruby.lib_files)
|
||||
|
||||
# Rails files
|
||||
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
||||
dsl.watch_spec_files_for(rails.app_files)
|
||||
dsl.watch_spec_files_for(rails.views)
|
||||
|
||||
watch(rails.controllers) do |m|
|
||||
[
|
||||
rspec.spec.call("routing/#{m[1]}_routing"),
|
||||
rspec.spec.call("controllers/#{m[1]}_controller")
|
||||
]
|
||||
end
|
||||
|
||||
# Rails config changes
|
||||
watch(rails.spec_helper) { rspec.spec_dir }
|
||||
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
||||
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
||||
|
||||
# Capybara features specs
|
||||
watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
|
||||
watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
|
||||
end
|
|
@ -79,7 +79,7 @@ star, smile, etc.). Some good tips about code reviews can be found in our
|
|||
|
||||
Overview and details of feature flag processes in development of GitLab itself is described in [feature flags process documentation](https://docs.gitlab.com/ee/development/feature_flags/process.html).
|
||||
|
||||
Guides on how to include feature flags in your backend/frontend code while developing GitLab are described in [developing with feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/developing.html).
|
||||
Guides on how to include feature flags in your backend/frontend code while developing GitLab are described in [developing with feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/development.html).
|
||||
|
||||
Getting access and how to expose the feature to users is detailed in [controlling feature flags documentation](https://docs.gitlab.com/ee/development/feature_flags/controls.html).
|
||||
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
12.4.6
|
||||
12.5.4
|
||||
|
|
BIN
app/assets/images/cluster_app_logos/crossplane.png
Normal file
BIN
app/assets/images/cluster_app_logos/crossplane.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
BIN
app/assets/images/cluster_app_logos/elastic_stack.png
Normal file
BIN
app/assets/images/cluster_app_logos/elastic_stack.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -2,6 +2,8 @@ import $ from 'jquery';
|
|||
import _ from 'underscore';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { joinPaths } from './lib/utils/url_utility';
|
||||
import flash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
const Api = {
|
||||
groupsPath: '/api/:version/groups.json',
|
||||
|
@ -29,6 +31,7 @@ const Api = {
|
|||
usersPath: '/api/:version/users.json',
|
||||
userPath: '/api/:version/users/:id',
|
||||
userStatusPath: '/api/:version/users/:id/status',
|
||||
userProjectsPath: '/api/:version/users/:id/projects',
|
||||
userPostStatusPath: '/api/:version/user/status',
|
||||
commitPath: '/api/:version/projects/:id/repository/commits',
|
||||
applySuggestionPath: '/api/:version/suggestions/:id/apply',
|
||||
|
@ -110,10 +113,9 @@ const Api = {
|
|||
.get(url, {
|
||||
params: Object.assign(defaults, options),
|
||||
})
|
||||
.then(({ data }) => {
|
||||
.then(({ data, headers }) => {
|
||||
callback(data);
|
||||
|
||||
return data;
|
||||
return { data, headers };
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -239,7 +241,8 @@ const Api = {
|
|||
.get(url, {
|
||||
params: Object.assign({}, defaults, options),
|
||||
})
|
||||
.then(({ data }) => callback(data));
|
||||
.then(({ data }) => callback(data))
|
||||
.catch(() => flash(__('Something went wrong while fetching projects')));
|
||||
},
|
||||
|
||||
commitMultiple(id, data) {
|
||||
|
@ -348,6 +351,20 @@ const Api = {
|
|||
});
|
||||
},
|
||||
|
||||
userProjects(userId, query, options, callback) {
|
||||
const url = Api.buildUrl(Api.userProjectsPath).replace(':id', userId);
|
||||
const defaults = {
|
||||
search: query,
|
||||
per_page: 20,
|
||||
};
|
||||
return axios
|
||||
.get(url, {
|
||||
params: Object.assign({}, defaults, options),
|
||||
})
|
||||
.then(({ data }) => callback(data))
|
||||
.catch(() => flash(__('Something went wrong while fetching projects')));
|
||||
},
|
||||
|
||||
branches(id, query = '', options = {}) {
|
||||
const url = Api.buildUrl(this.createBranchPath).replace(':id', encodeURIComponent(id));
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, no-var */
|
||||
/* eslint-disable func-names */
|
||||
|
||||
import $ from 'jquery';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
@ -12,11 +12,8 @@ import { __ } from '~/locale';
|
|||
// more than `x` users are referenced.
|
||||
//
|
||||
|
||||
var lastTextareaPreviewed;
|
||||
var lastTextareaHeight = null;
|
||||
var markdownPreview;
|
||||
var previewButtonSelector;
|
||||
var writeButtonSelector;
|
||||
let lastTextareaHeight;
|
||||
let lastTextareaPreviewed;
|
||||
|
||||
function MarkdownPreview() {}
|
||||
|
||||
|
@ -27,14 +24,13 @@ MarkdownPreview.prototype.emptyMessage = __('Nothing to preview.');
|
|||
MarkdownPreview.prototype.ajaxCache = {};
|
||||
|
||||
MarkdownPreview.prototype.showPreview = function($form) {
|
||||
var mdText;
|
||||
var preview = $form.find('.js-md-preview');
|
||||
var url = preview.data('url');
|
||||
const preview = $form.find('.js-md-preview');
|
||||
const url = preview.data('url');
|
||||
if (preview.hasClass('md-preview-loading')) {
|
||||
return;
|
||||
}
|
||||
|
||||
mdText = $form.find('textarea.markdown-area').val();
|
||||
const mdText = $form.find('textarea.markdown-area').val();
|
||||
|
||||
if (mdText === undefined) {
|
||||
return;
|
||||
|
@ -46,7 +42,7 @@ MarkdownPreview.prototype.showPreview = function($form) {
|
|||
} else {
|
||||
preview.addClass('md-preview-loading').text(__('Loading...'));
|
||||
this.fetchMarkdownPreview(mdText, url, response => {
|
||||
var body;
|
||||
let body;
|
||||
if (response.body.length > 0) {
|
||||
({ body } = response);
|
||||
} else {
|
||||
|
@ -91,8 +87,7 @@ MarkdownPreview.prototype.hideReferencedUsers = function($form) {
|
|||
};
|
||||
|
||||
MarkdownPreview.prototype.renderReferencedUsers = function(users, $form) {
|
||||
var referencedUsers;
|
||||
referencedUsers = $form.find('.referenced-users');
|
||||
const referencedUsers = $form.find('.referenced-users');
|
||||
if (referencedUsers.length) {
|
||||
if (users.length >= this.referenceThreshold) {
|
||||
referencedUsers.show();
|
||||
|
@ -108,8 +103,7 @@ MarkdownPreview.prototype.hideReferencedCommands = function($form) {
|
|||
};
|
||||
|
||||
MarkdownPreview.prototype.renderReferencedCommands = function(commands, $form) {
|
||||
var referencedCommands;
|
||||
referencedCommands = $form.find('.referenced-commands');
|
||||
const referencedCommands = $form.find('.referenced-commands');
|
||||
if (commands.length > 0) {
|
||||
referencedCommands.html(commands);
|
||||
referencedCommands.show();
|
||||
|
@ -119,15 +113,15 @@ MarkdownPreview.prototype.renderReferencedCommands = function(commands, $form) {
|
|||
}
|
||||
};
|
||||
|
||||
markdownPreview = new MarkdownPreview();
|
||||
const markdownPreview = new MarkdownPreview();
|
||||
|
||||
previewButtonSelector = '.js-md-preview-button';
|
||||
writeButtonSelector = '.js-md-write-button';
|
||||
const previewButtonSelector = '.js-md-preview-button';
|
||||
const writeButtonSelector = '.js-md-write-button';
|
||||
lastTextareaPreviewed = null;
|
||||
const markdownToolbar = $('.md-header-toolbar');
|
||||
|
||||
$.fn.setupMarkdownPreview = function() {
|
||||
var $form = $(this);
|
||||
const $form = $(this);
|
||||
$form.find('textarea.markdown-area').on('input', () => {
|
||||
markdownPreview.hideReferencedUsers($form);
|
||||
});
|
||||
|
@ -188,7 +182,7 @@ $(document).on('markdown-preview:hide', (e, $form) => {
|
|||
});
|
||||
|
||||
$(document).on('markdown-preview:toggle', (e, keyboardEvent) => {
|
||||
var $target;
|
||||
let $target;
|
||||
$target = $(keyboardEvent.target);
|
||||
if ($target.is('textarea.markdown-area')) {
|
||||
$(document).triggerHandler('markdown-preview:show', [$target.closest('form')]);
|
||||
|
@ -201,16 +195,14 @@ $(document).on('markdown-preview:toggle', (e, keyboardEvent) => {
|
|||
});
|
||||
|
||||
$(document).on('click', previewButtonSelector, function(e) {
|
||||
var $form;
|
||||
e.preventDefault();
|
||||
$form = $(this).closest('form');
|
||||
const $form = $(this).closest('form');
|
||||
$(document).triggerHandler('markdown-preview:show', [$form]);
|
||||
});
|
||||
|
||||
$(document).on('click', writeButtonSelector, function(e) {
|
||||
var $form;
|
||||
e.preventDefault();
|
||||
$form = $(this).closest('form');
|
||||
const $form = $(this).closest('form');
|
||||
$(document).triggerHandler('markdown-preview:hide', [$form]);
|
||||
});
|
||||
|
||||
|
|
|
@ -118,8 +118,6 @@ export default class FileTemplateMediator {
|
|||
}
|
||||
});
|
||||
|
||||
this.setFilename(item.name);
|
||||
|
||||
if (this.editor.getValue() !== '') {
|
||||
this.setTypeSelectorToggleText(item.name);
|
||||
}
|
||||
|
@ -133,14 +131,16 @@ export default class FileTemplateMediator {
|
|||
|
||||
selectTemplateFile(selector, query, data) {
|
||||
const self = this;
|
||||
const { name } = selector.config;
|
||||
|
||||
selector.renderLoading();
|
||||
|
||||
this.fetchFileTemplate(selector.config.type, query, data)
|
||||
.then(file => {
|
||||
this.setEditorContent(file);
|
||||
this.setFilename(name);
|
||||
selector.renderLoaded();
|
||||
this.typeSelector.setToggleText(selector.config.name);
|
||||
this.typeSelector.setToggleText(name);
|
||||
toast(__(`${query} template applied`), {
|
||||
action: {
|
||||
text: __('Undo'),
|
||||
|
|
|
@ -133,7 +133,7 @@ export default {
|
|||
if (this.board.name.length === 0) return;
|
||||
this.isLoading = true;
|
||||
if (this.isDeleteForm) {
|
||||
gl.boardService
|
||||
boardsStore
|
||||
.deleteBoard(this.currentBoard)
|
||||
.then(() => {
|
||||
visitUrl(boardsStore.rootPath);
|
||||
|
@ -143,7 +143,7 @@ export default {
|
|||
this.isLoading = false;
|
||||
});
|
||||
} else {
|
||||
gl.boardService
|
||||
boardsStore
|
||||
.createBoard(this.board)
|
||||
.then(resp => resp.data)
|
||||
.then(data => {
|
||||
|
|
|
@ -84,7 +84,8 @@ export default {
|
|||
this.$nextTick(() => {
|
||||
if (
|
||||
this.scrollHeight() <= this.listHeight() &&
|
||||
this.list.issuesSize > this.list.issues.length
|
||||
this.list.issuesSize > this.list.issues.length &&
|
||||
this.list.isExpanded
|
||||
) {
|
||||
this.list.page += 1;
|
||||
this.list.getIssues(false).catch(() => {
|
||||
|
|
|
@ -168,7 +168,7 @@ export default {
|
|||
}
|
||||
|
||||
const recentBoardsPromise = new Promise((resolve, reject) =>
|
||||
gl.boardService
|
||||
boardsStore
|
||||
.recentBoards()
|
||||
.then(resolve)
|
||||
.catch(err => {
|
||||
|
@ -184,7 +184,7 @@ export default {
|
|||
}),
|
||||
);
|
||||
|
||||
Promise.all([gl.boardService.allBoards(), recentBoardsPromise])
|
||||
Promise.all([boardsStore.allBoards(), recentBoardsPromise])
|
||||
.then(([allBoards, recentBoards]) => [allBoards.data, recentBoards.data])
|
||||
.then(([allBoardsJson, recentBoardsJson]) => {
|
||||
this.loading = false;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import { mapState } from 'vuex';
|
||||
import { GlTooltipDirective } from '@gitlab/ui';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
|
@ -63,6 +64,7 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['isShowingLabels']),
|
||||
numberOverLimit() {
|
||||
return this.issue.assignees.length - this.limitBeforeCounter;
|
||||
},
|
||||
|
@ -92,7 +94,7 @@ export default {
|
|||
return false;
|
||||
},
|
||||
showLabelFooter() {
|
||||
return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
|
||||
return this.isShowingLabels && this.issue.labels.find(this.showLabel);
|
||||
},
|
||||
issueReferencePath() {
|
||||
const { referencePath, groupId } = this.issue;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
/* global ListIssue */
|
||||
import { urlParamsToObject } from '~/lib/utils/common_utils';
|
||||
import boardsStore from '~/boards/stores/boards_store';
|
||||
import ModalHeader from './header.vue';
|
||||
import ModalList from './list.vue';
|
||||
import ModalFooter from './footer.vue';
|
||||
|
@ -109,7 +110,7 @@ export default {
|
|||
loadIssues(clearIssues = false) {
|
||||
if (!this.showAddIssuesModal) return false;
|
||||
|
||||
return gl.boardService
|
||||
return boardsStore
|
||||
.getBacklog({
|
||||
...urlParamsToObject(this.filter.path),
|
||||
page: this.page,
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'ee_else_ce/boards/models/issue';
|
|||
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';
|
||||
|
@ -29,6 +30,7 @@ import {
|
|||
} from '~/lib/utils/common_utils';
|
||||
import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
|
||||
import toggleFocusMode from 'ee_else_ce/boards/toggle_focus';
|
||||
import toggleLabels from 'ee_else_ce/boards/toggle_labels';
|
||||
import {
|
||||
setPromotionState,
|
||||
setWeigthFetchingState,
|
||||
|
@ -67,6 +69,7 @@ export default () => {
|
|||
BoardSidebar,
|
||||
BoardAddIssuesModal,
|
||||
},
|
||||
store,
|
||||
data: {
|
||||
state: boardsStore.state,
|
||||
loading: true,
|
||||
|
@ -314,5 +317,6 @@ export default () => {
|
|||
}
|
||||
|
||||
toggleFocusMode(ModalStore, boardsStore, $boardApp);
|
||||
toggleLabels();
|
||||
mountMultipleBoardsSwitcher();
|
||||
};
|
||||
|
|
|
@ -50,8 +50,8 @@ class List {
|
|||
this.page = 1;
|
||||
this.loading = true;
|
||||
this.loadingMore = false;
|
||||
this.issues = [];
|
||||
this.issuesSize = 0;
|
||||
this.issues = obj.issues || [];
|
||||
this.issuesSize = obj.issuesSize ? obj.issuesSize : 0;
|
||||
this.defaultAvatar = defaultAvatar;
|
||||
|
||||
if (obj.label) {
|
||||
|
|
3
app/assets/javascripts/boards/stores/getters.js
Normal file
3
app/assets/javascripts/boards/stores/getters.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
getLabelToggleState: state => (state.isShowingLabels ? 'on' : 'off'),
|
||||
};
|
|
@ -1,14 +1,18 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import state from 'ee_else_ce/boards/stores/state';
|
||||
import getters from 'ee_else_ce/boards/stores/getters';
|
||||
import actions from 'ee_else_ce/boards/stores/actions';
|
||||
import mutations from 'ee_else_ce/boards/stores/mutations';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default () =>
|
||||
export const createStore = () =>
|
||||
new Vuex.Store({
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
});
|
||||
|
||||
export default createStore();
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export default () => ({
|
||||
// ...
|
||||
isShowingLabels: true,
|
||||
});
|
||||
|
|
1
app/assets/javascripts/boards/toggle_labels.js
Normal file
1
app/assets/javascripts/boards/toggle_labels.js
Normal file
|
@ -0,0 +1 @@
|
|||
export default () => {};
|
|
@ -8,11 +8,12 @@ import Flash from '../flash';
|
|||
import Poll from '../lib/utils/poll';
|
||||
import initSettingsPanels from '../settings_panels';
|
||||
import eventHub from './event_hub';
|
||||
import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX } from './constants';
|
||||
import { APPLICATION_STATUS, INGRESS, INGRESS_DOMAIN_SUFFIX, CROSSPLANE } from './constants';
|
||||
import ClustersService from './services/clusters_service';
|
||||
import ClustersStore from './stores/clusters_store';
|
||||
import Applications from './components/applications.vue';
|
||||
import setupToggleButtons from '../toggle_buttons';
|
||||
import initProjectSelectDropdown from '~/project_select';
|
||||
|
||||
const Environments = () => import('ee_component/clusters/components/environments.vue');
|
||||
|
||||
|
@ -37,6 +38,8 @@ export default class Clusters {
|
|||
installJupyterPath,
|
||||
installKnativePath,
|
||||
updateKnativePath,
|
||||
installElasticStackPath,
|
||||
installCrossplanePath,
|
||||
installPrometheusPath,
|
||||
managePrometheusPath,
|
||||
clusterEnvironmentsPath,
|
||||
|
@ -81,11 +84,13 @@ export default class Clusters {
|
|||
installHelmEndpoint: installHelmPath,
|
||||
installIngressEndpoint: installIngressPath,
|
||||
installCertManagerEndpoint: installCertManagerPath,
|
||||
installCrossplaneEndpoint: installCrossplanePath,
|
||||
installRunnerEndpoint: installRunnerPath,
|
||||
installPrometheusEndpoint: installPrometheusPath,
|
||||
installJupyterEndpoint: installJupyterPath,
|
||||
installKnativeEndpoint: installKnativePath,
|
||||
updateKnativeEndpoint: updateKnativePath,
|
||||
installElasticStackEndpoint: installElasticStackPath,
|
||||
clusterEnvironmentsEndpoint: clusterEnvironmentsPath,
|
||||
});
|
||||
|
||||
|
@ -108,8 +113,10 @@ export default class Clusters {
|
|||
this.ingressDomainHelpText &&
|
||||
this.ingressDomainHelpText.querySelector('.js-ingress-domain-snippet');
|
||||
|
||||
initProjectSelectDropdown();
|
||||
Clusters.initDismissableCallout();
|
||||
initSettingsPanels();
|
||||
|
||||
const toggleButtonsContainer = document.querySelector('.js-cluster-enable-toggle-area');
|
||||
if (toggleButtonsContainer) {
|
||||
setupToggleButtons(toggleButtonsContainer);
|
||||
|
@ -222,6 +229,7 @@ export default class Clusters {
|
|||
eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
|
||||
eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
|
||||
eventHub.$on('uninstallApplication', data => this.uninstallApplication(data));
|
||||
eventHub.$on('setCrossplaneProviderStack', data => this.setCrossplaneProviderStack(data));
|
||||
// Add event listener to all the banner close buttons
|
||||
this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
|
||||
this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
|
||||
|
@ -233,6 +241,7 @@ export default class Clusters {
|
|||
eventHub.$off('updateApplication', this.updateApplication);
|
||||
eventHub.$off('saveKnativeDomain');
|
||||
eventHub.$off('setKnativeHostname');
|
||||
eventHub.$off('setCrossplaneProviderStack');
|
||||
eventHub.$off('uninstallApplication');
|
||||
}
|
||||
|
||||
|
@ -399,18 +408,33 @@ export default class Clusters {
|
|||
}
|
||||
|
||||
installApplication({ id: appId, params }) {
|
||||
this.store.updateAppProperty(appId, 'requestReason', null);
|
||||
this.store.updateAppProperty(appId, 'statusReason', null);
|
||||
return Clusters.validateInstallation(appId, params)
|
||||
.then(() => {
|
||||
this.store.updateAppProperty(appId, 'requestReason', null);
|
||||
this.store.updateAppProperty(appId, 'statusReason', null);
|
||||
this.store.installApplication(appId);
|
||||
|
||||
this.store.installApplication(appId);
|
||||
// eslint-disable-next-line promise/no-nesting
|
||||
this.service.installApplication(appId, params).catch(() => {
|
||||
this.store.notifyInstallFailure(appId);
|
||||
this.store.updateAppProperty(
|
||||
appId,
|
||||
'requestReason',
|
||||
s__('ClusterIntegration|Request to begin installing failed'),
|
||||
);
|
||||
});
|
||||
})
|
||||
.catch(error => this.store.updateAppProperty(appId, 'validationError', error));
|
||||
}
|
||||
|
||||
return this.service.installApplication(appId, params).catch(() => {
|
||||
this.store.notifyInstallFailure(appId);
|
||||
this.store.updateAppProperty(
|
||||
appId,
|
||||
'requestReason',
|
||||
s__('ClusterIntegration|Request to begin installing failed'),
|
||||
);
|
||||
static validateInstallation(appId, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (appId === CROSSPLANE && !params.stack) {
|
||||
reject(s__('ClusterIntegration|Select a stack to install Crossplane.'));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -458,6 +482,12 @@ export default class Clusters {
|
|||
this.store.updateAppProperty(appId, 'hostname', data.hostname);
|
||||
}
|
||||
|
||||
setCrossplaneProviderStack(data) {
|
||||
const appId = data.id;
|
||||
this.store.updateAppProperty(appId, 'stack', data.stack.code);
|
||||
this.store.updateAppProperty(appId, 'validationError', null);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.destroyed = true;
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ import jeagerLogo from 'images/cluster_app_logos/jeager.png';
|
|||
import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
|
||||
import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
|
||||
import certManagerLogo from 'images/cluster_app_logos/cert_manager.png';
|
||||
import crossplaneLogo from 'images/cluster_app_logos/crossplane.png';
|
||||
import knativeLogo from 'images/cluster_app_logos/knative.png';
|
||||
import meltanoLogo from 'images/cluster_app_logos/meltano.png';
|
||||
import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
|
||||
import elasticStackLogo from 'images/cluster_app_logos/elastic_stack.png';
|
||||
import { s__, sprintf } from '../../locale';
|
||||
import applicationRow from './application_row.vue';
|
||||
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
|
||||
|
@ -19,6 +21,7 @@ import KnativeDomainEditor from './knative_domain_editor.vue';
|
|||
import { CLUSTER_TYPE, PROVIDER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants';
|
||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||
import eventHub from '~/clusters/event_hub';
|
||||
import CrossplaneProviderStack from './crossplane_provider_stack.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -27,6 +30,7 @@ export default {
|
|||
LoadingButton,
|
||||
GlLoadingIcon,
|
||||
KnativeDomainEditor,
|
||||
CrossplaneProviderStack,
|
||||
},
|
||||
props: {
|
||||
type: {
|
||||
|
@ -88,9 +92,11 @@ export default {
|
|||
jupyterhubLogo,
|
||||
kubernetesLogo,
|
||||
certManagerLogo,
|
||||
crossplaneLogo,
|
||||
knativeLogo,
|
||||
meltanoLogo,
|
||||
prometheusLogo,
|
||||
elasticStackLogo,
|
||||
}),
|
||||
computed: {
|
||||
isProjectCluster() {
|
||||
|
@ -114,6 +120,15 @@ export default {
|
|||
certManagerInstalled() {
|
||||
return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED;
|
||||
},
|
||||
crossplaneInstalled() {
|
||||
return this.applications.crossplane.status === APPLICATION_STATUS.INSTALLED;
|
||||
},
|
||||
enableClusterApplicationCrossplane() {
|
||||
return gon.features && gon.features.enableClusterApplicationCrossplane;
|
||||
},
|
||||
enableClusterApplicationElasticStack() {
|
||||
return gon.features && gon.features.enableClusterApplicationElasticStack;
|
||||
},
|
||||
ingressDescription() {
|
||||
return sprintf(
|
||||
_.escape(
|
||||
|
@ -146,6 +161,24 @@ export default {
|
|||
false,
|
||||
);
|
||||
},
|
||||
crossplaneDescription() {
|
||||
return sprintf(
|
||||
_.escape(
|
||||
s__(
|
||||
`ClusterIntegration|Crossplane enables declarative provisioning of managed services from your cloud of choice using %{kubectl} or %{gitlabIntegrationLink}.
|
||||
Crossplane runs inside your Kubernetes cluster and supports secure connectivity and secrets management between app containers and the cloud services they depend on.`,
|
||||
),
|
||||
),
|
||||
{
|
||||
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ee/user/clusters/applications.html#crossplane"
|
||||
target="_blank" rel="noopener noreferrer">
|
||||
${_.escape(s__('ClusterIntegration|Gitlab Integration'))}</a>`,
|
||||
kubectl: `<code>kubectl</code>`,
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
|
||||
prometheusDescription() {
|
||||
return sprintf(
|
||||
_.escape(
|
||||
|
@ -168,9 +201,18 @@ export default {
|
|||
jupyterHostname() {
|
||||
return this.applications.jupyter.hostname;
|
||||
},
|
||||
elasticStackInstalled() {
|
||||
return this.applications.elastic_stack.status === APPLICATION_STATUS.INSTALLED;
|
||||
},
|
||||
elasticStackKibanaHostname() {
|
||||
return this.applications.elastic_stack.kibana_hostname;
|
||||
},
|
||||
knative() {
|
||||
return this.applications.knative;
|
||||
},
|
||||
crossplane() {
|
||||
return this.applications.crossplane;
|
||||
},
|
||||
cloudRun() {
|
||||
return this.providerType === PROVIDER_TYPE.GCP && this.preInstalledKnative;
|
||||
},
|
||||
|
@ -207,6 +249,12 @@ export default {
|
|||
hostname,
|
||||
});
|
||||
},
|
||||
setCrossplaneProviderStack(stack) {
|
||||
eventHub.$emit('setCrossplaneProviderStack', {
|
||||
id: 'crossplane',
|
||||
stack,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -217,7 +265,7 @@ export default {
|
|||
<p class="append-bottom-0">
|
||||
{{
|
||||
s__(`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.
|
||||
Helm Tiller is required to install any of the following applications.`)
|
||||
Helm Tiller is required to install any of the following applications.`)
|
||||
}}
|
||||
<a :href="helpPath">{{ __('More information') }}</a>
|
||||
</p>
|
||||
|
@ -242,9 +290,9 @@ export default {
|
|||
<div slot="description">
|
||||
{{
|
||||
s__(`ClusterIntegration|Helm streamlines installing
|
||||
and managing Kubernetes applications.
|
||||
Tiller runs inside of your Kubernetes Cluster,
|
||||
and manages releases of your charts.`)
|
||||
and managing Kubernetes applications.
|
||||
Tiller runs inside of your Kubernetes Cluster,
|
||||
and manages releases of your charts.`)
|
||||
}}
|
||||
</div>
|
||||
</application-row>
|
||||
|
@ -252,7 +300,7 @@ export default {
|
|||
<div class="svg-container" v-html="helmInstallIllustration"></div>
|
||||
{{
|
||||
s__(`ClusterIntegration|You must first install Helm Tiller before
|
||||
installing the applications below`)
|
||||
installing the applications below`)
|
||||
}}
|
||||
</div>
|
||||
<application-row
|
||||
|
@ -275,8 +323,8 @@ export default {
|
|||
<p>
|
||||
{{
|
||||
s__(`ClusterIntegration|Ingress gives you a way to route
|
||||
requests to services based on the request host or path,
|
||||
centralizing a number of services into a single entrypoint.`)
|
||||
requests to services based on the request host or path,
|
||||
centralizing a number of services into a single entrypoint.`)
|
||||
}}
|
||||
</p>
|
||||
|
||||
|
@ -308,8 +356,8 @@ export default {
|
|||
<p class="form-text text-muted">
|
||||
{{
|
||||
s__(`ClusterIntegration|Point a wildcard DNS to this
|
||||
generated endpoint in order to access
|
||||
your application after it has been deployed.`)
|
||||
generated endpoint in order to access
|
||||
your application after it has been deployed.`)
|
||||
}}
|
||||
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
|
||||
{{ __('More information') }}
|
||||
|
@ -320,8 +368,8 @@ export default {
|
|||
<p v-if="!ingressExternalEndpoint" class="settings-message js-no-endpoint-message">
|
||||
{{
|
||||
s__(`ClusterIntegration|The endpoint is in
|
||||
the process of being assigned. Please check your Kubernetes
|
||||
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
|
||||
the process of being assigned. Please check your Kubernetes
|
||||
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
|
||||
}}
|
||||
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
|
||||
{{ __('More information') }}
|
||||
|
@ -368,7 +416,7 @@ export default {
|
|||
<p class="form-text text-muted">
|
||||
{{
|
||||
s__(`ClusterIntegration|Issuers represent a certificate authority.
|
||||
You must provide an email address for your Issuer. `)
|
||||
You must provide an email address for your Issuer. `)
|
||||
}}
|
||||
<a
|
||||
href="http://docs.cert-manager.io/en/latest/reference/issuers.html?highlight=email"
|
||||
|
@ -424,12 +472,40 @@ export default {
|
|||
<div slot="description">
|
||||
{{
|
||||
s__(`ClusterIntegration|GitLab Runner connects to the
|
||||
repository and executes CI/CD jobs,
|
||||
pushing results back and deploying
|
||||
applications to production.`)
|
||||
repository and executes CI/CD jobs,
|
||||
pushing results back and deploying
|
||||
applications to production.`)
|
||||
}}
|
||||
</div>
|
||||
</application-row>
|
||||
<application-row
|
||||
v-if="enableClusterApplicationCrossplane"
|
||||
id="crossplane"
|
||||
:logo-url="crossplaneLogo"
|
||||
:title="applications.crossplane.title"
|
||||
:status="applications.crossplane.status"
|
||||
:status-reason="applications.crossplane.statusReason"
|
||||
:request-status="applications.crossplane.requestStatus"
|
||||
:request-reason="applications.crossplane.requestReason"
|
||||
:installed="applications.crossplane.installed"
|
||||
:install-failed="applications.crossplane.installFailed"
|
||||
:uninstallable="applications.crossplane.uninstallable"
|
||||
:uninstall-successful="applications.crossplane.uninstallSuccessful"
|
||||
:uninstall-failed="applications.crossplane.uninstallFailed"
|
||||
:install-application-request-params="{ stack: applications.crossplane.stack }"
|
||||
:disabled="!helmInstalled"
|
||||
title-link="https://crossplane.io"
|
||||
>
|
||||
<template>
|
||||
<div slot="description">
|
||||
<p v-html="crossplaneDescription"></p>
|
||||
<div class="form-group">
|
||||
<CrossplaneProviderStack :crossplane="crossplane" @set="setCrossplaneProviderStack" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</application-row>
|
||||
|
||||
<application-row
|
||||
id="jupyter"
|
||||
:logo-url="jupyterhubLogo"
|
||||
|
@ -451,10 +527,10 @@ export default {
|
|||
<p>
|
||||
{{
|
||||
s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns,
|
||||
manages, and proxies multiple instances of the single-user
|
||||
Jupyter notebook server. JupyterHub can be used to serve
|
||||
notebooks to a class of students, a corporate data science group,
|
||||
or a scientific research group.`)
|
||||
manages, and proxies multiple instances of the single-user
|
||||
Jupyter notebook server. JupyterHub can be used to serve
|
||||
notebooks to a class of students, a corporate data science group,
|
||||
or a scientific research group.`)
|
||||
}}
|
||||
</p>
|
||||
|
||||
|
@ -481,7 +557,7 @@ export default {
|
|||
<p v-if="ingressInstalled" class="form-text text-muted">
|
||||
{{
|
||||
s__(`ClusterIntegration|Replace this with your own hostname if you want.
|
||||
If you do so, point hostname to Ingress IP Address from above.`)
|
||||
If you do so, point hostname to Ingress IP Address from above.`)
|
||||
}}
|
||||
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
|
||||
{{ __('More information') }}
|
||||
|
@ -527,9 +603,9 @@ export default {
|
|||
<p>
|
||||
{{
|
||||
s__(`ClusterIntegration|Knative extends Kubernetes to provide
|
||||
a set of middleware components that are essential to build modern,
|
||||
source-centric, and container-based applications that can run
|
||||
anywhere: on premises, in the cloud, or even in a third-party data center.`)
|
||||
a set of middleware components that are essential to build modern,
|
||||
source-centric, and container-based applications that can run
|
||||
anywhere: on premises, in the cloud, or even in a third-party data center.`)
|
||||
}}
|
||||
</p>
|
||||
|
||||
|
@ -542,6 +618,75 @@ export default {
|
|||
/>
|
||||
</div>
|
||||
</application-row>
|
||||
<application-row
|
||||
v-if="enableClusterApplicationElasticStack"
|
||||
id="elastic_stack"
|
||||
:logo-url="elasticStackLogo"
|
||||
:title="applications.elastic_stack.title"
|
||||
:status="applications.elastic_stack.status"
|
||||
:status-reason="applications.elastic_stack.statusReason"
|
||||
:request-status="applications.elastic_stack.requestStatus"
|
||||
:request-reason="applications.elastic_stack.requestReason"
|
||||
:version="applications.elastic_stack.version"
|
||||
:chart-repo="applications.elastic_stack.chartRepo"
|
||||
:update-available="applications.elastic_stack.updateAvailable"
|
||||
:installed="applications.elastic_stack.installed"
|
||||
:install-failed="applications.elastic_stack.installFailed"
|
||||
:update-successful="applications.elastic_stack.updateSuccessful"
|
||||
:update-failed="applications.elastic_stack.updateFailed"
|
||||
:uninstallable="applications.elastic_stack.uninstallable"
|
||||
:uninstall-successful="applications.elastic_stack.uninstallSuccessful"
|
||||
:uninstall-failed="applications.elastic_stack.uninstallFailed"
|
||||
:disabled="!helmInstalled"
|
||||
:install-application-request-params="{
|
||||
kibana_hostname: applications.elastic_stack.kibana_hostname,
|
||||
}"
|
||||
title-link="https://github.com/helm/charts/tree/master/stable/elastic-stack"
|
||||
>
|
||||
<div slot="description">
|
||||
<p>
|
||||
{{
|
||||
s__(
|
||||
`ClusterIntegration|The elastic stack collects logs from all pods in your cluster`,
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
|
||||
<template v-if="ingressExternalEndpoint">
|
||||
<div class="form-group">
|
||||
<label for="elastic-stack-kibana-hostname">{{
|
||||
s__('ClusterIntegration|Kibana Hostname')
|
||||
}}</label>
|
||||
|
||||
<div class="input-group">
|
||||
<input
|
||||
v-model="applications.elastic_stack.kibana_hostname"
|
||||
:readonly="elasticStackInstalled"
|
||||
type="text"
|
||||
class="form-control js-hostname"
|
||||
/>
|
||||
<span class="input-group-btn">
|
||||
<clipboard-button
|
||||
:text="elasticStackKibanaHostname"
|
||||
:title="s__('ClusterIntegration|Copy Kibana Hostname')"
|
||||
class="js-clipboard-btn"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p v-if="ingressInstalled" class="form-text text-muted">
|
||||
{{
|
||||
s__(`ClusterIntegration|Replace this with your own hostname if you want.
|
||||
If you do so, point hostname to Ingress IP Address from above.`)
|
||||
}}
|
||||
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
|
||||
{{ __('More information') }}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</application-row>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import { s__ } from '../../locale';
|
||||
|
||||
export default {
|
||||
name: 'CrossplaneProviderStack',
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
Icon,
|
||||
},
|
||||
props: {
|
||||
stacks: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [
|
||||
{
|
||||
name: s__('Google Cloud Platform'),
|
||||
code: 'gcp',
|
||||
},
|
||||
{
|
||||
name: s__('Amazon Web Services'),
|
||||
code: 'aws',
|
||||
},
|
||||
{
|
||||
name: s__('Microsoft Azure'),
|
||||
code: 'azure',
|
||||
},
|
||||
{
|
||||
name: s__('Rook'),
|
||||
code: 'rook',
|
||||
},
|
||||
],
|
||||
},
|
||||
crossplane: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
dropdownText() {
|
||||
const result = this.stacks.reduce((map, obj) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
map[obj.code] = obj.name;
|
||||
return map;
|
||||
}, {});
|
||||
const { stack } = this.crossplane;
|
||||
if (stack !== '') {
|
||||
return result[stack];
|
||||
}
|
||||
return s__('Select Stack');
|
||||
},
|
||||
validationError() {
|
||||
return this.crossplane.validationError;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
selectStack(stack) {
|
||||
this.$emit('set', stack);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<label>
|
||||
{{ s__('ClusterIntegration|Enabled stack') }}
|
||||
</label>
|
||||
<gl-dropdown
|
||||
:disabled="crossplane.installed"
|
||||
:text="dropdownText"
|
||||
toggle-class="dropdown-menu-toggle gl-field-error-outline"
|
||||
class="w-100"
|
||||
:class="{ 'gl-show-field-errors': validationError }"
|
||||
>
|
||||
<gl-dropdown-item v-for="stack in stacks" :key="stack.code" @click="selectStack(stack)">
|
||||
<span class="ml-1">{{ stack.name }}</span>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
<span v-if="validationError" class="gl-field-error">{{ validationError }}</span>
|
||||
<p class="form-text text-muted">
|
||||
{{ s__(`You must select a stack for configuring your cloud provider. Learn more about`) }}
|
||||
<a
|
||||
href="https://crossplane.io/docs/master/stacks-guide.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>{{ __('Crossplane') }}</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
|
@ -2,7 +2,16 @@
|
|||
import { GlModal } from '@gitlab/ui';
|
||||
import { sprintf, s__ } from '~/locale';
|
||||
import trackUninstallButtonClickMixin from 'ee_else_ce/clusters/mixins/track_uninstall_button_click';
|
||||
import { HELM, INGRESS, CERT_MANAGER, PROMETHEUS, RUNNER, KNATIVE, JUPYTER } from '../constants';
|
||||
import {
|
||||
HELM,
|
||||
INGRESS,
|
||||
CERT_MANAGER,
|
||||
PROMETHEUS,
|
||||
RUNNER,
|
||||
KNATIVE,
|
||||
JUPYTER,
|
||||
ELASTIC_STACK,
|
||||
} from '../constants';
|
||||
|
||||
const CUSTOM_APP_WARNING_TEXT = {
|
||||
[HELM]: sprintf(
|
||||
|
@ -28,6 +37,7 @@ const CUSTOM_APP_WARNING_TEXT = {
|
|||
[JUPYTER]: s__(
|
||||
'ClusterIntegration|All data not committed to GitLab will be deleted and cannot be restored.',
|
||||
),
|
||||
[ELASTIC_STACK]: s__('ClusterIntegration|All data will be deleted and cannot be restored.'),
|
||||
};
|
||||
|
||||
export default {
|
||||
|
|
|
@ -50,8 +50,19 @@ export const JUPYTER = 'jupyter';
|
|||
export const KNATIVE = 'knative';
|
||||
export const RUNNER = 'runner';
|
||||
export const CERT_MANAGER = 'cert_manager';
|
||||
export const CROSSPLANE = 'crossplane';
|
||||
export const PROMETHEUS = 'prometheus';
|
||||
export const ELASTIC_STACK = 'elastic_stack';
|
||||
|
||||
export const APPLICATIONS = [HELM, INGRESS, JUPYTER, KNATIVE, RUNNER, CERT_MANAGER, PROMETHEUS];
|
||||
export const APPLICATIONS = [
|
||||
HELM,
|
||||
INGRESS,
|
||||
JUPYTER,
|
||||
KNATIVE,
|
||||
RUNNER,
|
||||
CERT_MANAGER,
|
||||
PROMETHEUS,
|
||||
ELASTIC_STACK,
|
||||
];
|
||||
|
||||
export const INGRESS_DOMAIN_SUFFIX = '.nip.io';
|
||||
|
|
|
@ -7,10 +7,12 @@ export default class ClusterService {
|
|||
helm: this.options.installHelmEndpoint,
|
||||
ingress: this.options.installIngressEndpoint,
|
||||
cert_manager: this.options.installCertManagerEndpoint,
|
||||
crossplane: this.options.installCrossplaneEndpoint,
|
||||
runner: this.options.installRunnerEndpoint,
|
||||
prometheus: this.options.installPrometheusEndpoint,
|
||||
jupyter: this.options.installJupyterEndpoint,
|
||||
knative: this.options.installKnativeEndpoint,
|
||||
elastic_stack: this.options.installElasticStackEndpoint,
|
||||
};
|
||||
this.appUpdateEndpointMap = {
|
||||
knative: this.options.updateKnativeEndpoint,
|
||||
|
|
|
@ -5,6 +5,8 @@ import {
|
|||
JUPYTER,
|
||||
KNATIVE,
|
||||
CERT_MANAGER,
|
||||
ELASTIC_STACK,
|
||||
CROSSPLANE,
|
||||
RUNNER,
|
||||
APPLICATION_INSTALLED_STATUSES,
|
||||
APPLICATION_STATUS,
|
||||
|
@ -25,6 +27,7 @@ const applicationInitialState = {
|
|||
uninstallable: false,
|
||||
uninstallFailed: false,
|
||||
uninstallSuccessful: false,
|
||||
validationError: null,
|
||||
};
|
||||
|
||||
export default class ClusterStore {
|
||||
|
@ -57,6 +60,11 @@ export default class ClusterStore {
|
|||
title: s__('ClusterIntegration|Cert-Manager'),
|
||||
email: null,
|
||||
},
|
||||
crossplane: {
|
||||
...applicationInitialState,
|
||||
title: s__('ClusterIntegration|Crossplane'),
|
||||
stack: null,
|
||||
},
|
||||
runner: {
|
||||
...applicationInitialState,
|
||||
title: s__('ClusterIntegration|GitLab Runner'),
|
||||
|
@ -85,6 +93,11 @@ export default class ClusterStore {
|
|||
updateSuccessful: false,
|
||||
updateFailed: false,
|
||||
},
|
||||
elastic_stack: {
|
||||
...applicationInitialState,
|
||||
title: s__('ClusterIntegration|Elastic Stack'),
|
||||
kibana_hostname: null,
|
||||
},
|
||||
},
|
||||
environments: [],
|
||||
fetchingEnvironments: false,
|
||||
|
@ -197,13 +210,15 @@ export default class ClusterStore {
|
|||
} else if (appId === CERT_MANAGER) {
|
||||
this.state.applications.cert_manager.email =
|
||||
this.state.applications.cert_manager.email || serverAppEntry.email;
|
||||
} else if (appId === CROSSPLANE) {
|
||||
this.state.applications.crossplane.stack =
|
||||
this.state.applications.crossplane.stack || serverAppEntry.stack;
|
||||
} else if (appId === JUPYTER) {
|
||||
this.state.applications.jupyter.hostname =
|
||||
this.state.applications.jupyter.hostname ||
|
||||
serverAppEntry.hostname ||
|
||||
(this.state.applications.ingress.externalIp
|
||||
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
|
||||
: '');
|
||||
this.state.applications.jupyter.hostname = this.updateHostnameIfUnset(
|
||||
this.state.applications.jupyter.hostname,
|
||||
serverAppEntry.hostname,
|
||||
'jupyter',
|
||||
);
|
||||
} else if (appId === KNATIVE) {
|
||||
if (!this.state.applications.knative.isEditingHostName) {
|
||||
this.state.applications.knative.hostname =
|
||||
|
@ -216,10 +231,26 @@ export default class ClusterStore {
|
|||
} else if (appId === RUNNER) {
|
||||
this.state.applications.runner.version = version;
|
||||
this.state.applications.runner.updateAvailable = updateAvailable;
|
||||
} else if (appId === ELASTIC_STACK) {
|
||||
this.state.applications.elastic_stack.kibana_hostname = this.updateHostnameIfUnset(
|
||||
this.state.applications.elastic_stack.kibana_hostname,
|
||||
serverAppEntry.kibana_hostname,
|
||||
'kibana',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateHostnameIfUnset(current, updated, fallback) {
|
||||
return (
|
||||
current ||
|
||||
updated ||
|
||||
(this.state.applications.ingress.externalIp
|
||||
? `${fallback}.${this.state.applications.ingress.externalIp}.nip.io`
|
||||
: '')
|
||||
);
|
||||
}
|
||||
|
||||
toggleFetchEnvironments(isFetching) {
|
||||
this.state.fetchingEnvironments = isFetching;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign, no-unused-expressions, no-sequences */
|
||||
/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign */
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
|
@ -9,40 +9,29 @@ const viewModes = ['two-up', 'swipe'];
|
|||
export default class ImageFile {
|
||||
constructor(file) {
|
||||
this.file = file;
|
||||
this.requestImageInfo(
|
||||
$('.two-up.view .frame.deleted img', this.file),
|
||||
(function(_this) {
|
||||
return function() {
|
||||
return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), () => {
|
||||
_this.initViewModes();
|
||||
this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), () =>
|
||||
this.requestImageInfo($('.two-up.view .frame.added img', this.file), () => {
|
||||
this.initViewModes();
|
||||
|
||||
// Load two-up view after images are loaded
|
||||
// so that we can display the correct width and height information
|
||||
const $images = $('.two-up.view img', _this.file);
|
||||
// Load two-up view after images are loaded
|
||||
// so that we can display the correct width and height information
|
||||
const $images = $('.two-up.view img', this.file);
|
||||
|
||||
$images.waitForImages(() => {
|
||||
_this.initView('two-up');
|
||||
});
|
||||
});
|
||||
};
|
||||
})(this),
|
||||
$images.waitForImages(() => {
|
||||
this.initView('two-up');
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
initViewModes() {
|
||||
const viewMode = viewModes[0];
|
||||
$('.view-modes', this.file).removeClass('hide');
|
||||
$('.view-modes-menu', this.file).on(
|
||||
'click',
|
||||
'li',
|
||||
(function(_this) {
|
||||
return function(event) {
|
||||
if (!$(event.currentTarget).hasClass('active')) {
|
||||
return _this.activateViewMode(event.currentTarget.className);
|
||||
}
|
||||
};
|
||||
})(this),
|
||||
);
|
||||
$('.view-modes-menu', this.file).on('click', 'li', event => {
|
||||
if (!$(event.currentTarget).hasClass('active')) {
|
||||
return this.activateViewMode(event.currentTarget.className);
|
||||
}
|
||||
});
|
||||
return this.activateViewMode(viewMode);
|
||||
}
|
||||
|
||||
|
@ -51,15 +40,10 @@ export default class ImageFile {
|
|||
.removeClass('active')
|
||||
.filter(`.${viewMode}`)
|
||||
.addClass('active');
|
||||
return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut(
|
||||
200,
|
||||
(function(_this) {
|
||||
return function() {
|
||||
$(`.view.${viewMode}`, _this.file).fadeIn(200);
|
||||
return _this.initView(viewMode);
|
||||
};
|
||||
})(this),
|
||||
);
|
||||
return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut(200, () => {
|
||||
$(`.view.${viewMode}`, this.file).fadeIn(200);
|
||||
return this.initView(viewMode);
|
||||
});
|
||||
}
|
||||
|
||||
initView(viewMode) {
|
||||
|
@ -103,22 +87,18 @@ export default class ImageFile {
|
|||
.on('touchmove', dragMove);
|
||||
}
|
||||
|
||||
prepareFrames(view) {
|
||||
static prepareFrames(view) {
|
||||
var maxHeight, maxWidth;
|
||||
maxWidth = 0;
|
||||
maxHeight = 0;
|
||||
$('.frame', view)
|
||||
.each(
|
||||
(function() {
|
||||
return function(index, frame) {
|
||||
var height, width;
|
||||
width = $(frame).width();
|
||||
height = $(frame).height();
|
||||
maxWidth = width > maxWidth ? width : maxWidth;
|
||||
return (maxHeight = height > maxHeight ? height : maxHeight);
|
||||
};
|
||||
})(this),
|
||||
)
|
||||
.each((index, frame) => {
|
||||
var height, width;
|
||||
width = $(frame).width();
|
||||
height = $(frame).height();
|
||||
maxWidth = width > maxWidth ? width : maxWidth;
|
||||
return (maxHeight = height > maxHeight ? height : maxHeight);
|
||||
})
|
||||
.css({
|
||||
width: maxWidth,
|
||||
height: maxHeight,
|
||||
|
@ -128,104 +108,95 @@ export default class ImageFile {
|
|||
|
||||
views = {
|
||||
'two-up': function() {
|
||||
return $('.two-up.view .wrap', this.file).each(
|
||||
(function(_this) {
|
||||
return function(index, wrap) {
|
||||
$('img', wrap).each(function() {
|
||||
var currentWidth;
|
||||
currentWidth = $(this).width();
|
||||
if (currentWidth > availWidth / 2) {
|
||||
return $(this).width(availWidth / 2);
|
||||
}
|
||||
});
|
||||
return _this.requestImageInfo($('img', wrap), (width, height) => {
|
||||
$('.image-info .meta-width', wrap).text(`${width}px`);
|
||||
$('.image-info .meta-height', wrap).text(`${height}px`);
|
||||
return $('.image-info', wrap).removeClass('hide');
|
||||
});
|
||||
};
|
||||
})(this),
|
||||
);
|
||||
return $('.two-up.view .wrap', this.file).each((index, wrap) => {
|
||||
$('img', wrap).each(function() {
|
||||
var currentWidth;
|
||||
currentWidth = $(this).width();
|
||||
if (currentWidth > availWidth / 2) {
|
||||
return $(this).width(availWidth / 2);
|
||||
}
|
||||
});
|
||||
return this.requestImageInfo($('img', wrap), (width, height) => {
|
||||
$('.image-info .meta-width', wrap).text(`${width}px`);
|
||||
$('.image-info .meta-height', wrap).text(`${height}px`);
|
||||
return $('.image-info', wrap).removeClass('hide');
|
||||
});
|
||||
});
|
||||
},
|
||||
swipe() {
|
||||
var maxHeight, maxWidth;
|
||||
maxWidth = 0;
|
||||
maxHeight = 0;
|
||||
return $('.swipe.view', this.file).each(
|
||||
(function(_this) {
|
||||
return function(index, view) {
|
||||
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
|
||||
(ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
|
||||
$swipeFrame = $('.swipe-frame', view);
|
||||
$swipeWrap = $('.swipe-wrap', view);
|
||||
$swipeBar = $('.swipe-bar', view);
|
||||
return $('.swipe.view', this.file).each((index, view) => {
|
||||
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding;
|
||||
const ref = ImageFile.prepareFrames(view);
|
||||
[maxWidth, maxHeight] = ref;
|
||||
$swipeFrame = $('.swipe-frame', view);
|
||||
$swipeWrap = $('.swipe-wrap', view);
|
||||
$swipeBar = $('.swipe-bar', view);
|
||||
|
||||
$swipeFrame.css({
|
||||
width: maxWidth + 16,
|
||||
height: maxHeight + 28,
|
||||
});
|
||||
$swipeWrap.css({
|
||||
width: maxWidth + 1,
|
||||
height: maxHeight + 2,
|
||||
});
|
||||
// Set swipeBar left position to match image frame
|
||||
$swipeBar.css({
|
||||
left: 1,
|
||||
});
|
||||
$swipeFrame.css({
|
||||
width: maxWidth + 16,
|
||||
height: maxHeight + 28,
|
||||
});
|
||||
$swipeWrap.css({
|
||||
width: maxWidth + 1,
|
||||
height: maxHeight + 2,
|
||||
});
|
||||
// Set swipeBar left position to match image frame
|
||||
$swipeBar.css({
|
||||
left: 1,
|
||||
});
|
||||
|
||||
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
|
||||
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
|
||||
|
||||
_this.initDraggable($swipeBar, wrapPadding, (e, left) => {
|
||||
if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
|
||||
$swipeWrap.width(maxWidth + 1 - left);
|
||||
$swipeBar.css('left', left);
|
||||
}
|
||||
});
|
||||
};
|
||||
})(this),
|
||||
);
|
||||
this.initDraggable($swipeBar, wrapPadding, (e, left) => {
|
||||
if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
|
||||
$swipeWrap.width(maxWidth + 1 - left);
|
||||
$swipeBar.css('left', left);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
'onion-skin': function() {
|
||||
var dragTrackWidth, maxHeight, maxWidth;
|
||||
maxWidth = 0;
|
||||
maxHeight = 0;
|
||||
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
|
||||
return $('.onion-skin.view', this.file).each(
|
||||
(function(_this) {
|
||||
return function(index, view) {
|
||||
var $frame, $track, $dragger, $frameAdded, framePadding, ref;
|
||||
(ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
|
||||
$frame = $('.onion-skin-frame', view);
|
||||
$frameAdded = $('.frame.added', view);
|
||||
$track = $('.drag-track', view);
|
||||
$dragger = $('.dragger', $track);
|
||||
return $('.onion-skin.view', this.file).each((index, view) => {
|
||||
var $frame, $track, $dragger, $frameAdded, framePadding;
|
||||
|
||||
$frame.css({
|
||||
width: maxWidth + 16,
|
||||
height: maxHeight + 28,
|
||||
});
|
||||
$('.swipe-wrap', view).css({
|
||||
width: maxWidth + 1,
|
||||
height: maxHeight + 2,
|
||||
});
|
||||
$dragger.css({
|
||||
left: dragTrackWidth,
|
||||
});
|
||||
const ref = ImageFile.prepareFrames(view);
|
||||
[maxWidth, maxHeight] = ref;
|
||||
$frame = $('.onion-skin-frame', view);
|
||||
$frameAdded = $('.frame.added', view);
|
||||
$track = $('.drag-track', view);
|
||||
$dragger = $('.dragger', $track);
|
||||
|
||||
$frameAdded.css('opacity', 1);
|
||||
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
|
||||
$frame.css({
|
||||
width: maxWidth + 16,
|
||||
height: maxHeight + 28,
|
||||
});
|
||||
$('.swipe-wrap', view).css({
|
||||
width: maxWidth + 1,
|
||||
height: maxHeight + 2,
|
||||
});
|
||||
$dragger.css({
|
||||
left: dragTrackWidth,
|
||||
});
|
||||
|
||||
_this.initDraggable($dragger, framePadding, (e, left) => {
|
||||
var opacity = left / dragTrackWidth;
|
||||
$frameAdded.css('opacity', 1);
|
||||
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
|
||||
|
||||
if (opacity >= 0 && opacity <= 1) {
|
||||
$dragger.css('left', left);
|
||||
$frameAdded.css('opacity', opacity);
|
||||
}
|
||||
});
|
||||
};
|
||||
})(this),
|
||||
);
|
||||
this.initDraggable($dragger, framePadding, (e, left) => {
|
||||
var opacity = left / dragTrackWidth;
|
||||
|
||||
if (opacity >= 0 && opacity <= 1) {
|
||||
$dragger.css('left', left);
|
||||
$frameAdded.css('opacity', opacity);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -235,14 +206,7 @@ export default class ImageFile {
|
|||
if (domImg.complete) {
|
||||
return callback.call(this, domImg.naturalWidth, domImg.naturalHeight);
|
||||
} else {
|
||||
return img.on(
|
||||
'load',
|
||||
(function(_this) {
|
||||
return function() {
|
||||
return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight);
|
||||
};
|
||||
})(this),
|
||||
);
|
||||
return img.on('load', () => callback.call(this, domImg.naturalWidth, domImg.naturalHeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* eslint-disable func-names, one-var, no-var, no-else-return */
|
||||
/* eslint-disable func-names, no-else-return */
|
||||
|
||||
import $ from 'jquery';
|
||||
import { __ } from './locale';
|
||||
|
@ -8,9 +8,8 @@ import { capitalizeFirstCharacter } from './lib/utils/text_utility';
|
|||
|
||||
export default function initCompareAutocomplete(limitTo = null, clickHandler = () => {}) {
|
||||
$('.js-compare-dropdown').each(function() {
|
||||
var $dropdown, selected;
|
||||
$dropdown = $(this);
|
||||
selected = $dropdown.data('selected');
|
||||
const $dropdown = $(this);
|
||||
const selected = $dropdown.data('selected');
|
||||
const $dropdownContainer = $dropdown.closest('.dropdown');
|
||||
const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer);
|
||||
const $filterInput = $('input[type="search"]', $dropdownContainer);
|
||||
|
@ -44,17 +43,16 @@ export default function initCompareAutocomplete(limitTo = null, clickHandler = (
|
|||
fieldName: $dropdown.data('fieldName'),
|
||||
filterInput: 'input[type="search"]',
|
||||
renderRow(ref) {
|
||||
var link;
|
||||
const link = $('<a />')
|
||||
.attr('href', '#')
|
||||
.addClass(ref === selected ? 'is-active' : '')
|
||||
.text(ref)
|
||||
.attr('data-ref', ref);
|
||||
if (ref.header != null) {
|
||||
return $('<li />')
|
||||
.addClass('dropdown-header')
|
||||
.text(ref.header);
|
||||
} else {
|
||||
link = $('<a />')
|
||||
.attr('href', '#')
|
||||
.addClass(ref === selected ? 'is-active' : '')
|
||||
.text(ref)
|
||||
.attr('data-ref', ref);
|
||||
return $('<li />').append(link);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -41,7 +41,7 @@ export default {
|
|||
noForkText() {
|
||||
return sprintf(
|
||||
__(
|
||||
"To protect this issue's confidentiality, %{link_start}fork the project%{link_end} and set the forks visiblity to private.",
|
||||
"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,
|
||||
|
|
227
app/assets/javascripts/contributors/components/contributors.vue
Normal file
227
app/assets/javascripts/contributors/components/contributors.vue
Normal file
|
@ -0,0 +1,227 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
import _ from 'underscore';
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import { GlAreaChart } from '@gitlab/ui/dist/charts';
|
||||
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
|
||||
import { getDatesInRange } from '~/lib/utils/datetime_utility';
|
||||
import { xAxisLabelFormatter, dateFormatter } from '../utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlAreaChart,
|
||||
GlLoadingIcon,
|
||||
},
|
||||
props: {
|
||||
endpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
branch: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
masterChart: null,
|
||||
individualCharts: [],
|
||||
svgs: {},
|
||||
masterChartHeight: 264,
|
||||
individualChartHeight: 216,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['chartData', 'loading']),
|
||||
...mapGetters(['showChart', 'parsedData']),
|
||||
masterChartData() {
|
||||
const data = {};
|
||||
this.xAxisRange.forEach(date => {
|
||||
data[date] = this.parsedData.total[date] || 0;
|
||||
});
|
||||
return [
|
||||
{
|
||||
name: __('Commits'),
|
||||
data: Object.entries(data),
|
||||
},
|
||||
];
|
||||
},
|
||||
masterChartOptions() {
|
||||
return {
|
||||
...this.getCommonChartOptions(true),
|
||||
yAxis: {
|
||||
name: __('Number of commits'),
|
||||
},
|
||||
grid: {
|
||||
bottom: 64,
|
||||
left: 64,
|
||||
right: 20,
|
||||
top: 20,
|
||||
},
|
||||
};
|
||||
},
|
||||
individualChartsData() {
|
||||
const maxNumberOfIndividualContributorsCharts = 100;
|
||||
|
||||
return Object.keys(this.parsedData.byAuthor)
|
||||
.map(name => {
|
||||
const author = this.parsedData.byAuthor[name];
|
||||
return {
|
||||
name,
|
||||
email: author.email,
|
||||
commits: author.commits,
|
||||
dates: [
|
||||
{
|
||||
name: __('Commits'),
|
||||
data: this.xAxisRange.map(date => [date, author.dates[date] || 0]),
|
||||
},
|
||||
],
|
||||
};
|
||||
})
|
||||
.sort((a, b) => b.commits - a.commits)
|
||||
.slice(0, maxNumberOfIndividualContributorsCharts);
|
||||
},
|
||||
individualChartOptions() {
|
||||
return {
|
||||
...this.getCommonChartOptions(false),
|
||||
yAxis: {
|
||||
name: __('Commits'),
|
||||
max: this.individualChartYAxisMax,
|
||||
},
|
||||
grid: {
|
||||
bottom: 27,
|
||||
left: 64,
|
||||
right: 20,
|
||||
top: 8,
|
||||
},
|
||||
};
|
||||
},
|
||||
individualChartYAxisMax() {
|
||||
return this.individualChartsData.reduce((acc, item) => {
|
||||
const values = item.dates[0].data.map(value => value[1]);
|
||||
return Math.max(acc, ...values);
|
||||
}, 0);
|
||||
},
|
||||
xAxisRange() {
|
||||
const dates = Object.keys(this.parsedData.total).sort((a, b) => new Date(a) - new Date(b));
|
||||
|
||||
const firstContributionDate = new Date(dates[0]);
|
||||
const lastContributionDate = new Date(dates[dates.length - 1]);
|
||||
|
||||
return getDatesInRange(firstContributionDate, lastContributionDate, dateFormatter);
|
||||
},
|
||||
firstContributionDate() {
|
||||
return this.xAxisRange[0];
|
||||
},
|
||||
lastContributionDate() {
|
||||
return this.xAxisRange[this.xAxisRange.length - 1];
|
||||
},
|
||||
charts() {
|
||||
return _.uniq(this.individualCharts);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchChartData(this.endpoint);
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchChartData']),
|
||||
getCommonChartOptions(isMasterChart) {
|
||||
return {
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
name: '',
|
||||
data: this.xAxisRange,
|
||||
axisLabel: {
|
||||
formatter: xAxisLabelFormatter,
|
||||
showMaxLabel: false,
|
||||
showMinLabel: false,
|
||||
},
|
||||
boundaryGap: false,
|
||||
splitNumber: isMasterChart ? 24 : 18,
|
||||
// 28 days
|
||||
minInterval: 28 * 86400 * 1000,
|
||||
min: this.firstContributionDate,
|
||||
max: this.lastContributionDate,
|
||||
},
|
||||
};
|
||||
},
|
||||
setSvg(name) {
|
||||
return getSvgIconPathContent(name)
|
||||
.then(path => {
|
||||
if (path) {
|
||||
this.$set(this.svgs, name, `path://${path}`);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
onMasterChartCreated(chart) {
|
||||
this.masterChart = chart;
|
||||
this.setSvg('scroll-handle')
|
||||
.then(() => {
|
||||
this.masterChart.setOption({
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'slider',
|
||||
handleIcon: this.svgs['scroll-handle'],
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
this.masterChart.on('datazoom', _.debounce(this.setIndividualChartsZoom, 200));
|
||||
},
|
||||
onIndividualChartCreated(chart) {
|
||||
this.individualCharts.push(chart);
|
||||
},
|
||||
setIndividualChartsZoom(options) {
|
||||
this.charts.forEach(chart =>
|
||||
chart.setOption(
|
||||
{
|
||||
dataZoom: {
|
||||
start: options.start,
|
||||
end: options.end,
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
{ lazyUpdate: true },
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="loading" class="contributors-loader text-center">
|
||||
<gl-loading-icon :inline="true" :size="4" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="showChart" class="contributors-charts">
|
||||
<h4>{{ __('Commits to') }} {{ branch }}</h4>
|
||||
<span>{{ __('Excluding merge commits. Limited to 6,000 commits.') }}</span>
|
||||
<div>
|
||||
<gl-area-chart
|
||||
:data="masterChartData"
|
||||
:option="masterChartOptions"
|
||||
:height="masterChartHeight"
|
||||
@created="onMasterChartCreated"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div v-for="contributor in individualChartsData" :key="contributor.name" class="col-6">
|
||||
<h4>{{ contributor.name }}</h4>
|
||||
<p>{{ n__('%d commit', '%d commits', contributor.commits) }} ({{ contributor.email }})</p>
|
||||
<gl-area-chart
|
||||
:data="contributor.dates"
|
||||
:option="individualChartOptions"
|
||||
:height="individualChartHeight"
|
||||
@created="onIndividualChartCreated"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
23
app/assets/javascripts/contributors/index.js
Normal file
23
app/assets/javascripts/contributors/index.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import Vue from 'vue';
|
||||
import ContributorsGraphs from './components/contributors.vue';
|
||||
import store from './stores';
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-contributors-graph');
|
||||
|
||||
if (!el) return null;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
store,
|
||||
|
||||
render(createElement) {
|
||||
return createElement(ContributorsGraphs, {
|
||||
props: {
|
||||
endpoint: el.dataset.projectGraphPath,
|
||||
branch: el.dataset.projectBranch,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
export default {
|
||||
fetchChartData(endpoint) {
|
||||
return axios.get(endpoint);
|
||||
},
|
||||
};
|
20
app/assets/javascripts/contributors/stores/actions.js
Normal file
20
app/assets/javascripts/contributors/stores/actions.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import flash from '~/flash';
|
||||
import { __ } from '~/locale';
|
||||
import service from '../services/contributors_service';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
export const fetchChartData = ({ commit }, endpoint) => {
|
||||
commit(types.SET_LOADING_STATE, true);
|
||||
|
||||
return service
|
||||
.fetchChartData(endpoint)
|
||||
.then(res => res.data)
|
||||
.then(data => {
|
||||
commit(types.SET_CHART_DATA, data);
|
||||
commit(types.SET_LOADING_STATE, false);
|
||||
})
|
||||
.catch(() => flash(__('An error occurred while loading chart data')));
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
33
app/assets/javascripts/contributors/stores/getters.js
Normal file
33
app/assets/javascripts/contributors/stores/getters.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
export const showChart = state => Boolean(!state.loading && state.chartData);
|
||||
|
||||
export const parsedData = state => {
|
||||
const byAuthor = {};
|
||||
const total = {};
|
||||
|
||||
state.chartData.forEach(({ date, author_name, author_email }) => {
|
||||
total[date] = total[date] ? total[date] + 1 : 1;
|
||||
|
||||
const authorData = byAuthor[author_name];
|
||||
|
||||
if (!authorData) {
|
||||
byAuthor[author_name] = {
|
||||
email: author_email.toLowerCase(),
|
||||
commits: 1,
|
||||
dates: {
|
||||
[date]: 1,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
authorData.commits += 1;
|
||||
authorData.dates[date] = authorData.dates[date] ? authorData.dates[date] + 1 : 1;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
total,
|
||||
byAuthor,
|
||||
};
|
||||
};
|
||||
|
||||
// prevent babel-plugin-rewire from generating an invalid default during karma tests
|
||||
export default () => {};
|
18
app/assets/javascripts/contributors/stores/index.js
Normal file
18
app/assets/javascripts/contributors/stores/index.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import state from './state';
|
||||
import mutations from './mutations';
|
||||
import * as getters from './getters';
|
||||
import * as actions from './actions';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export const createStore = () =>
|
||||
new Vuex.Store({
|
||||
actions,
|
||||
mutations,
|
||||
getters,
|
||||
state: state(),
|
||||
});
|
||||
|
||||
export default createStore();
|
|
@ -0,0 +1,3 @@
|
|||
export const SET_CHART_DATA = 'SET_CHART_DATA';
|
||||
export const SET_LOADING_STATE = 'SET_LOADING_STATE';
|
||||
export const SET_ACTIVE_BRANCH = 'SET_ACTIVE_BRANCH';
|
17
app/assets/javascripts/contributors/stores/mutations.js
Normal file
17
app/assets/javascripts/contributors/stores/mutations.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.SET_LOADING_STATE](state, value) {
|
||||
state.loading = value;
|
||||
},
|
||||
[types.SET_CHART_DATA](state, chartData) {
|
||||
Object.assign(state, {
|
||||
chartData,
|
||||
});
|
||||
},
|
||||
[types.SET_ACTIVE_BRANCH](state, branch) {
|
||||
Object.assign(state, {
|
||||
branch,
|
||||
});
|
||||
},
|
||||
};
|
5
app/assets/javascripts/contributors/stores/state.js
Normal file
5
app/assets/javascripts/contributors/stores/state.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default () => ({
|
||||
loading: false,
|
||||
chartData: null,
|
||||
branch: 'master',
|
||||
});
|
30
app/assets/javascripts/contributors/utils.js
Normal file
30
app/assets/javascripts/contributors/utils.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { getMonthNames } from '~/lib/utils/datetime_utility';
|
||||
|
||||
/**
|
||||
* Converts provided string to date and returns formatted value as a year for date in January and month name for the rest
|
||||
* @param {String}
|
||||
* @returns {String} - formatted value
|
||||
*
|
||||
* xAxisLabelFormatter('01-12-2019') will return '2019'
|
||||
* xAxisLabelFormatter('02-12-2019') will return 'Feb'
|
||||
* xAxisLabelFormatter('07-12-2019') will return 'Jul'
|
||||
*/
|
||||
export const xAxisLabelFormatter = val => {
|
||||
const date = new Date(val);
|
||||
const month = date.getUTCMonth();
|
||||
const year = date.getUTCFullYear();
|
||||
return month === 0 ? `${year}` : getMonthNames(true)[month];
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats provided date to YYYY-MM-DD format
|
||||
* @param {Date}
|
||||
* @returns {String} - formatted value
|
||||
*/
|
||||
export const dateFormatter = date => {
|
||||
const year = date.getUTCFullYear();
|
||||
const month = date.getUTCMonth();
|
||||
const day = date.getUTCDate();
|
||||
|
||||
return `${year}-${`0${month + 1}`.slice(-2)}-${`0${day}`.slice(-2)}`;
|
||||
};
|
|
@ -2,14 +2,19 @@
|
|||
import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
|
||||
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
|
||||
import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
|
||||
const findItem = (items, valueProp, value) => items.find(item => item[valueProp] === value);
|
||||
const toArray = value => [].concat(value);
|
||||
const itemsProp = (items, prop) => items.map(item => item[prop]);
|
||||
const defaultSearchFn = (searchQuery, labelProp) => item =>
|
||||
item[labelProp].toLowerCase().indexOf(searchQuery) > -1;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DropdownButton,
|
||||
DropdownSearchInput,
|
||||
DropdownHiddenInput,
|
||||
GlIcon,
|
||||
},
|
||||
props: {
|
||||
fieldName: {
|
||||
|
@ -28,7 +33,7 @@ export default {
|
|||
default: '',
|
||||
},
|
||||
value: {
|
||||
type: [Object, String],
|
||||
type: [Object, Array, String],
|
||||
required: false,
|
||||
default: () => null,
|
||||
},
|
||||
|
@ -72,6 +77,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
errorMessage: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -90,12 +100,11 @@ export default {
|
|||
searchFn: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: searchQuery => item => item.name.toLowerCase().indexOf(searchQuery) > -1,
|
||||
default: defaultSearchFn,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedItem: findItem(this.items, this.value),
|
||||
searchQuery: '',
|
||||
};
|
||||
},
|
||||
|
@ -109,36 +118,52 @@ export default {
|
|||
return this.disabledText;
|
||||
}
|
||||
|
||||
if (!this.selectedItem) {
|
||||
if (!this.selectedItems.length) {
|
||||
return this.placeholder;
|
||||
}
|
||||
|
||||
return this.selectedItemLabel;
|
||||
return this.selectedItemsLabels;
|
||||
},
|
||||
results() {
|
||||
if (!this.items) {
|
||||
return [];
|
||||
}
|
||||
return this.getItemsOrEmptyList().filter(this.searchFn(this.searchQuery, this.labelProperty));
|
||||
},
|
||||
selectedItems() {
|
||||
const valueProp = this.valueProperty;
|
||||
const valueList = toArray(this.value);
|
||||
const items = this.getItemsOrEmptyList();
|
||||
|
||||
return this.items.filter(this.searchFn(this.searchQuery));
|
||||
return items.filter(item => valueList.some(value => item[valueProp] === value));
|
||||
},
|
||||
selectedItemLabel() {
|
||||
return this.selectedItem && this.selectedItem[this.labelProperty];
|
||||
selectedItemsLabels() {
|
||||
return itemsProp(this.selectedItems, this.labelProperty).join(', ');
|
||||
},
|
||||
selectedItemValue() {
|
||||
return (this.selectedItem && this.selectedItem[this.valueProperty]) || '';
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
value(value) {
|
||||
this.selectedItem = findItem(this.items, this.valueProperty, value);
|
||||
selectedItemsValues() {
|
||||
return itemsProp(this.selectedItems, this.valueProperty).join(', ');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
select(item) {
|
||||
this.selectedItem = item;
|
||||
getItemsOrEmptyList() {
|
||||
return this.items || [];
|
||||
},
|
||||
selectSingle(item) {
|
||||
this.$emit('input', item[this.valueProperty]);
|
||||
},
|
||||
selectMultiple(item) {
|
||||
const value = toArray(this.value);
|
||||
const itemValue = item[this.valueProperty];
|
||||
const itemValueIndex = value.indexOf(itemValue);
|
||||
|
||||
if (itemValueIndex > -1) {
|
||||
value.splice(itemValueIndex, 1);
|
||||
} else {
|
||||
value.push(itemValue);
|
||||
}
|
||||
|
||||
this.$emit('input', value);
|
||||
},
|
||||
isSelected(item) {
|
||||
return this.selectedItems.includes(item);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -146,7 +171,7 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div class="js-gcp-machine-type-dropdown dropdown">
|
||||
<dropdown-hidden-input :name="fieldName" :value="selectedItemValue" />
|
||||
<dropdown-hidden-input :name="fieldName" :value="selectedItemsValues" />
|
||||
<dropdown-button
|
||||
:class="{ 'border-danger': hasErrors }"
|
||||
:is-disabled="disabled"
|
||||
|
@ -158,15 +183,28 @@ export default {
|
|||
<div class="dropdown-content">
|
||||
<ul>
|
||||
<li v-if="!results.length">
|
||||
<span class="js-empty-text menu-item">
|
||||
{{ emptyText }}
|
||||
</span>
|
||||
<span class="js-empty-text menu-item">{{ emptyText }}</span>
|
||||
</li>
|
||||
<li v-for="item in results" :key="item.id">
|
||||
<button class="js-dropdown-item" type="button" @click.prevent="select(item)">
|
||||
<slot name="item" :item="item">
|
||||
{{ item.name }}
|
||||
</slot>
|
||||
<button
|
||||
v-if="multiple"
|
||||
class="js-dropdown-item d-flex align-items-center"
|
||||
type="button"
|
||||
@click.stop.prevent="selectMultiple(item)"
|
||||
>
|
||||
<gl-icon
|
||||
:class="[{ invisible: !isSelected(item) }, 'mr-1']"
|
||||
name="mobile-issue-close"
|
||||
/>
|
||||
<slot name="item" :item="item">{{ item.name }}</slot>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="js-dropdown-item"
|
||||
type="button"
|
||||
@click.prevent="selectSingle(item)"
|
||||
>
|
||||
<slot name="item" :item="item">{{ item.name }}</slot>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -182,8 +220,7 @@ export default {
|
|||
'text-muted': !hasErrors,
|
||||
},
|
||||
]"
|
||||
>{{ errorMessage }}</span
|
||||
>
|
||||
{{ errorMessage }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import ServiceCredentialsForm from './service_credentials_form.vue';
|
||||
import EksClusterConfigurationForm from './eks_cluster_configuration_form.vue';
|
||||
|
||||
|
@ -16,14 +17,37 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
accountAndExternalIdsHelpPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createRoleArnHelpPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
externalLinkIcon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState(['hasCredentials']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="js-create-eks-cluster">
|
||||
<eks-cluster-configuration-form
|
||||
v-if="hasCredentials"
|
||||
:gitlab-managed-cluster-help-path="gitlabManagedClusterHelpPath"
|
||||
:kubernetes-integration-help-path="kubernetesIntegrationHelpPath"
|
||||
:external-link-icon="externalLinkIcon"
|
||||
/>
|
||||
<service-credentials-form
|
||||
v-else
|
||||
:create-role-arn-help-path="createRoleArnHelpPath"
|
||||
:account-and-external-ids-help-path="accountAndExternalIdsHelpPath"
|
||||
:external-link-icon="externalLinkIcon"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -4,8 +4,8 @@ import { sprintf, s__ } from '~/locale';
|
|||
import _ from 'underscore';
|
||||
import { GlFormInput, GlFormCheckbox } from '@gitlab/ui';
|
||||
import ClusterFormDropdown from './cluster_form_dropdown.vue';
|
||||
import RegionDropdown from './region_dropdown.vue';
|
||||
import { KUBERNETES_VERSIONS } from '../constants';
|
||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||
|
||||
const { mapState: mapRolesState, mapActions: mapRolesActions } = createNamespacedHelpers('roles');
|
||||
const { mapState: mapRegionsState, mapActions: mapRegionsActions } = createNamespacedHelpers(
|
||||
|
@ -22,13 +22,17 @@ const {
|
|||
mapState: mapSecurityGroupsState,
|
||||
mapActions: mapSecurityGroupsActions,
|
||||
} = createNamespacedHelpers('securityGroups');
|
||||
const {
|
||||
mapState: mapInstanceTypesState,
|
||||
mapActions: mapInstanceTypesActions,
|
||||
} = createNamespacedHelpers('instanceTypes');
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ClusterFormDropdown,
|
||||
RegionDropdown,
|
||||
GlFormInput,
|
||||
GlFormCheckbox,
|
||||
LoadingButton,
|
||||
},
|
||||
props: {
|
||||
gitlabManagedClusterHelpPath: {
|
||||
|
@ -39,6 +43,10 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
externalLinkIcon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
|
@ -51,7 +59,10 @@ export default {
|
|||
'selectedSubnet',
|
||||
'selectedRole',
|
||||
'selectedSecurityGroup',
|
||||
'selectedInstanceType',
|
||||
'nodeCount',
|
||||
'gitlabManagedCluster',
|
||||
'isCreatingCluster',
|
||||
]),
|
||||
...mapRolesState({
|
||||
roles: 'items',
|
||||
|
@ -83,6 +94,11 @@ export default {
|
|||
isLoadingSecurityGroups: 'isLoadingItems',
|
||||
loadingSecurityGroupsError: 'loadingItemsError',
|
||||
}),
|
||||
...mapInstanceTypesState({
|
||||
instanceTypes: 'items',
|
||||
isLoadingInstanceTypes: 'isLoadingItems',
|
||||
loadingInstanceTypesError: 'loadingItemsError',
|
||||
}),
|
||||
kubernetesVersions() {
|
||||
return KUBERNETES_VERSIONS;
|
||||
},
|
||||
|
@ -98,6 +114,27 @@ export default {
|
|||
securityGroupDropdownDisabled() {
|
||||
return !this.selectedVpc;
|
||||
},
|
||||
createClusterButtonDisabled() {
|
||||
return (
|
||||
!this.clusterName ||
|
||||
!this.environmentScope ||
|
||||
!this.kubernetesVersion ||
|
||||
!this.selectedRegion ||
|
||||
!this.selectedKeyPair ||
|
||||
!this.selectedVpc ||
|
||||
!this.selectedSubnet ||
|
||||
!this.selectedRole ||
|
||||
!this.selectedSecurityGroup ||
|
||||
!this.selectedInstanceType ||
|
||||
!this.nodeCount ||
|
||||
this.isCreatingCluster
|
||||
);
|
||||
},
|
||||
createClusterButtonLabel() {
|
||||
return this.isCreatingCluster
|
||||
? s__('ClusterIntegration|Creating Kubernetes cluster')
|
||||
: s__('ClusterIntegration|Create Kubernetes cluster');
|
||||
},
|
||||
kubernetesIntegrationHelpText() {
|
||||
const escapedUrl = _.escape(this.kubernetesIntegrationHelpPath);
|
||||
|
||||
|
@ -115,11 +152,26 @@ export default {
|
|||
roleDropdownHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}.',
|
||||
'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}.',
|
||||
),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
|
||||
'<a href="https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html#role-create" target="_blank" rel="noopener noreferrer">',
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
regionsDropdownHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Learn more about %{startLink}Regions %{externalLinkIcon}%{endLink}.',
|
||||
),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/" target="_blank" rel="noopener noreferrer">',
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
|
@ -128,11 +180,12 @@ export default {
|
|||
keyPairDropdownHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services%{endLink}.',
|
||||
'ClusterIntegration|Select the key pair name that will be used to create EC2 nodes. To use a new key pair name, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}.',
|
||||
),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair" target="_blank" rel="noopener noreferrer">',
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
|
@ -141,11 +194,12 @@ export default {
|
|||
vpcDropdownHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services%{endLink}.',
|
||||
'ClusterIntegration|Select a VPC to use for your EKS Cluster resources. To use a new VPC, first create one on %{startLink}Amazon Web Services %{externalLinkIcon} %{endLink}.',
|
||||
),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://console.aws.amazon.com/vpc/home?#vpc" target="_blank" rel="noopener noreferrer">',
|
||||
'<a href="https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html#vpc-create" target="_blank" rel="noopener noreferrer">',
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
|
@ -154,11 +208,12 @@ export default {
|
|||
subnetDropdownHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Choose the %{startLink}subnets%{endLink} in your VPC where your worker nodes will run.',
|
||||
'ClusterIntegration|Choose the %{startLink}subnets %{externalLinkIcon} %{endLink} in your VPC where your worker nodes will run.',
|
||||
),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://console.aws.amazon.com/vpc/home?#subnets" target="_blank" rel="noopener noreferrer">',
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
|
@ -167,11 +222,26 @@ export default {
|
|||
securityGroupDropdownHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Choose the %{startLink}security groups%{endLink} to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets.',
|
||||
'ClusterIntegration|Choose the %{startLink}security group %{externalLinkIcon} %{endLink} to apply to the EKS-managed Elastic Network Interfaces that are created in your worker node subnets.',
|
||||
),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://console.aws.amazon.com/vpc/home?#securityGroups" target="_blank" rel="noopener noreferrer">',
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
instanceTypesDropdownHelpText() {
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Choose the worker node %{startLink}instance type %{externalLinkIcon} %{endLink}.',
|
||||
),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://aws.amazon.com/ec2/instance-types" target="_blank" rel="noopener noreferrer">',
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
|
@ -195,9 +265,12 @@ export default {
|
|||
mounted() {
|
||||
this.fetchRegions();
|
||||
this.fetchRoles();
|
||||
this.fetchInstanceTypes();
|
||||
},
|
||||
methods: {
|
||||
...mapActions([
|
||||
'createCluster',
|
||||
'signOut',
|
||||
'setClusterName',
|
||||
'setEnvironmentScope',
|
||||
'setKubernetesVersion',
|
||||
|
@ -207,6 +280,8 @@ export default {
|
|||
'setRole',
|
||||
'setKeyPair',
|
||||
'setSecurityGroup',
|
||||
'setInstanceType',
|
||||
'setNodeCount',
|
||||
'setGitlabManagedCluster',
|
||||
]),
|
||||
...mapRegionsActions({ fetchRegions: 'fetchItems' }),
|
||||
|
@ -215,15 +290,22 @@ export default {
|
|||
...mapRolesActions({ fetchRoles: 'fetchItems' }),
|
||||
...mapKeyPairsActions({ fetchKeyPairs: 'fetchItems' }),
|
||||
...mapSecurityGroupsActions({ fetchSecurityGroups: 'fetchItems' }),
|
||||
...mapInstanceTypesActions({ fetchInstanceTypes: 'fetchItems' }),
|
||||
setRegionAndFetchVpcsAndKeyPairs(region) {
|
||||
this.setRegion({ region });
|
||||
this.setVpc({ vpc: null });
|
||||
this.setKeyPair({ keyPair: null });
|
||||
this.setSubnet({ subnet: null });
|
||||
this.setSecurityGroup({ securityGroup: null });
|
||||
this.fetchVpcs({ region });
|
||||
this.fetchKeyPairs({ region });
|
||||
},
|
||||
setVpcAndFetchSubnets(vpc) {
|
||||
this.setVpc({ vpc });
|
||||
this.fetchSubnets({ vpc });
|
||||
this.fetchSecurityGroups({ vpc });
|
||||
this.setSubnet({ subnet: null });
|
||||
this.setSecurityGroup({ securityGroup: null });
|
||||
this.fetchSubnets({ vpc, region: this.selectedRegion });
|
||||
this.fetchSecurityGroups({ vpc, region: this.selectedRegion });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -233,7 +315,12 @@ export default {
|
|||
<h2>
|
||||
{{ s__('ClusterIntegration|Enter the details for your Amazon EKS Kubernetes cluster') }}
|
||||
</h2>
|
||||
<p v-html="kubernetesIntegrationHelpText"></p>
|
||||
<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">
|
||||
<label class="label-bold" for="eks-cluster-name">{{
|
||||
s__('ClusterIntegration|Kubernetes cluster name')
|
||||
|
@ -273,7 +360,7 @@ export default {
|
|||
<cluster-form-dropdown
|
||||
field-id="eks-role"
|
||||
field-name="eks-role"
|
||||
:input="selectedRole"
|
||||
:value="selectedRole"
|
||||
:items="roles"
|
||||
:loading="isLoadingRoles"
|
||||
:loading-text="s__('ClusterIntegration|Loading IAM Roles')"
|
||||
|
@ -288,13 +375,21 @@ export default {
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Region') }}</label>
|
||||
<region-dropdown
|
||||
<cluster-form-dropdown
|
||||
field-id="eks-region"
|
||||
field-name="eks-region"
|
||||
:value="selectedRegion"
|
||||
:regions="regions"
|
||||
:error="loadingRegionsError"
|
||||
:items="regions"
|
||||
:loading="isLoadingRegions"
|
||||
:loading-text="s__('ClusterIntegration|Loading Regions')"
|
||||
:placeholder="s__('ClusterIntergation|Select a region')"
|
||||
:search-field-placeholder="s__('ClusterIntegration|Search regions')"
|
||||
:empty-text="s__('ClusterIntegration|No region found')"
|
||||
:has-errors="Boolean(loadingRegionsError)"
|
||||
:error-message="s__('ClusterIntegration|Could not load regions from your AWS account')"
|
||||
@input="setRegionAndFetchVpcsAndKeyPairs($event)"
|
||||
/>
|
||||
<p class="form-text text-muted" v-html="regionsDropdownHelpText"></p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label-bold" for="eks-key-pair">{{
|
||||
|
@ -303,7 +398,7 @@ export default {
|
|||
<cluster-form-dropdown
|
||||
field-id="eks-key-pair"
|
||||
field-name="eks-key-pair"
|
||||
:input="selectedKeyPair"
|
||||
:value="selectedKeyPair"
|
||||
:items="keyPairs"
|
||||
:disabled="keyPairDropdownDisabled"
|
||||
:disabled-text="s__('ClusterIntegration|Select a region to choose a Key Pair')"
|
||||
|
@ -323,7 +418,7 @@ export default {
|
|||
<cluster-form-dropdown
|
||||
field-id="eks-vpc"
|
||||
field-name="eks-vpc"
|
||||
:input="selectedVpc"
|
||||
:value="selectedVpc"
|
||||
:items="vpcs"
|
||||
:loading="isLoadingVpcs"
|
||||
:disabled="vpcDropdownDisabled"
|
||||
|
@ -339,11 +434,12 @@ export default {
|
|||
<p class="form-text text-muted" v-html="vpcDropdownHelpText"></p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Subnet') }}</label>
|
||||
<label class="label-bold" for="eks-role">{{ s__('ClusterIntegration|Subnets') }}</label>
|
||||
<cluster-form-dropdown
|
||||
field-id="eks-subnet"
|
||||
field-name="eks-subnet"
|
||||
:input="selectedSubnet"
|
||||
multiple
|
||||
:value="selectedSubnet"
|
||||
:items="subnets"
|
||||
:loading="isLoadingSubnets"
|
||||
:disabled="subnetDropdownDisabled"
|
||||
|
@ -360,12 +456,12 @@ export default {
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label-bold" for="eks-security-group">{{
|
||||
s__('ClusterIntegration|Security groups')
|
||||
s__('ClusterIntegration|Security group')
|
||||
}}</label>
|
||||
<cluster-form-dropdown
|
||||
field-id="eks-security-group"
|
||||
field-name="eks-security-group"
|
||||
:input="selectedSecurityGroup"
|
||||
:value="selectedSecurityGroup"
|
||||
:items="securityGroups"
|
||||
:loading="isLoadingSecurityGroups"
|
||||
:disabled="securityGroupDropdownDisabled"
|
||||
|
@ -382,6 +478,39 @@ export default {
|
|||
/>
|
||||
<p class="form-text text-muted" v-html="securityGroupDropdownHelpText"></p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label-bold" for="eks-instance-type">{{
|
||||
s__('ClusterIntegration|Instance type')
|
||||
}}</label>
|
||||
<cluster-form-dropdown
|
||||
field-id="eks-instance-type"
|
||||
field-name="eks-instance-type"
|
||||
:value="selectedInstanceType"
|
||||
:items="instanceTypes"
|
||||
:loading="isLoadingInstanceTypes"
|
||||
:loading-text="s__('ClusterIntegration|Loading instance types')"
|
||||
:placeholder="s__('ClusterIntergation|Select an instance type')"
|
||||
:search-field-placeholder="s__('ClusterIntegration|Search instance types')"
|
||||
:empty-text="s__('ClusterIntegration|No instance type found')"
|
||||
:has-errors="Boolean(loadingInstanceTypesError)"
|
||||
:error-message="s__('ClusterIntegration|Could not load instance types')"
|
||||
@input="setInstanceType({ instanceType: $event })"
|
||||
/>
|
||||
<p class="form-text text-muted" v-html="instanceTypesDropdownHelpText"></p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="label-bold" for="eks-node-count">{{
|
||||
s__('ClusterIntegration|Number of nodes')
|
||||
}}</label>
|
||||
<gl-form-input
|
||||
id="eks-node-count"
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
:value="nodeCount"
|
||||
@input="setNodeCount({ nodeCount: $event })"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<gl-form-checkbox
|
||||
:checked="gitlabManagedCluster"
|
||||
|
@ -390,5 +519,14 @@ export default {
|
|||
>
|
||||
<p class="form-text text-muted" v-html="gitlabManagedHelpText"></p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<loading-button
|
||||
class="js-create-cluster btn-success"
|
||||
:disabled="createClusterButtonDisabled"
|
||||
:loading="isCreatingCluster"
|
||||
:label="createClusterButtonLabel"
|
||||
@click="createCluster()"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
<script>
|
||||
import { sprintf, s__ } from '~/locale';
|
||||
|
||||
import ClusterFormDropdown from './cluster_form_dropdown.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ClusterFormDropdown,
|
||||
},
|
||||
props: {
|
||||
regions: {
|
||||
type: Array,
|
||||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
error: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasErrors() {
|
||||
return Boolean(this.error);
|
||||
},
|
||||
helpText() {
|
||||
return sprintf(
|
||||
s__('ClusterIntegration|Learn more about %{startLink}Regions%{endLink}.'),
|
||||
{
|
||||
startLink:
|
||||
'<a href="https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/" target="_blank" rel="noopener noreferrer">',
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<cluster-form-dropdown
|
||||
field-id="eks-region"
|
||||
field-name="eks-region"
|
||||
:items="regions"
|
||||
:loading="loading"
|
||||
:loading-text="s__('ClusterIntegration|Loading Regions')"
|
||||
:placeholder="s__('ClusterIntergation|Select a region')"
|
||||
:search-field-placeholder="s__('ClusterIntegration|Search regions')"
|
||||
:empty-text="s__('ClusterIntegration|No region found')"
|
||||
:has-errors="hasErrors"
|
||||
:error-message="s__('ClusterIntegration|Could not load regions from your AWS account')"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners"
|
||||
/>
|
||||
<p class="form-text text-muted" v-html="helpText"></p>
|
||||
</div>
|
||||
</template>
|
|
@ -1,3 +1,141 @@
|
|||
<script>
|
||||
import { GlFormInput } from '@gitlab/ui';
|
||||
import { sprintf, s__, __ } from '~/locale';
|
||||
import _ from 'underscore';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import LoadingButton from '~/vue_shared/components/loading_button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlFormInput,
|
||||
LoadingButton,
|
||||
ClipboardButton,
|
||||
},
|
||||
props: {
|
||||
accountAndExternalIdsHelpPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
createRoleArnHelpPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
externalLinkIcon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
roleArn: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['accountId', 'externalId', 'isCreatingRole', 'createRoleError']),
|
||||
submitButtonDisabled() {
|
||||
return this.isCreatingRole || !this.roleArn;
|
||||
},
|
||||
submitButtonLabel() {
|
||||
return this.isCreatingRole
|
||||
? __('Authenticating')
|
||||
: s__('ClusterIntegration|Authenticate with AWS');
|
||||
},
|
||||
accountAndExternalIdsHelpText() {
|
||||
const escapedUrl = _.escape(this.accountAndExternalIdsHelpPath);
|
||||
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|Create a provision role on %{startAwsLink}Amazon Web Services %{externalLinkIcon}%{endLink} using the account and external ID above. %{startMoreInfoLink}More information%{endLink}',
|
||||
),
|
||||
{
|
||||
startAwsLink:
|
||||
'<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
|
||||
startMoreInfoLink: `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">`,
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
provisionRoleArnHelpText() {
|
||||
const escapedUrl = _.escape(this.createRoleArnHelpPath);
|
||||
|
||||
return sprintf(
|
||||
s__(
|
||||
'ClusterIntegration|The Amazon Resource Name (ARN) associated with your role. If you do not have a provision role, first create one on %{startAwsLink}Amazon Web Services %{externalLinkIcon}%{endLink} using the above account and external IDs. %{startMoreInfoLink}More information%{endLink}',
|
||||
),
|
||||
{
|
||||
startAwsLink:
|
||||
'<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
|
||||
startMoreInfoLink: `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">`,
|
||||
externalLinkIcon: this.externalLinkIcon,
|
||||
endLink: '</a>',
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['createRole']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<form name="service-credentials-form"></form>
|
||||
<form name="service-credentials-form" @submit.prevent="createRole({ roleArn, externalId })">
|
||||
<h2>{{ s__('ClusterIntegration|Authenticate with Amazon Web Services') }}</h2>
|
||||
<p>
|
||||
{{
|
||||
s__(
|
||||
'ClusterIntegration|You must grant access to your organization’s AWS resources in order to create a new EKS cluster. To grant access, create a provision role using the account and external ID below and provide us the ARN.',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div v-if="createRoleError" class="js-invalid-credentials bs-callout bs-callout-danger">
|
||||
{{ createRoleError }}
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="gitlab-account-id">{{ __('Account ID') }}</label>
|
||||
<div class="input-group">
|
||||
<gl-form-input id="gitlab-account-id" type="text" readonly :value="accountId" />
|
||||
<div class="input-group-append">
|
||||
<clipboard-button
|
||||
:text="accountId"
|
||||
:title="__('Copy Account ID to clipboard')"
|
||||
class="input-group-text js-copy-account-id-button"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="eks-external-id">{{ __('External ID') }}</label>
|
||||
<div class="input-group">
|
||||
<gl-form-input id="eks-external-id" type="text" readonly :value="externalId" />
|
||||
<div class="input-group-append">
|
||||
<clipboard-button
|
||||
:text="externalId"
|
||||
:title="__('Copy External ID to clipboard')"
|
||||
class="input-group-text js-copy-external-id-button"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mb-3 mt-n3">
|
||||
<p class="form-text text-muted" v-html="accountAndExternalIdsHelpText"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="eks-provision-role-arn">{{ s__('ClusterIntegration|Provision Role ARN') }}</label>
|
||||
<gl-form-input id="eks-provision-role-arn" v-model="roleArn" />
|
||||
<p class="form-text text-muted" v-html="provisionRoleArnHelpText"></p>
|
||||
</div>
|
||||
<loading-button
|
||||
class="js-submit-service-credentials btn-success"
|
||||
type="submit"
|
||||
:disabled="submitButtonDisabled"
|
||||
:loading="isCreatingRole"
|
||||
:label="submitButtonLabel"
|
||||
/>
|
||||
</form>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,2 @@
|
|||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const KUBERNETES_VERSIONS = [
|
||||
{ name: '1.14', value: '1.14' },
|
||||
{ name: '1.13', value: '1.13' },
|
||||
{ name: '1.12', value: '1.12' },
|
||||
{ name: '1.11', value: '1.11' },
|
||||
];
|
||||
export const KUBERNETES_VERSIONS = [{ name: '1.14', value: '1.14' }];
|
||||
|
|
|
@ -1,16 +1,54 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import CreateEksCluster from './components/create_eks_cluster.vue';
|
||||
import createStore from './store';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default el => {
|
||||
const { gitlabManagedClusterHelpPath, kubernetesIntegrationHelpPath } = el.dataset;
|
||||
const {
|
||||
gitlabManagedClusterHelpPath,
|
||||
kubernetesIntegrationHelpPath,
|
||||
accountAndExternalIdsHelpPath,
|
||||
createRoleArnHelpPath,
|
||||
getRolesPath,
|
||||
getRegionsPath,
|
||||
getKeyPairsPath,
|
||||
getVpcsPath,
|
||||
getSubnetsPath,
|
||||
getSecurityGroupsPath,
|
||||
getInstanceTypesPath,
|
||||
externalId,
|
||||
accountId,
|
||||
hasCredentials,
|
||||
createRolePath,
|
||||
createClusterPath,
|
||||
signOutPath,
|
||||
externalLinkIcon,
|
||||
} = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
store: createStore(),
|
||||
store: createStore({
|
||||
initialState: {
|
||||
hasCredentials: parseBoolean(hasCredentials),
|
||||
externalId,
|
||||
accountId,
|
||||
createRolePath,
|
||||
createClusterPath,
|
||||
signOutPath,
|
||||
},
|
||||
apiPaths: {
|
||||
getRolesPath,
|
||||
getRegionsPath,
|
||||
getKeyPairsPath,
|
||||
getVpcsPath,
|
||||
getSubnetsPath,
|
||||
getSecurityGroupsPath,
|
||||
getInstanceTypesPath,
|
||||
},
|
||||
}),
|
||||
components: {
|
||||
CreateEksCluster,
|
||||
},
|
||||
|
@ -19,6 +57,9 @@ export default el => {
|
|||
props: {
|
||||
gitlabManagedClusterHelpPath,
|
||||
kubernetesIntegrationHelpPath,
|
||||
accountAndExternalIdsHelpPath,
|
||||
createRoleArnHelpPath,
|
||||
externalLinkIcon,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -1,84 +1,58 @@
|
|||
import EC2 from 'aws-sdk/clients/ec2';
|
||||
import IAM from 'aws-sdk/clients/iam';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
export const fetchRoles = () => {
|
||||
const iam = new IAM();
|
||||
|
||||
return iam
|
||||
.listRoles()
|
||||
.promise()
|
||||
.then(({ Roles: roles }) => roles.map(({ RoleName: name }) => ({ name })));
|
||||
};
|
||||
|
||||
export const fetchKeyPairs = () => {
|
||||
const ec2 = new EC2();
|
||||
|
||||
return ec2
|
||||
.describeKeyPairs()
|
||||
.promise()
|
||||
.then(({ KeyPairs: keyPairs }) => keyPairs.map(({ RegionName: name }) => ({ name })));
|
||||
};
|
||||
|
||||
export const fetchRegions = () => {
|
||||
const ec2 = new EC2();
|
||||
|
||||
return ec2
|
||||
.describeRegions()
|
||||
.promise()
|
||||
.then(({ Regions: regions }) =>
|
||||
regions.map(({ RegionName: name }) => ({
|
||||
name,
|
||||
value: name,
|
||||
export default apiPaths => ({
|
||||
fetchRoles() {
|
||||
return axios
|
||||
.get(apiPaths.getRolesPath)
|
||||
.then(({ data: { roles } }) =>
|
||||
roles.map(({ role_name: name, arn: value }) => ({ name, value })),
|
||||
);
|
||||
},
|
||||
fetchKeyPairs({ region }) {
|
||||
return axios
|
||||
.get(apiPaths.getKeyPairsPath, { params: { region } })
|
||||
.then(({ data: { key_pairs: keyPairs } }) =>
|
||||
keyPairs.map(({ key_name }) => ({ name: key_name, value: key_name })),
|
||||
);
|
||||
},
|
||||
fetchRegions() {
|
||||
return axios.get(apiPaths.getRegionsPath).then(({ data: { regions } }) =>
|
||||
regions.map(({ region_name }) => ({
|
||||
name: region_name,
|
||||
value: region_name,
|
||||
})),
|
||||
);
|
||||
};
|
||||
|
||||
export const fetchVpcs = () => {
|
||||
const ec2 = new EC2();
|
||||
|
||||
return ec2
|
||||
.describeVpcs()
|
||||
.promise()
|
||||
.then(({ Vpcs: vpcs }) =>
|
||||
vpcs.map(({ VpcId: id }) => ({
|
||||
value: id,
|
||||
name: id,
|
||||
},
|
||||
fetchVpcs({ region }) {
|
||||
return axios.get(apiPaths.getVpcsPath, { params: { region } }).then(({ data: { vpcs } }) =>
|
||||
vpcs.map(({ vpc_id }) => ({
|
||||
value: vpc_id,
|
||||
name: vpc_id,
|
||||
})),
|
||||
);
|
||||
};
|
||||
|
||||
export const fetchSubnets = ({ vpc }) => {
|
||||
const ec2 = new EC2();
|
||||
|
||||
return ec2
|
||||
.describeSubnets({
|
||||
Filters: [
|
||||
{
|
||||
Name: 'vpc-id',
|
||||
Values: [vpc],
|
||||
},
|
||||
],
|
||||
})
|
||||
.promise()
|
||||
.then(({ Subnets: subnets }) => subnets.map(({ SubnetId: id }) => ({ id, name: id })));
|
||||
};
|
||||
|
||||
export const fetchSecurityGroups = ({ vpc }) => {
|
||||
const ec2 = new EC2();
|
||||
|
||||
return ec2
|
||||
.describeSecurityGroups({
|
||||
Filters: [
|
||||
{
|
||||
Name: 'vpc-id',
|
||||
Values: [vpc],
|
||||
},
|
||||
],
|
||||
})
|
||||
.promise()
|
||||
.then(({ SecurityGroups: securityGroups }) =>
|
||||
securityGroups.map(({ GroupName: name, GroupId: value }) => ({ name, value })),
|
||||
);
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
},
|
||||
fetchSubnets({ vpc, region }) {
|
||||
return axios
|
||||
.get(apiPaths.getSubnetsPath, { params: { vpc_id: vpc, region } })
|
||||
.then(({ data: { subnets } }) =>
|
||||
subnets.map(({ subnet_id }) => ({ name: subnet_id, value: subnet_id })),
|
||||
);
|
||||
},
|
||||
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,
|
||||
})),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import * as types from './mutation_types';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import createFlash from '~/flash';
|
||||
|
||||
const getErrorMessage = data => {
|
||||
const errorKey = Object.keys(data)[0];
|
||||
|
||||
return data[errorKey][0];
|
||||
};
|
||||
|
||||
export const setClusterName = ({ commit }, payload) => {
|
||||
commit(types.SET_CLUSTER_NAME, payload);
|
||||
|
@ -12,6 +20,68 @@ export const setKubernetesVersion = ({ commit }, payload) => {
|
|||
commit(types.SET_KUBERNETES_VERSION, payload);
|
||||
};
|
||||
|
||||
export const createRole = ({ dispatch, state: { createRolePath } }, payload) => {
|
||||
dispatch('requestCreateRole');
|
||||
|
||||
return axios
|
||||
.post(createRolePath, {
|
||||
role_arn: payload.roleArn,
|
||||
role_external_id: payload.externalId,
|
||||
})
|
||||
.then(() => dispatch('createRoleSuccess'))
|
||||
.catch(error => dispatch('createRoleError', { error }));
|
||||
};
|
||||
|
||||
export const requestCreateRole = ({ commit }) => {
|
||||
commit(types.REQUEST_CREATE_ROLE);
|
||||
};
|
||||
|
||||
export const createRoleSuccess = ({ commit }) => {
|
||||
commit(types.CREATE_ROLE_SUCCESS);
|
||||
};
|
||||
|
||||
export const createRoleError = ({ commit }, payload) => {
|
||||
commit(types.CREATE_ROLE_ERROR, payload);
|
||||
};
|
||||
|
||||
export const createCluster = ({ dispatch, state }) => {
|
||||
dispatch('requestCreateCluster');
|
||||
|
||||
return axios
|
||||
.post(state.createClusterPath, {
|
||||
name: state.clusterName,
|
||||
environment_scope: state.environmentScope,
|
||||
managed: state.gitlabManagedCluster,
|
||||
provider_aws_attributes: {
|
||||
region: state.selectedRegion,
|
||||
vpc_id: state.selectedVpc,
|
||||
subnet_ids: state.selectedSubnet,
|
||||
role_arn: state.selectedRole,
|
||||
key_name: state.selectedKeyPair,
|
||||
security_group_id: state.selectedSecurityGroup,
|
||||
instance_type: state.selectedInstanceType,
|
||||
num_nodes: state.nodeCount,
|
||||
},
|
||||
})
|
||||
.then(({ headers: { location } }) => dispatch('createClusterSuccess', location))
|
||||
.catch(({ response: { data } }) => {
|
||||
dispatch('createClusterError', data);
|
||||
});
|
||||
};
|
||||
|
||||
export const requestCreateCluster = ({ commit }) => {
|
||||
commit(types.REQUEST_CREATE_CLUSTER);
|
||||
};
|
||||
|
||||
export const createClusterSuccess = (_, location) => {
|
||||
window.location.assign(location);
|
||||
};
|
||||
|
||||
export const createClusterError = ({ commit }, error) => {
|
||||
commit(types.CREATE_CLUSTER_ERROR, error);
|
||||
createFlash(getErrorMessage(error));
|
||||
};
|
||||
|
||||
export const setRegion = ({ commit }, payload) => {
|
||||
commit(types.SET_REGION, payload);
|
||||
};
|
||||
|
@ -40,4 +110,16 @@ export const setGitlabManagedCluster = ({ commit }, payload) => {
|
|||
commit(types.SET_GITLAB_MANAGED_CLUSTER, payload);
|
||||
};
|
||||
|
||||
export default () => {};
|
||||
export const setInstanceType = ({ commit }, payload) => {
|
||||
commit(types.SET_INSTANCE_TYPE, payload);
|
||||
};
|
||||
|
||||
export const setNodeCount = ({ commit }, 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)));
|
||||
|
|
|
@ -6,14 +6,16 @@ import state from './state';
|
|||
|
||||
import clusterDropdownStore from './cluster_dropdown';
|
||||
|
||||
import * as awsServices from '../services/aws_services_facade';
|
||||
import awsServicesFactory from '../services/aws_services_facade';
|
||||
|
||||
const createStore = () =>
|
||||
new Vuex.Store({
|
||||
const createStore = ({ initialState, apiPaths }) => {
|
||||
const awsServices = awsServicesFactory(apiPaths);
|
||||
|
||||
return new Vuex.Store({
|
||||
actions,
|
||||
getters,
|
||||
mutations,
|
||||
state: state(),
|
||||
state: Object.assign(state(), initialState),
|
||||
modules: {
|
||||
roles: {
|
||||
namespaced: true,
|
||||
|
@ -39,7 +41,12 @@ const createStore = () =>
|
|||
namespaced: true,
|
||||
...clusterDropdownStore(awsServices.fetchSecurityGroups),
|
||||
},
|
||||
instanceTypes: {
|
||||
namespaced: true,
|
||||
...clusterDropdownStore(awsServices.fetchInstanceTypes),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default createStore;
|
||||
|
|
|
@ -7,4 +7,13 @@ export const SET_KEY_PAIR = 'SET_KEY_PAIR';
|
|||
export const SET_SUBNET = 'SET_SUBNET';
|
||||
export const SET_ROLE = 'SET_ROLE';
|
||||
export const SET_SECURITY_GROUP = 'SET_SECURITY_GROUP';
|
||||
export const SET_INSTANCE_TYPE = 'SET_INSTANCE_TYPE';
|
||||
export const SET_NODE_COUNT = 'SET_NODE_COUNT';
|
||||
export const SET_GITLAB_MANAGED_CLUSTER = 'SET_GITLAB_MANAGED_CLUSTER';
|
||||
export const REQUEST_CREATE_ROLE = 'REQUEST_CREATE_ROLE';
|
||||
export const CREATE_ROLE_SUCCESS = 'CREATE_ROLE_SUCCESS';
|
||||
export const CREATE_ROLE_ERROR = 'CREATE_ROLE_ERROR';
|
||||
export const SIGN_OUT = 'SIGN_OUT';
|
||||
export const REQUEST_CREATE_CLUSTER = 'REQUEST_CREATE_CLUSTER';
|
||||
export const CREATE_CLUSTER_SUCCESS = 'CREATE_CLUSTER_SUCCESS';
|
||||
export const CREATE_CLUSTER_ERROR = 'CREATE_CLUSTER_ERROR';
|
||||
|
|
|
@ -28,7 +28,39 @@ export default {
|
|||
[types.SET_SECURITY_GROUP](state, { securityGroup }) {
|
||||
state.selectedSecurityGroup = securityGroup;
|
||||
},
|
||||
[types.SET_INSTANCE_TYPE](state, { instanceType }) {
|
||||
state.selectedInstanceType = instanceType;
|
||||
},
|
||||
[types.SET_NODE_COUNT](state, { nodeCount }) {
|
||||
state.nodeCount = nodeCount;
|
||||
},
|
||||
[types.SET_GITLAB_MANAGED_CLUSTER](state, { gitlabManagedCluster }) {
|
||||
state.gitlabManagedCluster = gitlabManagedCluster;
|
||||
},
|
||||
[types.REQUEST_CREATE_ROLE](state) {
|
||||
state.isCreatingRole = true;
|
||||
state.createRoleError = null;
|
||||
state.hasCredentials = false;
|
||||
},
|
||||
[types.CREATE_ROLE_SUCCESS](state) {
|
||||
state.isCreatingRole = false;
|
||||
state.createRoleError = null;
|
||||
state.hasCredentials = true;
|
||||
},
|
||||
[types.CREATE_ROLE_ERROR](state, { error }) {
|
||||
state.isCreatingRole = false;
|
||||
state.createRoleError = error;
|
||||
state.hasCredentials = false;
|
||||
},
|
||||
[types.REQUEST_CREATE_CLUSTER](state) {
|
||||
state.isCreatingCluster = true;
|
||||
state.createClusterError = null;
|
||||
},
|
||||
[types.CREATE_CLUSTER_ERROR](state, { error }) {
|
||||
state.isCreatingCluster = false;
|
||||
state.createClusterError = error;
|
||||
},
|
||||
[types.SIGN_OUT](state) {
|
||||
state.hasCredentials = false;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,18 +1,31 @@
|
|||
import { KUBERNETES_VERSIONS } from '../constants';
|
||||
|
||||
const [{ value: kubernetesVersion }] = KUBERNETES_VERSIONS;
|
||||
|
||||
export default () => ({
|
||||
isValidatingCredentials: false,
|
||||
validCredentials: false,
|
||||
createRolePath: null,
|
||||
|
||||
isCreatingRole: false,
|
||||
roleCreated: false,
|
||||
createRoleError: false,
|
||||
|
||||
accountId: '',
|
||||
externalId: '',
|
||||
|
||||
clusterName: '',
|
||||
environmentScope: '*',
|
||||
kubernetesVersion: [KUBERNETES_VERSIONS].value,
|
||||
kubernetesVersion,
|
||||
selectedRegion: '',
|
||||
selectedRole: '',
|
||||
selectedKeyPair: '',
|
||||
selectedVpc: '',
|
||||
selectedSubnet: '',
|
||||
selectedSecurityGroup: '',
|
||||
selectedInstanceType: 'm5.large',
|
||||
nodeCount: '3',
|
||||
|
||||
isCreatingCluster: false,
|
||||
createClusterError: false,
|
||||
|
||||
gitlabManagedCluster: true,
|
||||
});
|
||||
|
|
37
app/assets/javascripts/create_cluster/init_create_cluster.js
Normal file
37
app/assets/javascripts/create_cluster/init_create_cluster.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import initGkeDropdowns from './gke_cluster';
|
||||
import initGkeNamespace from './gke_cluster_namespace';
|
||||
import PersistentUserCallout from '~/persistent_user_callout';
|
||||
|
||||
const newClusterViews = [':clusters:new', ':clusters:create_gcp', ':clusters:create_user'];
|
||||
|
||||
const isProjectLevelCluster = page => page.startsWith('project:clusters');
|
||||
|
||||
export default (document, gon) => {
|
||||
const { page } = document.body.dataset;
|
||||
const isNewClusterView = newClusterViews.some(view => page.endsWith(view));
|
||||
|
||||
if (!isNewClusterView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callout = document.querySelector('.gcp-signup-offer');
|
||||
PersistentUserCallout.factory(callout);
|
||||
|
||||
initGkeDropdowns();
|
||||
|
||||
if (gon.features.createEksClusters) {
|
||||
import(/* webpackChunkName: 'eks_cluster' */ '~/create_cluster/eks_cluster')
|
||||
.then(({ default: initCreateEKSCluster }) => {
|
||||
const el = document.querySelector('.js-create-eks-cluster-form-container');
|
||||
|
||||
if (el) {
|
||||
initCreateEKSCluster(el);
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
if (isProjectLevelCluster(page)) {
|
||||
initGkeNamespace();
|
||||
}
|
||||
};
|
|
@ -1,44 +0,0 @@
|
|||
<script>
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
name: 'StageCardListItem',
|
||||
components: {
|
||||
Icon,
|
||||
GlButton,
|
||||
},
|
||||
props: {
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
canEdit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{ active: isActive }"
|
||||
class="stage-nav-item d-flex pl-4 pr-4 m-0 mb-1 ml-2 rounded border-color-default border-style-solid border-width-1px"
|
||||
>
|
||||
<slot></slot>
|
||||
<div v-if="canEdit" class="dropdown">
|
||||
<gl-button
|
||||
:title="__('More actions')"
|
||||
class="more-actions-toggle btn btn-transparent p-0"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<icon class="icon" name="ellipsis_v" />
|
||||
</gl-button>
|
||||
<ul class="more-actions-dropdown dropdown-menu dropdown-open-left">
|
||||
<slot name="dropdown-options"></slot>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,11 +1,6 @@
|
|||
<script>
|
||||
import StageCardListItem from './stage_card_list_item.vue';
|
||||
|
||||
export default {
|
||||
name: 'StageNavItem',
|
||||
components: {
|
||||
StageCardListItem,
|
||||
},
|
||||
props: {
|
||||
isDefaultStage: {
|
||||
type: Boolean,
|
||||
|
@ -40,16 +35,16 @@ export default {
|
|||
hasValue() {
|
||||
return this.value && this.value.length > 0;
|
||||
},
|
||||
editable() {
|
||||
return this.isUserAllowed && this.canEdit;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li @click="$emit('select')">
|
||||
<stage-card-list-item :is-active="isActive" :can-edit="editable">
|
||||
<div
|
||||
:class="{ active: isActive }"
|
||||
class="stage-nav-item d-flex pl-4 pr-4 m-0 mb-1 ml-2 rounded border-color-default border-style-solid border-width-1px"
|
||||
>
|
||||
<div class="stage-nav-item-cell stage-name p-0" :class="{ 'font-weight-bold': isActive }">
|
||||
{{ title }}
|
||||
</div>
|
||||
|
@ -62,27 +57,6 @@ export default {
|
|||
<span class="not-available">{{ __('Not available') }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<template v-slot:dropdown-options>
|
||||
<template v-if="isDefaultStage">
|
||||
<li>
|
||||
<button type="button" class="btn-default btn-transparent">
|
||||
{{ __('Hide stage') }}
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
<template v-else>
|
||||
<li>
|
||||
<button type="button" class="btn-default btn-transparent">
|
||||
{{ __('Edit stage') }}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="btn-danger danger">
|
||||
{{ __('Remove stage') }}
|
||||
</button>
|
||||
</li>
|
||||
</template>
|
||||
</template>
|
||||
</stage-card-list-item>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
|
|
|
@ -124,8 +124,10 @@ export default {
|
|||
:diff-viewer-mode="diffViewerMode"
|
||||
:new-path="diffFile.new_path"
|
||||
:new-sha="diffFile.diff_refs.head_sha"
|
||||
:new-size="diffFile.new_size"
|
||||
:old-path="diffFile.old_path"
|
||||
:old-sha="diffFile.diff_refs.base_sha"
|
||||
:old-size="diffFile.old_size"
|
||||
:file-hash="diffFileHash"
|
||||
:project-path="projectPath"
|
||||
:a-mode="diffFile.a_mode"
|
||||
|
|
|
@ -54,11 +54,12 @@ export default {
|
|||
showLoadingIcon() {
|
||||
return this.isLoadingCollapsedDiff || (!this.file.renderIt && !this.isCollapsed);
|
||||
},
|
||||
hasDiffLines() {
|
||||
hasDiff() {
|
||||
return (
|
||||
this.file.highlighted_diff_lines &&
|
||||
this.file.parallel_diff_lines &&
|
||||
this.file.parallel_diff_lines.length > 0
|
||||
(this.file.highlighted_diff_lines &&
|
||||
this.file.parallel_diff_lines &&
|
||||
this.file.parallel_diff_lines.length > 0) ||
|
||||
!this.file.blob.readable_text
|
||||
);
|
||||
},
|
||||
isFileTooLarge() {
|
||||
|
@ -82,7 +83,7 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
isCollapsed: function fileCollapsedWatch(newVal, oldVal) {
|
||||
if (!newVal && oldVal && !this.hasDiffLines) {
|
||||
if (!newVal && oldVal && !this.hasDiff) {
|
||||
this.handleLoadCollapsedDiff();
|
||||
}
|
||||
|
||||
|
@ -103,7 +104,7 @@ export default {
|
|||
'setFileCollapsed',
|
||||
]),
|
||||
handleToggle() {
|
||||
if (!this.hasDiffLines) {
|
||||
if (!this.hasDiff) {
|
||||
this.handleLoadCollapsedDiff();
|
||||
} else {
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
|
|
|
@ -290,5 +290,5 @@ export default function dropzoneInput(form) {
|
|||
formTextarea.focus();
|
||||
});
|
||||
|
||||
return Dropzone.forElement($formDropzone.get(0));
|
||||
return $formDropzone.get(0) ? Dropzone.forElement($formDropzone.get(0)) : null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
<script>
|
||||
import { mapActions, mapGetters, mapState } from 'vuex';
|
||||
import dateFormat from 'dateformat';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { GlButton, GlLink, GlLoadingIcon } from '@gitlab/ui';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
|
||||
import Stacktrace from './stacktrace.vue';
|
||||
import TrackEventDirective from '~/vue_shared/directives/track_event';
|
||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
|
||||
import { trackClickErrorLinkToSentryOptions } from '../utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlLink,
|
||||
GlLoadingIcon,
|
||||
TooltipOnTruncate,
|
||||
Icon,
|
||||
Stacktrace,
|
||||
},
|
||||
directives: {
|
||||
TrackEvent: TrackEventDirective,
|
||||
},
|
||||
mixins: [timeagoMixin],
|
||||
props: {
|
||||
issueDetailsPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
issueStackTracePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState('details', ['error', 'loading', 'loadingStacktrace', 'stacktraceData']),
|
||||
...mapGetters('details', ['stacktrace']),
|
||||
reported() {
|
||||
return sprintf(
|
||||
__('Reported %{timeAgo} by %{reportedBy}'),
|
||||
{
|
||||
reportedBy: `<strong>${this.error.culprit}</strong>`,
|
||||
timeAgo: this.timeFormated(this.stacktraceData.date_received),
|
||||
},
|
||||
false,
|
||||
);
|
||||
},
|
||||
firstReleaseLink() {
|
||||
return `${this.error.external_base_url}/releases/${this.error.first_release_short_version}`;
|
||||
},
|
||||
lastReleaseLink() {
|
||||
return `${this.error.external_base_url}releases/${this.error.last_release_short_version}`;
|
||||
},
|
||||
showDetails() {
|
||||
return Boolean(!this.loading && this.error && this.error.id);
|
||||
},
|
||||
showStacktrace() {
|
||||
return Boolean(!this.loadingStacktrace && this.stacktrace && this.stacktrace.length);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.startPollingDetails(this.issueDetailsPath);
|
||||
this.startPollingStacktrace(this.issueStackTracePath);
|
||||
},
|
||||
methods: {
|
||||
...mapActions('details', ['startPollingDetails', 'startPollingStacktrace']),
|
||||
trackClickErrorLinkToSentryOptions,
|
||||
formatDate(date) {
|
||||
return `${this.timeFormated(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="loading" class="py-3">
|
||||
<gl-loading-icon :size="3" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="showDetails" class="error-details">
|
||||
<div class="top-area align-items-center justify-content-between py-3">
|
||||
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
|
||||
<!-- <gl-button class="my-3 ml-auto" variant="success">
|
||||
{{ __('Create Issue') }}
|
||||
</gl-button>-->
|
||||
</div>
|
||||
<div>
|
||||
<tooltip-on-truncate :title="error.title" truncate-target="child" placement="top">
|
||||
<h2 class="text-truncate">{{ error.title }}</h2>
|
||||
</tooltip-on-truncate>
|
||||
<h3>{{ __('Error details') }}</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="bold">{{ __('Sentry event') }}:</span>
|
||||
<gl-link
|
||||
v-track-event="trackClickErrorLinkToSentryOptions(error.external_url)"
|
||||
:href="error.external_url"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="text-truncate">{{ error.external_url }}</span>
|
||||
<icon name="external-link" class="ml-1 flex-shrink-0" />
|
||||
</gl-link>
|
||||
</li>
|
||||
<li v-if="error.first_release_short_version">
|
||||
<span class="bold">{{ __('First seen') }}:</span>
|
||||
{{ formatDate(error.first_seen) }}
|
||||
<gl-link :href="firstReleaseLink" target="_blank">
|
||||
<span>{{ __('Release') }}: {{ error.first_release_short_version }}</span>
|
||||
</gl-link>
|
||||
</li>
|
||||
<li v-if="error.last_release_short_version">
|
||||
<span class="bold">{{ __('Last seen') }}:</span>
|
||||
{{ formatDate(error.last_seen) }}
|
||||
<gl-link :href="lastReleaseLink" target="_blank">
|
||||
<span>{{ __('Release') }}: {{ error.last_release_short_version }}</span>
|
||||
</gl-link>
|
||||
</li>
|
||||
<li>
|
||||
<span class="bold">{{ __('Events') }}:</span>
|
||||
<span>{{ error.count }}</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="bold">{{ __('Users') }}:</span>
|
||||
<span>{{ error.user_count }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div v-if="loadingStacktrace" class="py-3">
|
||||
<gl-loading-icon :size="3" />
|
||||
</div>
|
||||
|
||||
<template v-if="showStacktrace">
|
||||
<h3 class="my-4">{{ __('Stack trace') }}</h3>
|
||||
<stacktrace :entries="stacktrace" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,11 +1,19 @@
|
|||
<script>
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import { GlEmptyState, GlButton, GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui';
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import {
|
||||
GlEmptyState,
|
||||
GlButton,
|
||||
GlLink,
|
||||
GlLoadingIcon,
|
||||
GlTable,
|
||||
GlSearchBoxByType,
|
||||
} from '@gitlab/ui';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { __ } from '~/locale';
|
||||
import TrackEventDirective from '~/vue_shared/directives/track_event';
|
||||
import { trackViewInSentryOptions, trackClickErrorLinkToSentryOptions } from '../utils';
|
||||
import { trackViewInSentryOptions } from '../utils';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
|
@ -20,6 +28,7 @@ export default {
|
|||
GlLink,
|
||||
GlLoadingIcon,
|
||||
GlTable,
|
||||
GlSearchBoxByType,
|
||||
Icon,
|
||||
TimeAgo,
|
||||
},
|
||||
|
@ -48,8 +57,17 @@ export default {
|
|||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
errorSearchQuery: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['errors', 'externalUrl', 'loading']),
|
||||
...mapState('list', ['errors', 'externalUrl', 'loading']),
|
||||
...mapGetters('list', ['filterErrorsByTitle']),
|
||||
filteredErrors() {
|
||||
return this.errorSearchQuery ? this.filterErrorsByTitle(this.errorSearchQuery) : this.errors;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
if (this.errorTrackingEnabled) {
|
||||
|
@ -57,9 +75,11 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['startPolling', 'restartPolling']),
|
||||
...mapActions('list', ['startPolling', 'restartPolling']),
|
||||
trackViewInSentryOptions,
|
||||
trackClickErrorLinkToSentryOptions,
|
||||
viewDetails(errorId) {
|
||||
visitUrl(`error_tracking/${errorId}/details`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -71,10 +91,17 @@ export default {
|
|||
<gl-loading-icon :size="3" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="d-flex flex-row justify-content-around bg-secondary border">
|
||||
<gl-search-box-by-type
|
||||
v-model="errorSearchQuery"
|
||||
class="col-lg-10 m-3 p-0"
|
||||
:placeholder="__('Search or filter results...')"
|
||||
type="search"
|
||||
autofocus
|
||||
/>
|
||||
<gl-button
|
||||
v-track-event="trackViewInSentryOptions(externalUrl)"
|
||||
class="my-3 ml-auto"
|
||||
class="m-3"
|
||||
variant="primary"
|
||||
:href="externalUrl"
|
||||
target="_blank"
|
||||
|
@ -84,7 +111,14 @@ export default {
|
|||
</gl-button>
|
||||
</div>
|
||||
|
||||
<gl-table :items="errors" :fields="$options.fields" :show-empty="true" fixed stacked="sm">
|
||||
<gl-table
|
||||
class="mt-3"
|
||||
:items="filteredErrors"
|
||||
:fields="$options.fields"
|
||||
:show-empty="true"
|
||||
fixed
|
||||
stacked="sm"
|
||||
>
|
||||
<template slot="HEAD_events" slot-scope="data">
|
||||
<div class="text-md-right">{{ data.label }}</div>
|
||||
</template>
|
||||
|
@ -94,13 +128,11 @@ export default {
|
|||
<template slot="error" slot-scope="errors">
|
||||
<div class="d-flex flex-column">
|
||||
<gl-link
|
||||
v-track-event="trackClickErrorLinkToSentryOptions(errors.item.externalUrl)"
|
||||
:href="errors.item.externalUrl"
|
||||
class="d-flex text-dark"
|
||||
target="_blank"
|
||||
@click="viewDetails(errors.item.id)"
|
||||
>
|
||||
<strong class="text-truncate">{{ errors.item.title.trim() }}</strong>
|
||||
<icon name="external-link" class="ml-1 flex-shrink-0" />
|
||||
</gl-link>
|
||||
<span class="text-secondary text-truncate">
|
||||
{{ errors.item.culprit }}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<script>
|
||||
import StackTraceEntry from './stacktrace_entry.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
StackTraceEntry,
|
||||
},
|
||||
props: {
|
||||
entries: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isFirstEntry(index) {
|
||||
return index === 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="stacktrace">
|
||||
<stack-trace-entry
|
||||
v-for="(entry, index) in entries"
|
||||
:key="`stacktrace-entry-${index}`"
|
||||
:lines="entry.context"
|
||||
:file-path="entry.filename"
|
||||
:error-line="entry.lineNo"
|
||||
:expanded="isFirstEntry(index)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,110 @@
|
|||
<script>
|
||||
import { GlTooltip } from '@gitlab/ui';
|
||||
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
import FileIcon from '~/vue_shared/components/file_icon.vue';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ClipboardButton,
|
||||
FileIcon,
|
||||
Icon,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip,
|
||||
},
|
||||
props: {
|
||||
lines: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
filePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
errorLine: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
expanded: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: this.expanded,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
linesLength() {
|
||||
return this.lines.length;
|
||||
},
|
||||
collapseIcon() {
|
||||
return this.isExpanded ? 'chevron-down' : 'chevron-right';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isHighlighted(lineNum) {
|
||||
return lineNum === this.errorLine;
|
||||
},
|
||||
toggle() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
},
|
||||
lineNum(line) {
|
||||
return line[0];
|
||||
},
|
||||
lineCode(line) {
|
||||
return line[1];
|
||||
},
|
||||
},
|
||||
userColorScheme: window.gon.user_color_scheme,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="file-holder">
|
||||
<div ref="header" class="file-title file-title-flex-parent">
|
||||
<div class="file-header-content ">
|
||||
<div class="d-inline-block cursor-pointer" @click="toggle()">
|
||||
<icon :name="collapseIcon" :size="16" aria-hidden="true" class="append-right-5" />
|
||||
</div>
|
||||
<div class="d-inline-block append-right-4">
|
||||
<file-icon
|
||||
:file-name="filePath"
|
||||
:size="18"
|
||||
aria-hidden="true"
|
||||
css-classes="append-right-5"
|
||||
/>
|
||||
<strong v-gl-tooltip :title="filePath" class="file-title-name" data-container="body">
|
||||
{{ filePath }}
|
||||
</strong>
|
||||
</div>
|
||||
|
||||
<clipboard-button
|
||||
:title="__('Copy file path')"
|
||||
:text="filePath"
|
||||
css-class="btn-default btn-transparent btn-clipboard"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table v-if="isExpanded" :class="$options.userColorScheme" class="code js-syntax-highlight">
|
||||
<tbody>
|
||||
<template v-for="(line, index) in lines">
|
||||
<tr :key="`stacktrace-line-${index}`" class="line_holder">
|
||||
<td class="diff-line-num" :class="{ old: isHighlighted(lineNum(line)) }">
|
||||
{{ lineNum(line) }}
|
||||
</td>
|
||||
<td
|
||||
class="line_content"
|
||||
:class="{ old: isHighlighted(lineNum(line)) }"
|
||||
v-html="lineCode(line)"
|
||||
></td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
25
app/assets/javascripts/error_tracking/details.js
Normal file
25
app/assets/javascripts/error_tracking/details.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import Vue from 'vue';
|
||||
import store from './store';
|
||||
import ErrorDetails from './components/error_details.vue';
|
||||
|
||||
export default () => {
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: '#js-error_details',
|
||||
components: {
|
||||
ErrorDetails,
|
||||
},
|
||||
store,
|
||||
render(createElement) {
|
||||
const domEl = document.querySelector(this.$options.el);
|
||||
const { issueDetailsPath, issueStackTracePath } = domEl.dataset;
|
||||
|
||||
return createElement('error-details', {
|
||||
props: {
|
||||
issueDetailsPath,
|
||||
issueStackTracePath,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
export default {
|
||||
getErrorList({ endpoint }) {
|
||||
getSentryData({ endpoint }) {
|
||||
return axios.get(endpoint);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import service from '../../services';
|
||||
import * as types from './mutation_types';
|
||||
import createFlash from '~/flash';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
let stackTracePoll;
|
||||
let detailPoll;
|
||||
|
||||
const stopPolling = poll => {
|
||||
if (poll) poll.stop();
|
||||
};
|
||||
|
||||
export function startPollingDetails({ commit }, endpoint) {
|
||||
detailPoll = new Poll({
|
||||
resource: service,
|
||||
method: 'getSentryData',
|
||||
data: { endpoint },
|
||||
successCallback: ({ data }) => {
|
||||
if (!data) {
|
||||
detailPoll.restart();
|
||||
return;
|
||||
}
|
||||
|
||||
commit(types.SET_ERROR, data.error);
|
||||
commit(types.SET_LOADING, false);
|
||||
|
||||
stopPolling(detailPoll);
|
||||
},
|
||||
errorCallback: () => {
|
||||
commit(types.SET_LOADING, false);
|
||||
createFlash(__('Failed to load error details from Sentry.'));
|
||||
},
|
||||
});
|
||||
|
||||
detailPoll.makeRequest();
|
||||
}
|
||||
|
||||
export function startPollingStacktrace({ commit }, endpoint) {
|
||||
stackTracePoll = new Poll({
|
||||
resource: service,
|
||||
method: 'getSentryData',
|
||||
data: { endpoint },
|
||||
successCallback: ({ data }) => {
|
||||
if (!data) {
|
||||
stackTracePoll.restart();
|
||||
return;
|
||||
}
|
||||
commit(types.SET_STACKTRACE_DATA, data.error);
|
||||
commit(types.SET_LOADING_STACKTRACE, false);
|
||||
|
||||
stopPolling(stackTracePoll);
|
||||
},
|
||||
errorCallback: () => {
|
||||
commit(types.SET_LOADING_STACKTRACE, false);
|
||||
createFlash(__('Failed to load stacktrace.'));
|
||||
},
|
||||
});
|
||||
|
||||
stackTracePoll.makeRequest();
|
||||
}
|
||||
|
||||
export default () => {};
|
|
@ -0,0 +1,3 @@
|
|||
export const stacktrace = state => state.stacktraceData.stack_trace_entries.reverse();
|
||||
|
||||
export default () => {};
|
|
@ -0,0 +1,4 @@
|
|||
export const SET_ERROR = 'SET_ERRORS';
|
||||
export const SET_LOADING = 'SET_LOADING';
|
||||
export const SET_LOADING_STACKTRACE = 'SET_LOADING_STACKTRACE';
|
||||
export const SET_STACKTRACE_DATA = 'SET_STACKTRACE_DATA';
|
|
@ -0,0 +1,16 @@
|
|||
import * as types from './mutation_types';
|
||||
|
||||
export default {
|
||||
[types.SET_ERROR](state, data) {
|
||||
state.error = data;
|
||||
},
|
||||
[types.SET_LOADING](state, loading) {
|
||||
state.loading = loading;
|
||||
},
|
||||
[types.SET_LOADING_STACKTRACE](state, data) {
|
||||
state.loadingStacktrace = data;
|
||||
},
|
||||
[types.SET_STACKTRACE_DATA](state, data) {
|
||||
state.stacktraceData = data;
|
||||
},
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue