diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-27 15:06:16 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-27 15:06:16 +0000 |
commit | 8320f7956d72986f5a7c850874fce4f8b5a8e015 (patch) | |
tree | c761b309cfff422609d47a17ac4d6a732c142f49 | |
parent | 45482d5a2704da7fabe4ccf07f85d9be6e0a791a (diff) | |
download | gitlab-ce-8320f7956d72986f5a7c850874fce4f8b5a8e015.tar.gz |
Add latest changes from gitlab-org/gitlab@master
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 |