summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-09-27 15:06:16 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-09-27 15:06:16 +0000
commit8320f7956d72986f5a7c850874fce4f8b5a8e015 (patch)
treec761b309cfff422609d47a17ac4d6a732c142f49
parent45482d5a2704da7fabe4ccf07f85d9be6e0a791a (diff)
downloadgitlab-ce-8320f7956d72986f5a7c850874fce4f8b5a8e015.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab-ci.yml3
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml21
-rw-r--r--.gitlab/ci/memory.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml299
-rw-r--r--.gitlab/ci/test-metadata.gitlab-ci.yml50
-rw-r--r--app/assets/javascripts/user_popovers.js23
-rw-r--r--app/helpers/gitlab_routing_helper.rb2
-rw-r--r--app/mailers/emails/pipelines.rb4
-rw-r--r--app/views/notify/pipeline_failed_email.html.haml2
-rw-r--r--app/views/notify/pipeline_failed_email.text.erb2
-rw-r--r--app/views/notify/pipeline_success_email.html.haml2
-rw-r--r--app/views/notify/pipeline_success_email.text.erb2
-rw-r--r--app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml2
-rw-r--r--changelogs/unreleased/bjk-32646_puma_killer.yml5
-rw-r--r--changelogs/unreleased/labels_rest.yml5
-rw-r--r--changelogs/unreleased/show-correct-link-in-mr-pipelines.yml5
-rw-r--r--doc/api/group_labels.md50
-rw-r--r--doc/api/labels.md62
-rw-r--r--doc/development/contributing/issue_workflow.md9
-rw-r--r--doc/development/testing_guide/ci.md33
-rw-r--r--doc/development/testing_guide/end_to_end/best_practices.md12
-rw-r--r--lib/api/group_labels.rb48
-rw-r--r--lib/api/helpers/label_helpers.rb42
-rw-r--r--lib/api/labels.rb66
-rw-r--r--lib/gitlab/cluster/puma_worker_killer_initializer.rb14
-rw-r--r--lib/quality/test_level.rb26
-rw-r--r--scripts/rspec_helpers.sh100
-rw-r--r--spec/javascripts/zen_mode_spec.js2
-rw-r--r--spec/lib/quality/test_level_spec.rb28
-rw-r--r--spec/mailers/emails/pipelines_spec.rb69
-rw-r--r--spec/requests/api/group_labels_spec.rb94
-rw-r--r--spec/requests/api/labels_spec.rb201
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb2
-rw-r--r--spec/support/shared_examples/ci/pipeline_email_examples.rb20
-rw-r--r--spec/views/notify/pipeline_failed_email.html.haml_spec.rb2
-rw-r--r--spec/views/notify/pipeline_failed_email.text.erb_spec.rb2
-rw-r--r--spec/views/notify/pipeline_success_email.html.haml_spec.rb2
-rw-r--r--spec/views/notify/pipeline_success_email.text.erb_spec.rb26
39 files changed, 929 insertions, 414 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6c10c4853c4..ff1f79a3fa4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -17,8 +17,7 @@ variables:
GIT_DEPTH: "20"
GIT_SUBMODULE_STRATEGY: "none"
GET_SOURCES_ATTEMPTS: "3"
- KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master.json
- EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/${CI_PROJECT_NAME}/rspec_report-master-ee.json
+ KNAPSACK_RSPEC_SUITE_REPORT_PATH: knapsack/report-master.json
FLAKY_RSPEC_SUITE_REPORT_PATH: rspec_flaky/report-suite.json
BUILD_ASSETS_IMAGE: "false"
ES_JAVA_OPTS: "-Xms256m -Xmx256m"
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 92149d5a93e..1feda7ed4d4 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -77,7 +77,7 @@ gitlab:assets:compile pull-cache:
- .default-before_script
- .assets-compile-cache
- .only-code-qa-changes
- - .use-pg
+ - .use-pg9
stage: prepare
script:
- node --version
@@ -120,7 +120,7 @@ compile-assets pull-cache:
- .default-only
- .default-before_script
- .only-code-changes
- - .use-pg
+ - .use-pg9
dependencies: ["compile-assets", "compile-assets pull-cache", "setup-test-env"]
karma:
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index 49dd778f4fb..e0ce6e2fde2 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -110,19 +110,34 @@
- $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
kubernetes: active
-.use-pg:
+.use-pg9:
services:
- - name: postgres:9.6.14
+ - name: postgres:9.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
-.use-pg-10:
+.use-pg10:
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33"
services:
- name: postgres:10.9
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine
+.use-pg9-ee:
+ services:
+ - name: postgres:9.6
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ - name: redis:alpine
+ - name: docker.elastic.co/elasticsearch/elasticsearch:5.6.12
+
+.use-pg10-ee:
+ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-10-graphicsmagick-1.3.33"
+ services:
+ - name: postgres:10.9
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ - name: redis:alpine
+ - name: docker.elastic.co/elasticsearch/elasticsearch:5.6.12
+
.only-ee:
only:
variables:
diff --git a/.gitlab/ci/memory.gitlab-ci.yml b/.gitlab/ci/memory.gitlab-ci.yml
index d990c7eefa2..93bf87b24b2 100644
--- a/.gitlab/ci/memory.gitlab-ci.yml
+++ b/.gitlab/ci/memory.gitlab-ci.yml
@@ -36,7 +36,7 @@ memory-static:
memory-on-boot:
extends:
- .only-code-memory-job-base
- - .use-pg-10
+ - .use-pg10
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 8851f2ba211..c315501b0ba 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -33,31 +33,29 @@
- .default-before_script
- .only-code-qa-changes
+setup-test-env:
+ extends:
+ - .only-code-qa-rails-job-base
+ - .use-pg9
+ stage: prepare
+ script:
+ - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
+ - scripts/gitaly-test-build # Do not use 'bundle exec' here
+ artifacts:
+ expire_in: 7d
+ paths:
+ - tmp/tests
+ - config/secrets.yml
+ - vendor/gitaly-ruby
+ cache:
+ policy: pull-push
+
.rspec-base:
extends: .only-code-rails-job-base
stage: test
script:
- - JOB_NAME=( $CI_JOB_NAME )
- - TEST_TOOL=${JOB_NAME[0]}
- - TEST_LEVEL=${JOB_NAME[1]}
- - DATABASE=${JOB_NAME[2]}
- - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- - export KNAPSACK_GENERATE_REPORT=true KNAPSACK_LOG_LEVEL=debug KNAPSACK_TEST_DIR=spec
- - export SUITE_FLAKY_RSPEC_REPORT_PATH=${FLAKY_RSPEC_SUITE_REPORT_PATH}
- - export FLAKY_RSPEC_REPORT_PATH=rspec_flaky/all_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- - export NEW_FLAKY_RSPEC_REPORT_PATH=rspec_flaky/new_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- - export FLAKY_RSPEC_GENERATE_REPORT=true
- - export CACHE_CLASSES=true
- - cp ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
- - '[[ -f $FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_REPORT_PATH}'
- - '[[ -f $NEW_FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${NEW_FLAKY_RSPEC_REPORT_PATH}'
- - scripts/gitaly-test-spawn
- - date
- - 'export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new.pattern(:${TEST_LEVEL})")'
- - mkdir -p tmp/memory_test
- - export MEMORY_TEST_PATH="tmp/memory_test/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_memory.csv"
- - knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag level:${TEST_LEVEL} --tag ~geo"
- - date
+ - source scripts/rspec_helpers.sh
+ - rspec_paralellized_job "--tag ~quarantine --tag ~geo"
artifacts:
expire_in: 31d
when: always
@@ -71,78 +69,145 @@
reports:
junit: junit_rspec.xml
-.rspec-base-pg:
+.rspec-base-pg9:
extends:
- .rspec-base
- - .use-pg
+ - .use-pg9
-.rspec-base-pg-10:
+.rspec-base-pg10:
extends:
- .rspec-base
- - .use-pg-10
+ - .use-pg10
+ - .only-master
-setup-test-env:
- extends:
- - .only-code-qa-rails-job-base
- - .use-pg
- stage: prepare
- script:
- - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
- - scripts/gitaly-test-build # Do not use 'bundle exec' here
- artifacts:
- expire_in: 7d
- paths:
- - tmp/tests
- - config/secrets.yml
- - vendor/gitaly-ruby
- cache:
- policy: pull-push
+rspec unit pg9:
+ extends: .rspec-base-pg9
+ parallel: 20
+
+rspec integration pg9:
+ extends: .rspec-base-pg9
+ parallel: 6
-rspec unit pg:
- extends: .rspec-base-pg
+rspec system pg9:
+ extends: .rspec-base-pg9
+ parallel: 24
+
+rspec unit pg10:
+ extends: .rspec-base-pg10
parallel: 20
-rspec integration pg:
- extends: .rspec-base-pg
+rspec integration pg10:
+ extends: .rspec-base-pg10
parallel: 6
-rspec system pg:
- extends: .rspec-base-pg
+rspec system pg10:
+ extends: .rspec-base-pg10
parallel: 24
-rspec unit pg-10:
+.rspec-ee-base-pg9:
+ extends:
+ - .rspec-base
+ - .only-ee
+ - .use-pg9-ee
+
+.rspec-ee-base-pg10:
+ extends:
+ - .rspec-base
+ - .only-ee
+ - .use-pg10-ee
+
+rspec-ee unit pg9:
+ extends: .rspec-ee-base-pg9
+ parallel: 7
+
+rspec-ee integration pg9:
+ extends: .rspec-ee-base-pg9
+ parallel: 3
+
+rspec-ee system pg9:
+ extends: .rspec-ee-base-pg9
+ parallel: 5
+
+rspec-ee unit pg10:
extends:
- - .rspec-base-pg-10
+ - .rspec-ee-base-pg10
- .only-master
- parallel: 20
+ parallel: 7
-rspec integration pg-10:
+rspec-ee integration pg10:
extends:
- - .rspec-base-pg-10
+ - .rspec-ee-base-pg10
- .only-master
- parallel: 6
+ parallel: 3
-rspec system pg-10:
+rspec-ee system pg10:
extends:
- - .rspec-base-pg-10
+ - .rspec-ee-base-pg10
- .only-master
- parallel: 24
+ parallel: 5
-rspec-fast-spec-helper:
- extends: .rspec-base-pg
+.rspec-ee-base-geo:
+ extends:
+ - .rspec-base
+ - .only-ee
script:
- - bundle exec rspec spec/fast_spec_helper.rb
+ - source scripts/rspec_helpers.sh
+ - scripts/prepare_postgres_fdw.sh
+ - rspec_paralellized_job "--tag ~quarantine --tag geo"
+
+.rspec-ee-base-geo-pg9:
+ extends:
+ - .rspec-ee-base-geo
+ - .use-pg9-ee
+
+.rspec-ee-base-geo-pg10:
+ extends:
+ - .rspec-ee-base-geo
+ - .use-pg10-ee
+
+rspec-ee unit pg9 geo:
+ extends: .rspec-ee-base-geo-pg9
+ parallel: 2
+
+rspec-ee integration pg9 geo:
+ extends: .rspec-ee-base-geo-pg9
-rspec quarantine pg:
+rspec-ee system pg9 geo:
+ extends: .rspec-ee-base-geo-pg9
+
+rspec-ee unit pg10 geo:
+ extends: .rspec-ee-base-geo-pg10
+ parallel: 2
+
+rspec-ee integration pg10 geo:
+ extends: .rspec-ee-base-geo-pg10
+
+rspec-ee system pg10 geo:
+ extends: .rspec-ee-base-geo-pg10
+
+rspec quarantine pg9:
extends:
- - .rspec-base-pg
+ - .rspec-base-pg9
- .only-master
+ variables:
+ RSPEC_OPTS: "--tag quarantine -- spec/"
script:
- - export NO_KNAPSACK=1 CACHE_CLASSES=true
- - scripts/gitaly-test-spawn
- - bin/rspec --color --format documentation --tag quarantine -- spec/
+ - source scripts/rspec_helpers.sh
+ - rspec_simple_job "${RSPEC_OPTS}"
allow_failure: true
+rspec-ee quarantine pg9:
+ extends:
+ - rspec quarantine pg9
+ - .only-ee
+ variables:
+ RSPEC_OPTS: "--tag quarantine -- ee/spec/"
+
+rspec fast_spec_helper:
+ extends: .rspec-base-pg9
+ script:
+ - bin/rspec spec/fast_spec_helper.rb
+
static-analysis:
extends: .only-code-qa-rails-job-base
dependencies: ["setup-test-env", "compile-assets", "compile-assets pull-cache"]
@@ -174,7 +239,7 @@ downtime_check:
.db-job-base:
extends:
- .only-code-rails-job-base
- - .use-pg
+ - .use-pg9
stage: test
dependencies: ["setup-test-env"]
needs: ["setup-test-env"]
@@ -258,108 +323,6 @@ coverage:
- coverage/assets/
- tmp/memory_test/
-## EE-specific content
-.rspec-base-ee:
- extends:
- - .rspec-base
- - .only-ee
- script:
- - JOB_NAME=( $CI_JOB_NAME )
- - TEST_TOOL=${JOB_NAME[0]}
- - TEST_LEVEL=${JOB_NAME[1]}
- - DATABASE=${JOB_NAME[2]}
- - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_ee_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- - export KNAPSACK_GENERATE_REPORT=true KNAPSACK_LOG_LEVEL=debug KNAPSACK_TEST_DIR=spec
- - export CACHE_CLASSES=true
- - cp ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
- - scripts/gitaly-test-spawn
- - date
- - 'export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new(%(ee/)).pattern(:${TEST_LEVEL})")'
- - mkdir -p tmp/memory_test
- - export MEMORY_TEST_PATH="tmp/memory_test/ee_${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_memory.csv"
- - knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag level:${TEST_LEVEL} --tag ~geo"
- - date
-
-.rspec-base-pg-ee:
- extends: .rspec-base-ee
- services:
- - name: postgres:9.6
- command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- - name: redis:alpine
- - name: docker.elastic.co/elasticsearch/elasticsearch:5.6.12
-
-rspec unit pg ee:
- extends: .rspec-base-pg-ee
- parallel: 7
-
-rspec integration pg ee:
- extends: .rspec-base-pg-ee
- parallel: 3
-
-rspec system pg ee:
- extends: .rspec-base-pg-ee
- parallel: 5
-
-.rspec-base-geo:
- extends: .rspec-base-ee
- parallel: 3
- script:
- - JOB_NAME=( $CI_JOB_NAME )
- - TEST_TOOL=${JOB_NAME[0]}
- - TEST_LEVEL=${JOB_NAME[1]}
- - DATABASE=${JOB_NAME[2]}
- - export KNAPSACK_TEST_FILE_PATTERN="ee/spec/**{,/*/**}/*_spec.rb" KNAPSACK_GENERATE_REPORT=true CACHE_CLASSES=true
- - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json
- - cp ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH} ${KNAPSACK_REPORT_PATH}
- - source scripts/prepare_postgres_fdw.sh
- - scripts/gitaly-test-spawn
- - mkdir -p tmp/memory_test
- - export MEMORY_TEST_PATH="tmp/memory_test/ee_${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_memory.csv"
- - knapsack rspec "-Ispec --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag geo"
-
-rspec geo pg ee:
- extends:
- - .rspec-base-geo
- - .use-pg
- except:
- variables:
- - $CI_COMMIT_REF_NAME =~ /(^geo[\/-].*|.*-geo$)/
-
-rspec geo pg-10 ee:
- extends:
- - .rspec-base-geo
- - .use-pg-10
- except:
- variables:
- - $CI_COMMIT_REF_NAME =~ /(^geo[\/-].*|.*-geo$)/
-
-quick-rspec geo pg ee:
- extends:
- - .rspec-base-geo
- - .use-pg
- stage: quick-test
- only:
- variables:
- - $CI_COMMIT_REF_NAME =~ /(^geo[\/-].*|.*-geo$)/
-
-quick-rspec geo pg-10 ee:
- extends:
- - .rspec-base-geo
- - .use-pg-10
- stage: quick-test
- only:
- variables:
- - $CI_COMMIT_REF_NAME =~ /(^geo[\/-].*|.*-geo$)/
-
-rspec quarantine pg ee:
- extends:
- - rspec quarantine pg
- - .only-ee
- script:
- - export NO_KNAPSACK=1 CACHE_CLASSES=true
- - scripts/gitaly-test-spawn
- - bin/rspec --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag quarantine -- ee/spec/
-
db:rollback geo:
extends:
- db:rollback
@@ -367,5 +330,3 @@ db:rollback geo:
script:
- bundle exec rake geo:db:migrate VERSION=20170627195211
- bundle exec rake geo:db:migrate
-
-## END of EE-specific content
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index 5e2523e8a9c..6a7f3157d59 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -1,10 +1,16 @@
.tests-metadata-state:
extends:
- .default-only
+ - .only-code-changes
variables:
TESTS_METADATA_S3_BUCKET: "gitlab-ce-cache"
before_script:
- source scripts/utils.sh
+ cache:
+ key: tests_metadata
+ paths:
+ - knapsack/
+ - rspec_flaky/
artifacts:
expire_in: 31d
paths:
@@ -13,55 +19,29 @@
- rspec_profiling/
retrieve-tests-metadata:
- extends:
- - .tests-metadata-state
- - .only-code-changes
+ extends: .tests-metadata-state
stage: prepare
cache:
- key: tests_metadata
policy: pull
script:
- - mkdir -p knapsack/${CI_PROJECT_NAME}/
- - wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH
- - '[[ -f $KNAPSACK_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${KNAPSACK_RSPEC_SUITE_REPORT_PATH}'
- - mkdir -p rspec_flaky/
- - mkdir -p rspec_profiling/
- - wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH
- - '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}'
- - '[[ ! -d "ee/" ]] || wget -O $EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH'
- - '[[ ! -d "ee/" ]] || [[ -f $EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH}'
+ - source scripts/rspec_helpers.sh
+ - retrieve_tests_metadata
update-tests-metadata:
- extends:
- - .tests-metadata-state
- - .only-code-changes
+ extends: .tests-metadata-state
stage: post-test
cache:
- key: tests_metadata
- paths:
- - knapsack/
- - rspec_flaky/
policy: push
script:
- retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document
- - echo "{}" > ${KNAPSACK_RSPEC_SUITE_REPORT_PATH}
- - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_*_pg_node_*.json
- - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH'
- - '[[ ! -d "ee/" ]] || echo "{}" > ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH}'
- - '[[ ! -d "ee/" ]] || scripts/merge-reports ${EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_*_pg_ee_*node_*.json'
- - '[[ ! -d "ee/" ]] || [[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $EE_KNAPSACK_RSPEC_SUITE_REPORT_PATH'
- - rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
- - scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json
- - FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH}
- - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $FLAKY_RSPEC_SUITE_REPORT_PATH'
- - rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json
- - scripts/insert-rspec-profiling-data
+ - source scripts/rspec_helpers.sh
+ - update_tests_metadata
only:
refs:
- - master
+ - schedules
variables:
- - $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE == "gitlab-org"
- - $CI_SERVER_HOST == "dev.gitlab.org"
+ # Only update the Knapsack metadata on GitLab.com/gitlab-org/gitlab
+ - $CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_PATH == "gitlab-org/gitlab"
flaky-examples-check:
extends:
diff --git a/app/assets/javascripts/user_popovers.js b/app/assets/javascripts/user_popovers.js
index 948f4d5e631..c0b7587be10 100644
--- a/app/assets/javascripts/user_popovers.js
+++ b/app/assets/javascripts/user_popovers.js
@@ -63,7 +63,7 @@ const handleUserPopoverMouseOver = event => {
UsersCache.retrieveById(userId)
.then(userData => {
if (!userData) {
- return;
+ return undefined;
}
Object.assign(user, {
@@ -76,19 +76,16 @@ const handleUserPopoverMouseOver = event => {
loaded: true,
});
- UsersCache.retrieveStatusById(userId)
- .then(status => {
- if (!status) {
- return;
- }
+ return UsersCache.retrieveStatusById(userId);
+ })
+ .then(status => {
+ if (!status) {
+ return;
+ }
- Object.assign(user, {
- status,
- });
- })
- .catch(() => {
- throw new Error(`User status for "${userId}" could not be retrieved!`);
- });
+ Object.assign(user, {
+ status,
+ });
})
.catch(() => {
renderedPopover.$destroy();
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index f524696cc2f..f05218efe0c 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -58,7 +58,7 @@ module GitlabRoutingHelper
end
def commits_url(entity, *args)
- project_commits_url(entity.project, entity.ref, *args)
+ project_commits_url(entity.project, entity.source_ref, *args)
end
def commit_url(entity, *args)
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index fb57c0da34d..34e12a5fa6d 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -15,7 +15,7 @@ module Emails
def pipeline_mail(pipeline, recipients, status)
@project = pipeline.project
@pipeline = pipeline
- @merge_request = pipeline.merge_requests_as_head_pipeline.first
+ @merge_request = pipeline.all_merge_requests.first
add_headers
# We use bcc here because we don't want to generate this emails for a
@@ -44,7 +44,7 @@ module Emails
commit = [@pipeline.short_sha]
commit << "in #{@merge_request.to_reference}" if @merge_request
- subject("Pipeline ##{@pipeline.id} has #{status} for #{@pipeline.ref}", commit.join(' '))
+ subject("Pipeline ##{@pipeline.id} has #{status} for #{@pipeline.source_ref}", commit.join(' '))
end
end
end
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index 86dcca4a447..f01181857ce 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -34,7 +34,7 @@
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
- = @pipeline.ref
+ = @pipeline.source_ref
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
diff --git a/app/views/notify/pipeline_failed_email.text.erb b/app/views/notify/pipeline_failed_email.text.erb
index 722eedf90be..9cd479ef1e6 100644
--- a/app/views/notify/pipeline_failed_email.text.erb
+++ b/app/views/notify/pipeline_failed_email.text.erb
@@ -1,7 +1,7 @@
Your pipeline has failed.
Project: <%= @project.name %> ( <%= project_url(@project) %> )
-Branch: <%= @pipeline.ref %> ( <%= commits_url(@pipeline) %> )
+Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> )
<% if @merge_request -%>
Merge Request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> )
<% end -%>
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index 4fe3c4c8269..e575a5569fa 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -34,7 +34,7 @@
%img{ height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-branch-gray.gif'), style: "display:block;", width: "13", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
- = @pipeline.ref
+ = @pipeline.source_ref
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
diff --git a/app/views/notify/pipeline_success_email.text.erb b/app/views/notify/pipeline_success_email.text.erb
index 9aadf380f79..4005158dc9e 100644
--- a/app/views/notify/pipeline_success_email.text.erb
+++ b/app/views/notify/pipeline_success_email.text.erb
@@ -1,7 +1,7 @@
Your pipeline has passed.
Project: <%= @project.name %> ( <%= project_url(@project) %> )
-Branch: <%= @pipeline.ref %> ( <%= commits_url(@pipeline) %> )
+Branch: <%= @pipeline.source_ref %> ( <%= commits_url(@pipeline) %> )
<% if @merge_request -%>
Merge Request: <%= @merge_request.to_reference %> ( <%= merge_request_url(@merge_request) %> )
<% end -%>
diff --git a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
index 755fd3a17d3..fb03e6e12e3 100644
--- a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
+++ b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
@@ -1,5 +1,5 @@
- if show_auto_devops_implicitly_enabled_banner?(project, current_user)
- .qa-auto-devops-banner.auto-devops-implicitly-enabled-banner.alert.alert-warning
+ .qa-auto-devops-banner.auto-devops-implicitly-enabled-banner.alert.alert-info
- more_information_link = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link'
- auto_devops_message = s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}") % { more_information_link: more_information_link }
= auto_devops_message.html_safe
diff --git a/changelogs/unreleased/bjk-32646_puma_killer.yml b/changelogs/unreleased/bjk-32646_puma_killer.yml
new file mode 100644
index 00000000000..11318966a01
--- /dev/null
+++ b/changelogs/unreleased/bjk-32646_puma_killer.yml
@@ -0,0 +1,5 @@
+---
+title: Update PumaWorkerKiller defaults
+merge_request: 17758
+author:
+type: performance
diff --git a/changelogs/unreleased/labels_rest.yml b/changelogs/unreleased/labels_rest.yml
new file mode 100644
index 00000000000..e2b37ee2a11
--- /dev/null
+++ b/changelogs/unreleased/labels_rest.yml
@@ -0,0 +1,5 @@
+---
+title: Add proper label REST API for update, delete and promote
+merge_request: 17239
+author: Mathieu Parent
+type: added
diff --git a/changelogs/unreleased/show-correct-link-in-mr-pipelines.yml b/changelogs/unreleased/show-correct-link-in-mr-pipelines.yml
new file mode 100644
index 00000000000..8a4b557f716
--- /dev/null
+++ b/changelogs/unreleased/show-correct-link-in-mr-pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Show the original branch name and link of merge request in pipeline emails
+merge_request: 17513
+author:
+type: fixed
diff --git a/doc/api/group_labels.md b/doc/api/group_labels.md
index 9563f967a2a..f3c3a821354 100644
--- a/doc/api/group_labels.md
+++ b/doc/api/group_labels.md
@@ -51,6 +51,40 @@ Example response:
]
```
+## Get a single group label
+
+Get a single label for a given group.
+
+```
+GET /groups/:id/labels/:label_id
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `label_id` | integer or string | yes | The ID or title of a group's label. |
+| `include_ancestor_groups` | boolean | no | Include ancestor groups. Defaults to `true`. |
+
+```bash
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels/bug
+```
+
+Example response:
+
+```json
+{
+ "id": 7,
+ "name": "bug",
+ "color": "#FF0000",
+ "text_color" : "#FFFFFF",
+ "description": null,
+ "open_issues_count": 0,
+ "closed_issues_count": 0,
+ "open_merge_requests_count": 0,
+ "subscribed": false
+}
+```
+
## Create a new group label
Create a new group label for a given group.
@@ -91,19 +125,19 @@ Example response:
Updates an existing group label. At least one parameter is required, to update the group label.
```
-PUT /groups/:id/labels
+PUT /groups/:id/labels/:label_id
```
| Attribute | Type | Required | Description |
| ------------- | ------- | -------- | ---------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `name` | string | yes | The name of the label |
+| `label_id` | integer or string | yes | The ID or title of a group's label. |
| `new_name` | string | no | The new name of the label |
| `color` | string | no | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) |
| `description` | string | no | The description of the label. |
```bash
-curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "Feature Proposal", "new_name": "Feature Idea" }' https://gitlab.example.com/api/v4/groups/5/labels
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"new_name": "Feature Idea" }' https://gitlab.example.com/api/v4/groups/5/labels/Feature%20Proposal
```
Example response:
@@ -122,23 +156,27 @@ Example response:
}
```
+NOTE: **Note:** An older endpoint `PUT /groups/:id/labels` with `name` in the params is still available, but deprecated.
+
## Delete a group label
Deletes a group label with a given name.
```
-DELETE /groups/:id/labels
+DELETE /groups/:id/labels/:label_id
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `name` | string | yes | The name of the label. |
+| `label_id` | integer or string | yes | The ID or title of a group's label. |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels?name=bug
+curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels/bug
```
+NOTE: **Note:** An older endpoint `DELETE /groups/:id/labels` with `name` in the params is still available, but deprecated.
+
## Subscribe to a group label
Subscribes the authenticated user to a group label to receive notifications. If
diff --git a/doc/api/labels.md b/doc/api/labels.md
index f29c0a28cdf..525dbe02e5f 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -90,6 +90,42 @@ Example response:
]
```
+## Get a single project label
+
+Get a single label for a given project.
+
+```
+GET /projects/:id/labels/:label_id
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ------- | -------- | --------------------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `label_id` | integer or string | yes | The ID or title of a group's label. |
+| `include_ancestor_groups` | boolean | no | Include ancestor groups. Defaults to `true`. |
+
+```bash
+curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/1/labels/bug
+```
+
+Example response:
+
+```json
+{
+ "id" : 1,
+ "name" : "bug",
+ "color" : "#d9534f",
+ "text_color" : "#FFFFFF",
+ "description": "Bug reported by user",
+ "open_issues_count": 1,
+ "closed_issues_count": 0,
+ "open_merge_requests_count": 1,
+ "subscribed": false,
+ "priority": 10,
+ "is_project_label": true
+}
+```
+
## Create a new label
Creates a new label for the given repository with the given name and color.
@@ -133,40 +169,40 @@ Example response:
Deletes a label with a given name.
```
-DELETE /projects/:id/labels
+DELETE /projects/:id/labels/:label_id
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `label_id` | integer | yes (or `name`) | The id of the existing label |
-| `name` | string | yes (or `label_id`) | The name of the existing label |
+| `label_id` | integer or string | yes | The ID or title of a group's label. |
```bash
-curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels?name=bug"
+curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels/bug"
```
+NOTE: **Note:** An older endpoint `DELETE /projects/:id/labels` with `name` in the params is still available, but deprecated.
+
## Edit an existing label
Updates an existing label with new name or new color. At least one parameter
is required, to update the label.
```
-PUT /projects/:id/labels
+PUT /projects/:id/labels/:label_id
```
| Attribute | Type | Required | Description |
| --------------- | ------- | --------------------------------- | ------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `label_id` | integer | yes (or `name`) | The id of the existing label |
-| `name` | string | yes (or `label_id`) | The name of the existing label |
+| `label_id` | integer or string | yes | The ID or title of a group's label. |
| `new_name` | string | yes if `color` is not provided | The new name of the label |
| `color` | string | yes if `new_name` is not provided | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) |
| `description` | string | no | The new description of the label |
| `priority` | integer | no | The new priority of the label. Must be greater or equal than zero or `null` to remove the priority. |
```bash
-curl --request PUT --data "name=documentation&new_name=docs&color=#8E44AD&description=Documentation" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels"
+curl --request PUT --data "new_name=docs&color=#8E44AD&description=Documentation" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels/documentation"
```
Example response:
@@ -187,6 +223,8 @@ Example response:
}
```
+NOTE: **Note:** An older endpoint `PUT /projects/:id/labels` with `name` or `label_id` in the params is still available, but deprecated.
+
## Promote a project label to a group label
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/25218) in GitLab 12.3.
@@ -194,16 +232,16 @@ Example response:
Promotes a project label to a group label.
```
-PUT /projects/:id/labels/promote
+PUT /projects/:id/labels/:label_id/promote
```
| Attribute | Type | Required | Description |
| --------------- | ------- | --------------------------------- | ------------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
-| `name` | string | yes | The name of the existing label |
+| `label_id` | integer or string | yes | The ID or title of a group's label. |
```bash
-curl --request PUT --data "name=documentation" --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels/promote"
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/labels/documentation/promote"
```
Example response:
@@ -221,6 +259,8 @@ Example response:
}
```
+NOTE: **Note:** An older endpoint `PUT /projects/:id/labels/promote` with `name` in the params is still available, but deprecated.
+
## Subscribe to a label
Subscribes the authenticated user to a label to receive notifications.
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index dfe2bc43ecc..a93bd50534f 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -149,8 +149,13 @@ You can find the groups listed in the [Product Stages, Groups, and Categories](h
We use the term group to map down product requirements from our product stages.
As a team needs some way to collect the work their members are planning to be assigned to, we use the `~group::` labels to do so.
-Normally there is a 1:1 relationship between Stage labels and Group labels. In the spirit of "Everyone can contribute",
-any issue can be picked up by any group, depending on current priorities. For example, an issue labeled ~"devops::create" may be picked up by the ~"group::access" group.
+Normally there is a 1:1 relationship between Stage labels and Group labels. In
+the spirit of "Everyone can contribute", any issue can be picked up by any group,
+depending on current priorities. When picking up an issue belonging to a different
+group, it should be relabelled. For example, if an issue labelled ~"devops::create"
+and ~"group::knowledge" is picked up by someone in the Access group of the Plan stage,
+the issue should be relabelled as ~"group::access" while keeping the original
+~"devops::create" unchanged.
We also use stage and group labels to help quantify our [throughput](https://about.gitlab.com/handbook/engineering/management/throughput/).
Please read [Stage and Group labels in Throughtput](https://about.gitlab.com/handbook/engineering/management/throughput/#stage-and-group-labels-in-throughput) for more information on how the labels are used in this context.
diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md
index d9f66a827de..5bdd0a69d7f 100644
--- a/doc/development/testing_guide/ci.md
+++ b/doc/development/testing_guide/ci.md
@@ -4,27 +4,24 @@
Our current CI parallelization setup is as follows:
-1. The `knapsack` job in the prepare stage that is supposed to ensure we have a
- `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file:
- - The `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file is fetched
- from S3, if it's not here we initialize the file with `{}`.
-1. Each `rspec x y` job are run with `knapsack rspec` and should have an evenly
- distributed share of tests:
- - It works because the jobs have access to the
- `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` since the "artifacts
- from all previous stages are passed by default".
+1. The `retrieve-tests-metadata` job in the `prepare` stage ensures we have a
+ `knapsack/report-master.json` file:
+ - The `knapsack/report-master.json` file is fetched from S3, if it's not here
+ we initialize the file with `{}`.
+1. Each `[rspec|rspec-ee] [unit|integration|system|geo] n m` job are run with
+ `knapsack rspec` and should have an evenly distributed share of tests:
+ - It works because the jobs have access to the `knapsack/report-master.json`
+ since the "artifacts from all previous stages are passed by default".
- the jobs set their own report path to
- `KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`.
+ `"knapsack/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"`.
- if knapsack is doing its job, test files that are run should be listed under
`Report specs`, not under `Leftover specs`.
-1. The `update-knapsack` job takes all the
- `knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`
- files from the `rspec x y` jobs and merge them all together into a single
- `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file that is then
- uploaded to S3.
-
-After that, the next pipeline will use the up-to-date
-`knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file.
+1. The `update-tests-metadata` job (which only runs on scheduled pipelines for
+ [the canonical project](https://gitlab.com/gitlab-org/gitlab) takes all the
+ `knapsack/rspec*_pg_*.json` files and merge them all together into a single
+ `knapsack/report-master.json` file that is then uploaded to S3.
+
+After that, the next pipeline will use the up-to-date `knapsack/report-master.json` file.
## Monitoring
diff --git a/doc/development/testing_guide/end_to_end/best_practices.md b/doc/development/testing_guide/end_to_end/best_practices.md
index 2200069ecfd..042879b47aa 100644
--- a/doc/development/testing_guide/end_to_end/best_practices.md
+++ b/doc/development/testing_guide/end_to_end/best_practices.md
@@ -53,3 +53,15 @@ In summary:
- **Do**: Split tests across separate files, unless the tests share expensive setup.
- **Don't**: Put new tests in an existing file without considering the impact on parallelization.
+
+## Limit the use of `before(:all)` hook
+
+Limit the use of `before(:all)` to perform setup tasks with only API calls, non UI operations
+or basic UI operations such as login.
+
+We use [`capybara-screenshot`](https://github.com/mattheworiordan/capybara-screenshot) library to automatically save screenshots on failures.
+This library [saves the screenshots in the RSpec's `after` hook](https://github.com/mattheworiordan/capybara-screenshot/blob/master/lib/capybara-screenshot/rspec.rb#L97).
+[If there is a failure in `before(:all)`, the `after` hook is not called](https://github.com/rspec/rspec-core/pull/2652/files#diff-5e04af96d5156e787f28d519a8c99615R148) and so the screenshots are not saved.
+
+Given this fact, we should limit the use of `before(:all)` to only those operations where a screenshot is not
+necessary in case of failure and QA logs would be enough for debugging.
diff --git a/lib/api/group_labels.rb b/lib/api/group_labels.rb
index cb044d4095f..7585293031f 100644
--- a/lib/api/group_labels.rb
+++ b/lib/api/group_labels.rb
@@ -26,6 +26,18 @@ module API
get_labels(user_group, Entities::GroupLabel, include_ancestor_groups: params[:include_ancestor_groups])
end
+ desc 'Get a single label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ optional :include_ancestor_groups, type: Boolean, default: true,
+ desc: 'Include ancestor groups'
+ end
+ get ':id/labels/:name' do
+ get_label(user_group, Entities::GroupLabel, include_ancestor_groups: params[:include_ancestor_groups])
+ end
+
desc 'Create a new label' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
@@ -38,22 +50,21 @@ module API
end
desc 'Update an existing label. At least one optional parameter is required.' do
- detail 'This feature was added in GitLab 11.8'
+ detail 'This feature was added in GitLab 11.8 and deprecated in GitLab 12.4.'
success Entities::GroupLabel
end
params do
- requires :name, type: String, desc: 'The name of the label to be updated'
- optional :new_name, type: String, desc: 'The new name of the label'
- optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
- optional :description, type: String, desc: 'The new description of label'
- at_least_one_of :new_name, :color, :description
+ optional :label_id, type: Integer, desc: 'The id of the label to be updated'
+ optional :name, type: String, desc: 'The name of the label to be updated'
+ use :group_label_update_params
+ exactly_one_of :label_id, :name
end
put ':id/labels' do
update_label(user_group, Entities::GroupLabel)
end
desc 'Delete an existing label' do
- detail 'This feature was added in GitLab 11.8'
+ detail 'This feature was added in GitLab 11.8 and deprecated in GitLab 12.4.'
success Entities::GroupLabel
end
params do
@@ -62,6 +73,29 @@ module API
delete ':id/labels' do
delete_label(user_group)
end
+
+ desc 'Update an existing label. At least one optional parameter is required.' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be updated'
+ use :group_label_update_params
+ end
+ put ':id/labels/:name' do
+ update_label(user_group, Entities::GroupLabel)
+ end
+
+ desc 'Delete an existing label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be deleted'
+ end
+ delete ':id/labels/:name' do
+ delete_label(user_group)
+ end
end
end
end
diff --git a/lib/api/helpers/label_helpers.rb b/lib/api/helpers/label_helpers.rb
index 126747e4f34..2fb2d9b79cf 100644
--- a/lib/api/helpers/label_helpers.rb
+++ b/lib/api/helpers/label_helpers.rb
@@ -11,6 +11,23 @@ module API
optional :description, type: String, desc: 'The description of label to be created'
end
+ params :label_update_params do
+ optional :new_name, type: String, desc: 'The new name of the label'
+ optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
+ optional :description, type: String, desc: 'The new description of label'
+ end
+
+ params :project_label_update_params do
+ use :label_update_params
+ optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
+ at_least_one_of :new_name, :color, :description, :priority
+ end
+
+ params :group_label_update_params do
+ use :label_update_params
+ at_least_one_of :new_name, :color, :description
+ end
+
def find_label(parent, id_or_title, include_ancestor_groups: true)
labels = available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)
label = labels.find_by_id(id_or_title) || labels.find_by_title(id_or_title)
@@ -26,6 +43,12 @@ module API
with_counts: params[:with_counts]
end
+ def get_label(parent, entity, include_ancestor_groups: true)
+ label = find_label(parent, params_id_or_title, include_ancestor_groups: include_ancestor_groups)
+
+ present label, with: entity, current_user: current_user, parent: parent
+ end
+
def create_label(parent, entity)
authorize! :admin_label, parent
@@ -57,6 +80,7 @@ module API
# params is used to update the label so we need to remove this field here
params.delete(:label_id)
+ params.delete(:name)
label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label)
render_validation_error!(label) unless label.valid?
@@ -80,6 +104,24 @@ module API
destroy_conditionally!(label)
end
+ def promote_label(parent)
+ authorize! :admin_label, parent
+
+ label = find_label(parent, params[:name], include_ancestor_groups: false)
+
+ begin
+ group_label = ::Labels::PromoteService.new(parent, current_user).execute(label)
+
+ if group_label
+ present group_label, with: Entities::GroupLabel, current_user: current_user, parent: parent.group
+ else
+ render_api_error!('Failed to promote project label to group label', 400)
+ end
+ rescue => error
+ render_api_error!(error.to_s, 400)
+ end
+ end
+
def params_id_or_title
@params_id_or_title ||= params[:label_id] || params[:name]
end
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 12553cbbbfa..2b283d82e4a 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -25,6 +25,18 @@ module API
get_labels(user_project, Entities::ProjectLabel, include_ancestor_groups: params[:include_ancestor_groups])
end
+ desc 'Get a single label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::ProjectLabel
+ end
+ params do
+ optional :include_ancestor_groups, type: Boolean, default: true,
+ desc: 'Include ancestor groups'
+ end
+ get ':id/labels/:name' do
+ get_label(user_project, Entities::ProjectLabel, include_ancestor_groups: params[:include_ancestor_groups])
+ end
+
desc 'Create a new label' do
success Entities::ProjectLabel
end
@@ -37,23 +49,21 @@ module API
end
desc 'Update an existing label. At least one optional parameter is required.' do
+ detail 'This feature was deprecated in GitLab 12.4.'
success Entities::ProjectLabel
end
params do
optional :label_id, type: Integer, desc: 'The id of the label to be updated'
optional :name, type: String, desc: 'The name of the label to be updated'
- optional :new_name, type: String, desc: 'The new name of the label'
- optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
- optional :description, type: String, desc: 'The new description of label'
- optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
+ use :project_label_update_params
exactly_one_of :label_id, :name
- at_least_one_of :new_name, :color, :description, :priority
end
put ':id/labels' do
update_label(user_project, Entities::ProjectLabel)
end
desc 'Delete an existing label' do
+ detail 'This feature was deprecated in GitLab 12.4.'
success Entities::ProjectLabel
end
params do
@@ -66,28 +76,48 @@ module API
end
desc 'Promote a label to a group label' do
- detail 'This feature was added in GitLab 12.3'
+ detail 'This feature was added in GitLab 12.3 and deprecated in GitLab 12.4.'
success Entities::GroupLabel
end
params do
requires :name, type: String, desc: 'The name of the label to be promoted'
end
put ':id/labels/promote' do
- authorize! :admin_label, user_project
+ promote_label(user_project)
+ end
- label = find_label(user_project, params[:name], include_ancestor_groups: false)
+ desc 'Update an existing label. At least one optional parameter is required.' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::ProjectLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be updated'
+ use :project_label_update_params
+ end
+ put ':id/labels/:name' do
+ update_label(user_project, Entities::ProjectLabel)
+ end
- begin
- group_label = ::Labels::PromoteService.new(user_project, current_user).execute(label)
+ desc 'Delete an existing label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::ProjectLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be deleted'
+ end
+ delete ':id/labels/:name' do
+ delete_label(user_project)
+ end
- if group_label
- present group_label, with: Entities::GroupLabel, current_user: current_user, parent: user_project.group
- else
- render_api_error!('Failed to promote project label to group label', 400)
- end
- rescue => error
- render_api_error!(error.to_s, 400)
- end
+ desc 'Promote a label to a group label' do
+ detail 'This feature was added in GitLab 12.4.'
+ success Entities::GroupLabel
+ end
+ params do
+ requires :name, type: String, desc: 'The name or id of the label to be promoted'
+ end
+ put ':id/labels/:name/promote' do
+ promote_label(user_project)
end
end
end
diff --git a/lib/gitlab/cluster/puma_worker_killer_initializer.rb b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
index 4affc52b7b0..a8440b63baa 100644
--- a/lib/gitlab/cluster/puma_worker_killer_initializer.rb
+++ b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
@@ -3,7 +3,7 @@
module Gitlab
module Cluster
class PumaWorkerKillerInitializer
- def self.start(puma_options, puma_per_worker_max_memory_mb: 650)
+ def self.start(puma_options, puma_per_worker_max_memory_mb: 850, puma_master_max_memory_mb: 550)
require 'puma_worker_killer'
PumaWorkerKiller.config do |config|
@@ -12,10 +12,9 @@ module Gitlab
# not each worker as is the case with GITLAB_UNICORN_MEMORY_MAX
worker_count = puma_options[:workers] || 1
# The Puma Worker Killer checks the total RAM used by both the master
- # and worker processes. Bump the limits to N+1 instead of N workers
- # to account for this:
+ # and worker processes.
# https://github.com/schneems/puma_worker_killer/blob/v0.1.0/lib/puma_worker_killer/puma_memory.rb#L57
- config.ram = (worker_count + 1) * puma_per_worker_max_memory_mb
+ config.ram = puma_master_max_memory_mb + (worker_count * puma_per_worker_max_memory_mb)
config.frequency = 20 # seconds
@@ -23,10 +22,9 @@ module Gitlab
# of available RAM.
config.percent_usage = 0.98
- # Ideally we'll never hit the maximum amount of memory. If so the worker
- # is restarted already, thus periodically restarting workers shouldn't be
- # needed.
- config.rolling_restart_frequency = false
+ # Ideally we'll never hit the maximum amount of memory. Restart the workers
+ # regularly rather than rely on OOM behavior for periodic restarting.
+ config.rolling_restart_frequency = 43200 # 12 hours in seconds.
observer = Gitlab::Cluster::PumaWorkerKillerObserver.new
config.pre_term = observer.callback
diff --git a/lib/quality/test_level.rb b/lib/quality/test_level.rb
index a65657dadd0..b7822adf6ed 100644
--- a/lib/quality/test_level.rb
+++ b/lib/quality/test_level.rb
@@ -53,11 +53,11 @@ module Quality
end
def pattern(level)
- @patterns[level] ||= "#{prefix}spec/{#{TEST_LEVEL_FOLDERS.fetch(level).join(',')}}{,/**/}*_spec.rb"
+ @patterns[level] ||= "#{prefix}spec/#{folders_pattern(level)}{,/**/}*_spec.rb"
end
def regexp(level)
- @regexps[level] ||= Regexp.new("#{prefix}spec/(#{TEST_LEVEL_FOLDERS.fetch(level).join('|')})").freeze
+ @regexps[level] ||= Regexp.new("#{prefix}spec/#{folders_regex(level)}").freeze
end
def level_for(file_path)
@@ -72,5 +72,27 @@ module Quality
raise UnknownTestLevelError, "Test level for #{file_path} couldn't be set. Please rename the file properly or change the test level detection regexes in #{__FILE__}."
end
end
+
+ private
+
+ def folders_pattern(level)
+ case level
+ # Geo specs aren't in a specific folder, but they all have the :geo tag, so we must search for them globally
+ when :all, :geo
+ '**'
+ else
+ "{#{TEST_LEVEL_FOLDERS.fetch(level).join(',')}}"
+ end
+ end
+
+ def folders_regex(level)
+ case level
+ # Geo specs aren't in a specific folder, but they all have the :geo tag, so we must search for them globally
+ when :all, :geo
+ ''
+ else
+ "(#{TEST_LEVEL_FOLDERS.fetch(level).join('|')})"
+ end
+ end
end
end
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
new file mode 100644
index 00000000000..77b6a8df7c9
--- /dev/null
+++ b/scripts/rspec_helpers.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+
+function retrieve_tests_metadata() {
+ mkdir -p knapsack/ rspec_flaky/ rspec_profiling/
+
+ if [[ ! -f "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" ]]; then
+ wget -O "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
+ fi
+
+ if [[ ! -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" ]]; then
+ wget -O "${FLAKY_RSPEC_SUITE_REPORT_PATH}" "http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo "{}" > "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+ fi
+}
+
+function update_tests_metadata() {
+ echo "{}" > "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
+
+ scripts/merge-reports "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "knapsack/rspec*_pg9_*.json"
+ if [[ -n "${TESTS_METADATA_S3_BUCKET}" ]]; then
+ scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}"
+ fi
+
+ rm -f "knapsack/rspec*.json"
+
+ scripts/merge-reports "${FLAKY_RSPEC_SUITE_REPORT_PATH}" "rspec_flaky/all_*.json"
+
+ export FLAKY_RSPEC_GENERATE_REPORT="1"
+ scripts/prune-old-flaky-specs "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+
+ if [[ -n ${TESTS_METADATA_S3_BUCKET} ]]; then
+ scripts/sync-reports put "${TESTS_METADATA_S3_BUCKET}" "${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+ fi
+
+ rm -f "rspec_flaky/all_*.json" "rspec_flaky/new_*.json"
+
+ scripts/insert-rspec-profiling-data
+}
+
+function rspec_simple_job() {
+ local rspec_opts="${1}"
+
+ export NO_KNAPSACK="1"
+ export CACHE_CLASSES="true"
+
+ scripts/gitaly-test-spawn
+
+ bin/rspec --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml "${rspec_opts}"
+}
+
+function rspec_paralellized_job() {
+ read -ra job_name <<< "$CI_JOB_NAME"
+ local test_tool="${job_name[0]}"
+ local test_level="${job_name[1]}"
+ local database="${job_name[2]}"
+ local rspec_opts="${1}"
+ local spec_folder_prefix=""
+
+ if [[ "${test_tool}" =~ "-ee" ]]; then
+ spec_folder_prefix="ee/"
+ fi
+
+ export CACHE_CLASSES="true"
+ export KNAPSACK_LOG_LEVEL="debug"
+ export KNAPSACK_REPORT_PATH="knapsack/${test_tool}_${test_level}_${database}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"
+
+ if [[ -z "${KNAPSACK_TEST_FILE_PATTERN}" ]]; then
+ pattern=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new(%(${spec_folder_prefix})).pattern(:${test_level})")
+ export KNAPSACK_TEST_FILE_PATTERN="${pattern}"
+ fi
+
+ echo "KNAPSACK_TEST_FILE_PATTERN: ${KNAPSACK_TEST_FILE_PATTERN}"
+
+ if [[ -d "ee/" ]]; then
+ export KNAPSACK_GENERATE_REPORT="true"
+ export FLAKY_RSPEC_GENERATE_REPORT="true"
+ export SUITE_FLAKY_RSPEC_REPORT_PATH="${FLAKY_RSPEC_SUITE_REPORT_PATH}"
+ export FLAKY_RSPEC_REPORT_PATH="rspec_flaky/all_${test_tool}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"
+ export NEW_FLAKY_RSPEC_REPORT_PATH="rspec_flaky/new_${test_tool}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json"
+
+ cp "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}" "${KNAPSACK_REPORT_PATH}"
+
+ if [[ ! -f $FLAKY_RSPEC_REPORT_PATH ]]; then
+ echo "{}" > "${FLAKY_RSPEC_REPORT_PATH}"
+ fi
+
+ if [[ ! -f $NEW_FLAKY_RSPEC_REPORT_PATH ]]; then
+ echo "{}" > "${NEW_FLAKY_RSPEC_REPORT_PATH}"
+ fi
+ fi
+
+ scripts/gitaly-test-spawn
+
+ mkdir -p tmp/memory_test
+
+ export MEMORY_TEST_PATH="tmp/memory_test/${test_tool}_${test_level}_${database}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_memory.csv"
+
+ knapsack rspec "-Ispec --color --format documentation --format RspecJunitFormatter --out junit_rspec.xml ${rspec_opts}"
+
+ date
+}
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index 8f662c71c7a..5dee11b3810 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import Dropzone from 'dropzone';
import Mousetrap from 'mousetrap';
import ZenMode from '~/zen_mode';
+import initNotes from '~/init_notes';
describe('ZenMode', () => {
let zen;
@@ -28,6 +29,7 @@ describe('ZenMode', () => {
beforeEach(() => {
loadFixtures(fixtureName);
+ initNotes();
dropzoneForElementSpy = spyOn(Dropzone, 'forElement').and.callFake(() => ({
enable: () => true,
diff --git a/spec/lib/quality/test_level_spec.rb b/spec/lib/quality/test_level_spec.rb
index 59870ce44a7..4db188bd8f2 100644
--- a/spec/lib/quality/test_level_spec.rb
+++ b/spec/lib/quality/test_level_spec.rb
@@ -4,6 +4,20 @@ require 'fast_spec_helper'
RSpec.describe Quality::TestLevel do
describe '#pattern' do
+ context 'when level is all' do
+ it 'returns a pattern' do
+ expect(subject.pattern(:all))
+ .to eq("spec/**{,/**/}*_spec.rb")
+ end
+ end
+
+ context 'when level is geo' do
+ it 'returns a pattern' do
+ expect(subject.pattern(:geo))
+ .to eq("spec/**{,/**/}*_spec.rb")
+ end
+ end
+
context 'when level is unit' do
it 'returns a pattern' do
expect(subject.pattern(:unit))
@@ -44,6 +58,20 @@ RSpec.describe Quality::TestLevel do
end
describe '#regexp' do
+ context 'when level is all' do
+ it 'returns a regexp' do
+ expect(subject.regexp(:all))
+ .to eq(%r{spec/})
+ end
+ end
+
+ context 'when level is geo' do
+ it 'returns a regexp' do
+ expect(subject.regexp(:geo))
+ .to eq(%r{spec/})
+ end
+ end
+
context 'when level is unit' do
it 'returns a regexp' do
expect(subject.regexp(:unit))
diff --git a/spec/mailers/emails/pipelines_spec.rb b/spec/mailers/emails/pipelines_spec.rb
new file mode 100644
index 00000000000..8d4afe9f00f
--- /dev/null
+++ b/spec/mailers/emails/pipelines_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'email_spec'
+
+describe Emails::Pipelines do
+ include EmailSpec::Matchers
+
+ set(:project) { create(:project, :repository) }
+
+ shared_examples_for 'correct pipeline information' do
+ it 'has a correct information' do
+ expect(subject)
+ .to have_subject "#{project.name} | Pipeline ##{pipeline.id} has " \
+ "#{status} for #{pipeline.source_ref} | " \
+ "#{pipeline.short_sha}".to_s
+
+ expect(subject).to have_body_text pipeline.source_ref
+ expect(subject).to have_body_text status_text
+ end
+
+ context 'when pipeline for merge requests' do
+ let(:pipeline) { merge_request.all_pipelines.first }
+
+ let(:merge_request) do
+ create(:merge_request, :with_detached_merge_request_pipeline,
+ source_project: project,
+ target_project: project)
+ end
+
+ it 'has a correct information with merge request link' do
+ expect(subject)
+ .to have_subject "#{project.name} | Pipeline ##{pipeline.id} has " \
+ "#{status} for #{pipeline.source_ref} | " \
+ "#{pipeline.short_sha} in !#{merge_request.iid}".to_s
+
+ expect(subject).to have_body_text merge_request.to_reference
+ expect(subject).to have_body_text pipeline.source_ref
+ expect(subject).not_to have_body_text pipeline.ref
+ end
+ end
+ end
+
+ describe '#pipeline_success_email' do
+ subject { Notify.pipeline_success_email(pipeline, pipeline.user.try(:email)) }
+
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: ref, sha: sha) }
+ let(:ref) { 'master' }
+ let(:sha) { project.commit(ref).sha }
+
+ it_behaves_like 'correct pipeline information' do
+ let(:status) { 'succeeded' }
+ let(:status_text) { 'Your pipeline has passed.' }
+ end
+ end
+
+ describe '#pipeline_failed_email' do
+ subject { Notify.pipeline_failed_email(pipeline, pipeline.user.try(:email)) }
+
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: ref, sha: sha) }
+ let(:ref) { 'master' }
+ let(:sha) { project.commit(ref).sha }
+
+ it_behaves_like 'correct pipeline information' do
+ let(:status) { 'failed' }
+ let(:status_text) { 'Your pipeline has failed.' }
+ end
+ end
+end
diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb
index 3ac394b57c5..f7994b55efa 100644
--- a/spec/requests/api/group_labels_spec.rb
+++ b/spec/requests/api/group_labels_spec.rb
@@ -65,6 +65,17 @@ describe API::GroupLabels do
end
end
+ describe 'GET :id/labels/:label_id' do
+ it 'returns a single label for the group' do
+ get api("/groups/#{group.id}/labels/#{group_label1.name}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['name']).to eq(group_label1.name)
+ expect(json_response['color']).to eq(group_label1.color)
+ expect(json_response['description']).to eq(group_label1.description)
+ end
+ end
+
describe 'POST /groups/:id/labels' do
it 'returns created label when all params are given' do
post api("/groups/#{group.id}/labels", user),
@@ -117,7 +128,7 @@ describe API::GroupLabels do
end
end
- describe 'DELETE /groups/:id/labels' do
+ describe 'DELETE /groups/:id/labels (deprecated)' do
it 'returns 204 for existing label' do
delete api("/groups/#{group.id}/labels", user), params: { name: group_label1.name }
@@ -154,7 +165,37 @@ describe API::GroupLabels do
end
end
- describe 'PUT /groups/:id/labels' do
+ describe 'DELETE /groups/:id/labels/:label_id' do
+ it 'returns 204 for existing label' do
+ delete api("/groups/#{group.id}/labels/#{group_label1.name}", user)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
+
+ it 'returns 404 for non existing label' do
+ delete api("/groups/#{group.id}/labels/not_exists", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 Label Not Found')
+ end
+
+ it "does not delete parent's group labels" do
+ subgroup = create(:group, parent: group)
+ subgroup_label = create(:group_label, title: 'feature', group: subgroup)
+
+ delete api("/groups/#{subgroup.id}/labels/#{subgroup_label.name}", user)
+
+ expect(response).to have_gitlab_http_status(204)
+ expect(subgroup.labels.size).to eq(0)
+ expect(group.labels).to include(group_label1)
+ end
+
+ it_behaves_like '412 response' do
+ let(:request) { api("/groups/#{group.id}/labels/#{group_label1.name}", user) }
+ end
+ end
+
+ describe 'PUT /groups/:id/labels (deprecated)' do
it 'returns 200 if name and colors and description are changed' do
put api("/groups/#{group.id}/labels", user),
params: {
@@ -199,7 +240,7 @@ describe API::GroupLabels do
put api("/groups/#{group.id}/labels", user), params: { new_name: group_label1.name }
expect(response).to have_gitlab_http_status(400)
- expect(json_response['error']).to eq('name is missing')
+ expect(json_response['error']).to eq('label_id, name are missing, exactly one parameter must be provided')
end
it 'returns 400 if no new parameters given' do
@@ -211,6 +252,53 @@ describe API::GroupLabels do
end
end
+ describe 'PUT /groups/:id/labels/:label_id' do
+ it 'returns 200 if name and colors and description are changed' do
+ put api("/groups/#{group.id}/labels/#{group_label1.name}", user),
+ params: {
+ new_name: 'New Label',
+ color: '#FFFFFF',
+ description: 'test'
+ }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['name']).to eq('New Label')
+ expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['description']).to eq('test')
+ end
+
+ it "does not update parent's group label" do
+ subgroup = create(:group, parent: group)
+ subgroup_label = create(:group_label, title: 'feature', group: subgroup)
+
+ put api("/groups/#{subgroup.id}/labels/#{subgroup_label.name}", user),
+ params: {
+ new_name: 'New Label'
+ }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(subgroup.labels[0].name).to eq('New Label')
+ expect(group_label1.name).to eq('feature')
+ end
+
+ it 'returns 404 if label does not exist' do
+ put api("/groups/#{group.id}/labels/not_exists", user),
+ params: {
+ new_name: 'label3'
+ }
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns 400 if no new parameters given' do
+ put api("/groups/#{group.id}/labels/#{group_label1.name}", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('new_name, color, description are missing, '\
+ 'at least one parameter must be provided')
+ end
+ end
+
describe 'POST /groups/:id/labels/:label_id/subscribe' do
context 'when label_id is a label title' do
it 'subscribes to the label' do
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 4f8233a9110..7089da3d351 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -1,139 +1,122 @@
require 'spec_helper'
describe API::Labels do
+ def put_labels_api(route_type, user, spec_params, request_params = {})
+ if route_type == :deprecated
+ put api("/projects/#{project.id}/labels", user),
+ params: request_params.merge(spec_params)
+ else
+ label_id = spec_params[:name] || spec_params[:label_id]
+
+ put api("/projects/#{project.id}/labels/#{label_id}", user),
+ params: request_params.merge(spec_params.except(:name, :id))
+ end
+ end
+
let(:user) { create(:user) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let!(:label1) { create(:label, title: 'label1', project: project) }
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
- shared_examples 'label update API' do
- it 'returns 200 if name is changed' do
- request_params = {
- new_name: 'New Label'
- }.merge(spec_params)
-
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ route_types = [:deprecated, :rest]
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['name']).to eq('New Label')
- expect(json_response['color']).to eq(label1.color)
- end
-
- it 'returns 200 if colors is changed' do
- request_params = {
- color: '#FFFFFF'
- }.merge(spec_params)
+ shared_examples 'label update API' do
+ route_types.each do |route_type|
+ it "returns 200 if name is changed (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, new_name: 'New Label')
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['name']).to eq('New Label')
+ expect(json_response['color']).to eq(label1.color)
+ end
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['name']).to eq(label1.name)
- expect(json_response['color']).to eq('#FFFFFF')
- end
+ it "returns 200 if colors is changed (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, color: '#FFFFFF')
- it 'returns 200 if a priority is added' do
- request_params = {
- priority: 3
- }.merge(spec_params)
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['name']).to eq(label1.name)
+ expect(json_response['color']).to eq('#FFFFFF')
+ end
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ it "returns 200 if a priority is added (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, priority: 3)
- expect(response.status).to eq(200)
- expect(json_response['name']).to eq(label1.name)
- expect(json_response['priority']).to eq(3)
- end
-
- it 'returns 400 if no new parameters given' do
- put api("/projects/#{project.id}/labels", user), params: spec_params
+ expect(response.status).to eq(200)
+ expect(json_response['name']).to eq(label1.name)
+ expect(json_response['priority']).to eq(3)
+ end
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['error']).to eq('new_name, color, description, priority are missing, '\
- 'at least one parameter must be provided')
- end
+ it "returns 400 if no new parameters given (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params)
- it 'returns 400 when color code is too short' do
- request_params = {
- color: '#FF'
- }.merge(spec_params)
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('new_name, color, description, priority are missing, '\
+ 'at least one parameter must be provided')
+ end
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ it "returns 400 when color code is too short (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, color: '#FF')
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['message']['color']).to eq(['must be a valid color code'])
- end
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']['color']).to eq(['must be a valid color code'])
+ end
- it 'returns 400 for too long color code' do
- request_params = {
- color: '#FFAAFFFF'
- }.merge(spec_params)
+ it "returns 400 for too long color code (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, color: '#FFAAFFFF')
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']['color']).to eq(['must be a valid color code'])
+ end
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['message']['color']).to eq(['must be a valid color code'])
- end
+ it "returns 400 for invalid priority (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, priority: 'foo')
- it 'returns 400 for invalid priority' do
- request_params = {
- priority: 'foo'
- }.merge(spec_params)
+ expect(response).to have_gitlab_http_status(400)
+ end
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ it "returns 200 if name and colors and description are changed (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, new_name: 'New Label', color: '#FFFFFF', description: 'test')
- expect(response).to have_gitlab_http_status(400)
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['name']).to eq('New Label')
+ expect(json_response['color']).to eq('#FFFFFF')
+ expect(json_response['description']).to eq('test')
+ end
- it 'returns 200 if name and colors and description are changed' do
- request_params = {
- new_name: 'New Label',
- color: '#FFFFFF',
- description: 'test'
- }.merge(spec_params)
+ it "returns 400 for invalid name (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, new_name: ',', color: '#FFFFFF')
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message']['title']).to eq(['is invalid'])
+ end
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['name']).to eq('New Label')
- expect(json_response['color']).to eq('#FFFFFF')
- expect(json_response['description']).to eq('test')
- end
+ it "returns 200 if description is changed (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, description: 'test')
- it 'returns 400 for invalid name' do
- request_params = {
- new_name: ',',
- color: '#FFFFFF'
- }.merge(spec_params)
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(expected_response_label_id)
+ expect(json_response['description']).to eq('test')
+ end
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ it "returns 200 if priority is changed (#{route_type} route)" do
+ put_labels_api(route_type, user, spec_params, priority: 10)
- expect(response).to have_gitlab_http_status(400)
- expect(json_response['message']['title']).to eq(['is invalid'])
+ expect(response.status).to eq(200)
+ expect(json_response['id']).to eq(expected_response_label_id)
+ expect(json_response['priority']).to eq(10)
+ end
end
- it 'returns 200 if description is changed' do
- request_params = {
- description: 'test'
- }.merge(spec_params)
+ it 'returns 200 if a priority is removed (deprecated route)' do
+ label = find_by_spec_params(spec_params)
- put api("/projects/#{project.id}/labels", user),
- params: request_params
+ expect(label).not_to be_nil
- expect(response).to have_gitlab_http_status(200)
- expect(json_response['id']).to eq(expected_response_label_id)
- expect(json_response['description']).to eq('test')
- end
+ label.priorities.create(project: label.project, priority: 1)
+ label.save!
- it 'returns 200 if priority is changed' do
request_params = {
- priority: 10
+ priority: nil
}.merge(spec_params)
put api("/projects/#{project.id}/labels", user),
@@ -141,21 +124,22 @@ describe API::Labels do
expect(response.status).to eq(200)
expect(json_response['id']).to eq(expected_response_label_id)
- expect(json_response['priority']).to eq(10)
+ expect(json_response['priority']).to be_nil
end
- it 'returns 200 if a priority is removed' do
+ it 'returns 200 if a priority is removed (rest route)' do
label = find_by_spec_params(spec_params)
expect(label).not_to be_nil
+ label_id = spec_params[:name] || spec_params[:label_id]
label.priorities.create(project: label.project, priority: 1)
label.save!
request_params = {
priority: nil
- }.merge(spec_params)
+ }.merge(spec_params.except(:name, :id))
- put api("/projects/#{project.id}/labels", user),
+ put api("/projects/#{project.id}/labels/#{label_id}", user),
params: request_params
expect(response.status).to eq(200)
@@ -173,11 +157,18 @@ describe API::Labels do
end
shared_examples 'label delete API' do
- it 'returns 204 for existing label' do
+ it 'returns 204 for existing label (deprecated route)' do
delete api("/projects/#{project.id}/labels", user), params: spec_params
expect(response).to have_gitlab_http_status(204)
end
+
+ it 'returns 204 for existing label (rest route)' do
+ label_id = spec_params[:name] || spec_params[:label_id]
+ delete api("/projects/#{project.id}/labels/#{label_id}", user), params: spec_params.except(:name, :label_id)
+
+ expect(response).to have_gitlab_http_status(204)
+ end
end
before do
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index 7ec65318ec5..fd5ad9451f7 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -69,7 +69,7 @@ module JavaScriptFixturesHelpers
link_tags = doc.css('link')
link_tags.remove
- scripts = doc.css("script:not([type='text/template']):not([type='text/x-template'])")
+ scripts = doc.css("script:not([type='text/template']):not([type='text/x-template']):not([type='application/json'])")
scripts.remove
fixture = doc.to_html
diff --git a/spec/support/shared_examples/ci/pipeline_email_examples.rb b/spec/support/shared_examples/ci/pipeline_email_examples.rb
new file mode 100644
index 00000000000..f72d8af3c65
--- /dev/null
+++ b/spec/support/shared_examples/ci/pipeline_email_examples.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+shared_examples_for 'correct pipeline information for pipelines for merge requests' do
+ context 'when pipeline for merge request' do
+ let(:pipeline) { merge_request.all_pipelines.first }
+
+ let(:merge_request) do
+ create(:merge_request, :with_detached_merge_request_pipeline,
+ source_project: project,
+ target_project: project)
+ end
+
+ it 'renders a source ref of the pipeline' do
+ render
+
+ expect(rendered).to have_content pipeline.source_ref
+ expect(rendered).not_to have_content pipeline.ref
+ end
+ end
+end
diff --git a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
index bf633a118ca..28f8203fd8f 100644
--- a/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_failed_email.html.haml_spec.rb
@@ -33,6 +33,8 @@ describe 'notify/pipeline_failed_email.html.haml' do
expect(rendered).to have_content "##{pipeline.id}"
expect(rendered).to have_content pipeline.user.name
end
+
+ it_behaves_like 'correct pipeline information for pipelines for merge requests'
end
context 'pipeline without user' do
diff --git a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
index 060274eb56a..d15969acf83 100644
--- a/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
+++ b/spec/views/notify/pipeline_failed_email.text.erb_spec.rb
@@ -36,4 +36,6 @@ describe 'notify/pipeline_failed_email.text.erb' do
expect(rendered).to have_content(pipeline.user.name)
expect(rendered).to have_content("/-/jobs/#{job.id}/raw")
end
+
+ it_behaves_like 'correct pipeline information for pipelines for merge requests'
end
diff --git a/spec/views/notify/pipeline_success_email.html.haml_spec.rb b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
index 46a6c908863..79beca79724 100644
--- a/spec/views/notify/pipeline_success_email.html.haml_spec.rb
+++ b/spec/views/notify/pipeline_success_email.html.haml_spec.rb
@@ -33,6 +33,8 @@ describe 'notify/pipeline_success_email.html.haml' do
expect(rendered).to have_content "##{pipeline.id}"
expect(rendered).to have_content pipeline.user.name
end
+
+ it_behaves_like 'correct pipeline information for pipelines for merge requests'
end
context 'pipeline without user' do
diff --git a/spec/views/notify/pipeline_success_email.text.erb_spec.rb b/spec/views/notify/pipeline_success_email.text.erb_spec.rb
new file mode 100644
index 00000000000..ba4633bc346
--- /dev/null
+++ b/spec/views/notify/pipeline_success_email.text.erb_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'notify/pipeline_success_email.text.erb' do
+ let(:user) { create(:user, developer_projects: [project]) }
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, :simple, source_project: project) }
+
+ let(:pipeline) do
+ create(:ci_pipeline,
+ :success,
+ project: project,
+ user: user,
+ ref: project.default_branch,
+ sha: project.commit.sha)
+ end
+
+ before do
+ assign(:project, project)
+ assign(:pipeline, pipeline)
+ assign(:merge_request, merge_request)
+ end
+
+ it_behaves_like 'correct pipeline information for pipelines for merge requests'
+end