From 915709a4ab352cca1a06d213c865219b304d5cdc Mon Sep 17 00:00:00 2001 From: Landon Abney Date: Fri, 6 Jul 2018 20:32:37 +0000 Subject: [PATCH 01/31] Remove duplicate nip.io link The nip.io service was being linked to twice in the note on cert requests. --- doc/install/kubernetes/gitlab_omnibus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md index 9aee6b9dc74..c2c8a7a92fd 100644 --- a/doc/install/kubernetes/gitlab_omnibus.md +++ b/doc/install/kubernetes/gitlab_omnibus.md @@ -120,7 +120,7 @@ gitlabConfigStorageSize: 1Gi Ingress routing and SSL are automatically configured within this Chart. An NGINX ingress is provisioned and configured, and will route traffic to any service. SSL certificates are automatically created and configured by [kube-lego](https://github.com/kubernetes/charts/tree/master/stable/kube-lego). > **Note:** -Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [nip.io](http://nip.io) and [nip.io](http://nip.io) are unlikely to work. +Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [nip.io](http://nip.io) are unlikely to work. ## Installing GitLab using the Helm Chart > **Note:** From 93c60f611c5ad095d8e4e6ee771bcbbccb084f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Soko=C5=82owski?= Date: Fri, 10 Aug 2018 14:43:10 +0000 Subject: [PATCH 02/31] #47845 - Propagate failure_reason to job webhook. --- lib/gitlab/data_builder/build.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gitlab/data_builder/build.rb b/lib/gitlab/data_builder/build.rb index 2f1445a050a..0b71b31a476 100644 --- a/lib/gitlab/data_builder/build.rb +++ b/lib/gitlab/data_builder/build.rb @@ -28,6 +28,7 @@ module Gitlab build_finished_at: build.finished_at, build_duration: build.duration, build_allow_failure: build.allow_failure, + build_failure_reason: build.failure_reason, # TODO: do we still need it? project_id: project.id, From 55218c4e764f0cde0277f29e940697eb47d8866e Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Sun, 12 Aug 2018 06:58:30 +0000 Subject: [PATCH 03/31] format doc/ reference --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6e1cc9a432..96d8f709274 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ GitLab Community Edition (CE) is available freely under the MIT Expat license. All third party components incorporated into the GitLab Software are licensed under the original license provided by the owner of the applicable component. -All Documentation content that resides under the doc/ directory of this repository is licensed under Creative Commons: CC BY-SA 4.0. +All Documentation content that resides under the `doc/` directory of this repository is licensed under Creative Commons: CC BY-SA 4.0. ## Install a development environment From 8be9c0bfadb16caf53e67b5b604b37571694c241 Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Sun, 12 Aug 2018 07:42:35 +0000 Subject: [PATCH 04/31] fix hashed storage readiness link --- doc/administration/repository_storage_types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md index 88221db78f1..bd758c49eba 100644 --- a/doc/administration/repository_storage_types.md +++ b/doc/administration/repository_storage_types.md @@ -42,7 +42,7 @@ Registry, etc. ## Hashed Storage > **Warning:** Hashed storage is in **Beta**. For the latest updates, check the -> associated [issue](https://gitlab.com/gitlab-com/infrastructure/issues/2821) +> associated [issue](https://gitlab.com/gitlab-com/infrastructure/issues/3542) > and please report any problems you encounter. Hashed Storage is the new storage behavior we are rolling out with 10.0. Instead From 446af7238bea84d3282dd0a53e0b274739957b83 Mon Sep 17 00:00:00 2001 From: Thong Kuah Date: Wed, 15 Aug 2018 17:18:56 +1200 Subject: [PATCH 05/31] Convert the #install_command spec to promethus#install_command as the subject This makes this consistent with the specs for other applications as well as fixing a bug in the 'application failed to install previously' spec --- .../clusters/applications/prometheus_spec.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index 7454be3ab2f..26b75c75e1d 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -154,20 +154,17 @@ describe Clusters::Applications::Prometheus do end describe '#install_command' do - let(:kubeclient) { double('kubernetes client') } let(:prometheus) { create(:clusters_applications_prometheus) } - it 'returns an instance of Gitlab::Kubernetes::Helm::InstallCommand' do - expect(prometheus.install_command).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) - end + subject { prometheus.install_command } + + it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) } it 'should be initialized with 3 arguments' do - command = prometheus.install_command - - expect(command.name).to eq('prometheus') - expect(command.chart).to eq('stable/prometheus') - expect(command.version).to eq('6.7.3') - expect(command.files).to eq(prometheus.files) + expect(subject.name).to eq('prometheus') + expect(subject.chart).to eq('stable/prometheus') + expect(subject.version).to eq('6.7.3') + expect(subject.files).to eq(prometheus.files) end context 'application failed to install previously' do From b7fa2e0beb51ba2f489c67fbcabaff4169816305 Mon Sep 17 00:00:00 2001 From: Steve Azzopardi Date: Thu, 19 Jul 2018 16:49:32 +0200 Subject: [PATCH 06/31] Add documentation for interactive web terminals Create a new page for the interactive web terminals since it doesn't fit in any other place. Add link in CI home page. --- doc/ci/README.md | 2 + .../img/finished_job_with_terminal_open.png | Bin 0 -> 40247 bytes .../img/interactive_web_terminal_page.png | Bin 0 -> 23431 bytes .../interactive_web_terminal_running_job.png | Bin 0 -> 26692 bytes doc/ci/interactive_web_terminal/index.md | 48 ++++++++++++++++++ 5 files changed, 50 insertions(+) create mode 100644 doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png create mode 100644 doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png create mode 100644 doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png create mode 100644 doc/ci/interactive_web_terminal/index.md diff --git a/doc/ci/README.md b/doc/ci/README.md index 7666219acb0..d782d64e971 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -76,6 +76,8 @@ learn how to leverage its potential even more. - [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md) - [Kubernetes clusters](../user/project/clusters/index.md) - Integrate one or more Kubernetes clusters to your project +- [Interactive web terminal](interactive_web_terminal/index.md) - Open an interactive + web terminal to debug the running jobs ## GitLab CI/CD for Docker diff --git a/doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png b/doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png new file mode 100644 index 0000000000000000000000000000000000000000..80be16301e8cfa2eca548b87c794858835811839 GIT binary patch literal 40247 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%0D)mI#>cGwQ&`}%{W0N&1kk(uMW{C)6lPx&DB;S% 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 EUjT|z0ssI2 literal 0 HcmV?d00001 diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_page.png new file mode 100644 index 0000000000000000000000000000000000000000..b59c1b6bc43d28716c51be78156b56efdf494783 GIT binary patch literal 23431 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
ZrQr$7RyPfTz0N2V2uk4K@y2sDahu0 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 Lzyd_jyBq!=5MM-Q literal 0 HcmV?d00001 diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png new file mode 100644 index 0000000000000000000000000000000000000000..cc60ecdfdc9d73ec4b370568e10b8a8d0ba08b8e GIT binary patch literal 26692 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 literal 0 HcmV?d00001 diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md new file mode 100644 index 00000000000..2b4048599da --- /dev/null +++ b/doc/ci/interactive_web_terminal/index.md @@ -0,0 +1,48 @@ +# Getting started with interactive web terminals + +> Introduced in GitLab 11.2. + +Interactive web terminals give the user access to a terminal in GitLab for +running one-of commands for their CI pipeline. + +NOTE: **Note:** +This is not available for the shared Runners on GitLab.com. +To make use of this feature, you need to provide your +[own Runner](https://docs.gitlab.com/runner/install/) and properly +[configure it](#configuration). + +## Configuration + +Two things need to be configured for the interactive web terminal to work: + +- The Runner needs to have [`[session_server]` configured + properly][session-server] +- Web terminals need to be + [enabled](../../administration/integration/terminal.md#enabling-and-disabling-terminal-support) + +## Debugging a running job + +NOTE: **Note:** Not all executors are +[supported](https://docs.gitlab.com/runner/executors/#compatibility-chart). + +Sometimes, when a job is running, things don't go as you would expect, and it +would be helpful if one can have a shell to aid debugging. When a job is +running, on the right panel you can see a button that will open the terminal +for the current job. + +![Example of job running with terminal +available](img/interactive_web_terminal_running_job.png) + +When clicked, you will be redirected to a new page where you can access the +terminal and type commands like a normal shell. + +![terminal of the job](img/interactive_web_terminal_page.png) + +If you have the terminal open and the job has finished with its tasks, the +terminal will block the job from finishing for the duration configured in +[`[session_server].terminal_max_retention_time`][session-server] until you +close the terminal window. + +![finished job with terminal open](img/finished_job_with_terminal_open.png) + +[session-server]: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section From e321eb6093c79e8d607b6e1cb22065cb9a48e1ff Mon Sep 17 00:00:00 2001 From: Steve Azzopardi Date: Thu, 16 Aug 2018 14:10:25 +0200 Subject: [PATCH 07/31] Update interactive web terminal documentation --- .../img/finished_job_with_terminal_open.png | Bin 40247 -> 35571 bytes .../interactive_web_terminal_running_job.png | Bin 26692 -> 55682 bytes doc/ci/interactive_web_terminal/index.md | 8 ++++---- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png b/doc/ci/interactive_web_terminal/img/finished_job_with_terminal_open.png index 80be16301e8cfa2eca548b87c794858835811839..199268a1486c365567c068d4a3a51575d73a9925 100644 GIT binary patch literal 35571 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 literal 40247 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%0D)mI#>cGwQ&`}%{W0N&1kk(uMW{C)6lPx&DB;S% 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 EUjT|z0ssI2 diff --git a/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png b/doc/ci/interactive_web_terminal/img/interactive_web_terminal_running_job.png index cc60ecdfdc9d73ec4b370568e10b8a8d0ba08b8e..f92c6df07a18fa9ec494fa9574ace3633dc5272e 100644 GIT binary patch literal 55682 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 diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md index 2b4048599da..55f43e0765b 100644 --- a/doc/ci/interactive_web_terminal/index.md +++ b/doc/ci/interactive_web_terminal/index.md @@ -1,6 +1,6 @@ # Getting started with interactive web terminals -> Introduced in GitLab 11.2. +> Introduced in GitLab 11.3. Interactive web terminals give the user access to a terminal in GitLab for running one-of commands for their CI pipeline. @@ -27,14 +27,14 @@ NOTE: **Note:** Not all executors are Sometimes, when a job is running, things don't go as you would expect, and it would be helpful if one can have a shell to aid debugging. When a job is -running, on the right panel you can see a button that will open the terminal +running, on the right panel you can see a button `debug` that will open the terminal for the current job. ![Example of job running with terminal available](img/interactive_web_terminal_running_job.png) -When clicked, you will be redirected to a new page where you can access the -terminal and type commands like a normal shell. +When clicked, a new tab will open to the terminal page where you can access +the terminal and type commands like a normal shell. ![terminal of the job](img/interactive_web_terminal_page.png) From 8aed9f08fc681d5653c2bc4b688950caf525579b Mon Sep 17 00:00:00 2001 From: Warren Parad Date: Sun, 15 Jul 2018 15:00:48 +0200 Subject: [PATCH 08/31] Add authenticate to events api. fix #49255 --- .../unreleased/fix_event_api_permissions.yml | 5 ++ doc/api/events.md | 12 +++-- lib/api/events.rb | 5 ++ spec/requests/api/events_spec.rb | 48 +++++++++++++++++-- 4 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/fix_event_api_permissions.yml diff --git a/changelogs/unreleased/fix_event_api_permissions.yml b/changelogs/unreleased/fix_event_api_permissions.yml new file mode 100644 index 00000000000..dee280e93ad --- /dev/null +++ b/changelogs/unreleased/fix_event_api_permissions.yml @@ -0,0 +1,5 @@ +--- +title: 'Events API now requires the read_user or api scope.' +merge_request: 20627 +author: Warren Parad +type: fixed diff --git a/doc/api/events.md b/doc/api/events.md index f4d26c4de1c..fee0b38542a 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -48,9 +48,11 @@ GitLab removes events older than 1 year from the events table for performance re ## List currently authenticated user's events ->**Note:** This endpoint was introduced in GitLab 9.3. +>**Notes:** +> This endpoint was introduced in GitLab 9.3. +> `read_user` access was introduced in GitLab 11.3. -Get a list of events for the authenticated user. +Get a list of events for the authenticated user. Scope `read_user` or `api` is required. ``` GET /events @@ -119,9 +121,11 @@ Example response: ### Get user contribution events ->**Note:** Documentation was formerly located in the [Users API pages][users-api]. +>**Notes:** +> Documentation was formerly located in the [Users API pages][users-api]. +> `read_user` access was introduced in GitLab 11.3. -Get the contribution events for the specified user, sorted from newest to oldest. +Get the contribution events for the specified user, sorted from newest to oldest. Scope `read_user` or `api` is required. ``` GET /users/:id/events diff --git a/lib/api/events.rb b/lib/api/events.rb index fc4ba5a3188..a415508a632 100644 --- a/lib/api/events.rb +++ b/lib/api/events.rb @@ -1,6 +1,7 @@ module API class Events < Grape::API include PaginationParams + include APIGuard helpers do params :event_filter_params do @@ -24,6 +25,8 @@ module API end resource :events do + allow_access_with_scope :read_user, if: -> (request) { request.get? } + desc "List currently authenticated user's events" do detail 'This feature was introduced in GitLab 9.3.' success Entities::Event @@ -46,6 +49,8 @@ module API requires :id, type: String, desc: 'The ID or Username of the user' end resource :users do + allow_access_with_scope :read_user, if: -> (request) { request.get? } + desc 'Get the contribution events of a specified user' do detail 'This feature was introduced in GitLab 8.13.' success Entities::Event diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb index e6a61fdcf39..573eebe314d 100644 --- a/spec/requests/api/events_spec.rb +++ b/spec/requests/api/events_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' describe API::Events do include ApiHelpers + let(:user) { create(:user) } let(:non_member) { create(:user) } - let(:other_user) { create(:user, username: 'otheruser') } let(:private_project) { create(:project, :private, creator_id: user.id, namespace: user.namespace) } let(:closed_issue) { create(:closed_issue, project: private_project, author: user) } let!(:closed_issue_event) { create(:event, project: private_project, author: user, target: closed_issue, action: Event::CLOSED, created_at: Date.new(2016, 12, 30)) } @@ -28,12 +28,52 @@ describe API::Events do expect(json_response.size).to eq(1) end end + + context 'when the requesting token has "read_user" scope' do + let(:token) { create(:personal_access_token, scopes: ['read_user'], user: user) } + + it 'returns users events' do + get api('/events?action=closed&target_type=issue&after=2016-12-1&before=2016-12-31', personal_access_token: token) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.size).to eq(1) + end + end + + context 'when the requesting token does not have "read_user" or "api" scope' do + let(:token_without_scopes) { create(:personal_access_token, scopes: ['read_repository'], user: user) } + + it 'returns a "403" response' do + get api('/events', personal_access_token: token_without_scopes) + + expect(response).to have_gitlab_http_status(403) + end + end end describe 'GET /users/:id/events' do - context "as a user that cannot see the event's project" do - it 'returns no events' do - get api("/users/#{user.id}/events", other_user) + context "as a user that cannot see another user" do + it 'returns a "404" response' do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(non_member, :read_user, user).and_return(false) + + get api("/users/#{user.id}/events", non_member) + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to be_empty + end + end + + context "as a user token that cannot see another user" do + let(:non_member_token) { create(:personal_access_token, scopes: ['read_user'], user: non_member) } + + it 'returns a "404" response' do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(non_member, :read_user, user).and_return(false) + + get api("/users/#{user.id}/events", personal_access_token: non_member_token) expect(response).to have_gitlab_http_status(200) expect(json_response).to be_empty From 2efd5c0b63eb3b86354988cf0f47a0ca83e52d3e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 17 Aug 2018 00:11:00 +0800 Subject: [PATCH 09/31] Fill variable text with JS directly to speed up It's too slow to use `set(value)`, often timing out. Filling with JS is much faster for longer text, especially when the key size is larger than 8192. Before this patch: ``` Top 3 slowest examples (256.83 seconds, 89.4% of total time): cloning code using a deploy key user sets up a deploy key with QA::Runtime::Key::RSA(8192) to clone code using pipelines 161.26 seconds ./qa/specs/features/project/deploy_key_clone_spec.rb:42 cloning code using a deploy key user sets up a deploy key with QA::Runtime::Key::ECDSA(521) to clone code using pipelines 47.79 seconds ./qa/specs/features/project/deploy_key_clone_spec.rb:42 cloning code using a deploy key user sets up a deploy key with QA::Runtime::Key::ED25519() to clone code using pipelines 47.79 seconds ./qa/specs/features/project/deploy_key_clone_spec.rb:42 ``` Note that 161.26 was timed out. So it would actually take longer if it could ever complete. After patch: ``` Top 3 slowest examples (166.72 seconds, 83.8% of total time): cloning code using a deploy key user sets up a deploy key with QA::Runtime::Key::RSA(8192) to clone code using pipelines 83.66 seconds ./qa/specs/features/project/deploy_key_clone_spec.rb:42 cloning code using a deploy key user sets up a deploy key with QA::Runtime::Key::ECDSA(521) to clone code using pipelines 42.78 seconds ./qa/specs/features/project/deploy_key_clone_spec.rb:42 cloning code using a deploy key user sets up a deploy key with QA::Runtime::Key::ED25519() to clone code using pipelines 40.27 seconds ./qa/specs/features/project/deploy_key_clone_spec.rb:42 ``` Not that faster for smaller keys, but it's much faster for RSA 8192 (2 times faster). This was inspired from: https://github.com/teamcapybara/capybara/blob/679548cea10773d45e32808f4d964377cfe5e892/lib/capybara/selenium/node.rb#L217 Where it's clearing the field by filling an empty string. Here we do the same for the exact value we want to fill. --- qa/qa/page/project/settings/secret_variables.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qa/qa/page/project/settings/secret_variables.rb b/qa/qa/page/project/settings/secret_variables.rb index d2f5d5a9060..937ae6797c8 100644 --- a/qa/qa/page/project/settings/secret_variables.rb +++ b/qa/qa/page/project/settings/secret_variables.rb @@ -23,7 +23,13 @@ module QA # After we fill the key, JS would generate another field so # we need to use the same index to find the corresponding one. keys[index].set(key) - all_elements(:ci_variable_input_value)[index].set(value) + node = all_elements(:ci_variable_input_value)[index] + + # Simply run `node.set(value)` is too slow for long text here, + # so we need to run JavaScript directly to set the value. + # The code was inspired from: + # https://github.com/teamcapybara/capybara/blob/679548cea10773d45e32808f4d964377cfe5e892/lib/capybara/selenium/node.rb#L217 + execute_script("arguments[0].value = #{value.to_json}", node) end def save_variables From 2b48ec9011e9a3ce69aaddda2d6794d1622a399e Mon Sep 17 00:00:00 2001 From: Steve Azzopardi Date: Fri, 17 Aug 2018 08:14:25 +0200 Subject: [PATCH 10/31] Add beta flag for interactive web terminals --- doc/ci/interactive_web_terminal/index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md index 55f43e0765b..507aceb27fa 100644 --- a/doc/ci/interactive_web_terminal/index.md +++ b/doc/ci/interactive_web_terminal/index.md @@ -2,6 +2,10 @@ > Introduced in GitLab 11.3. +CAUTION: **Warning:** +Interactive web terminals are in beta, so they might not work properly and +lack features. For more information [follow issue #25990](https://gitlab.com/gitlab-org/gitlab-ce/issues/25990). + Interactive web terminals give the user access to a terminal in GitLab for running one-of commands for their CI pipeline. From 3741fcc3de77f1d3604927f0b14cdb9ddc334712 Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Fri, 17 Aug 2018 07:14:33 +0000 Subject: [PATCH 11/31] use nfs4 --- doc/administration/high_availability/gitlab.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md index 637d44d2823..b74554a5598 100644 --- a/doc/administration/high_availability/gitlab.md +++ b/doc/administration/high_availability/gitlab.md @@ -25,11 +25,11 @@ for each GitLab application server in your environment. options. Here is an example snippet to add to `/etc/fstab`: ``` - 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 - 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 + 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2 ``` 1. Create the shared directories. These may be different depending on your NFS From bc53c4f0b6e1fba6d72e9a47f2e8846697f56371 Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Fri, 17 Aug 2018 10:32:40 +0000 Subject: [PATCH 12/31] Update .gitlab-ci.yml --- .gitlab-ci.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd02d72b4c2..797a20ef16e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -739,7 +739,7 @@ karma: - chrome_debug.log - coverage-javascript/ -codequality: +code_quality: <<: *dedicated-no-docs-no-db-pull-cache-job image: docker:stable allow_failure: true @@ -757,9 +757,13 @@ codequality: script: # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - - docker run --env SOURCE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code + - docker run + --env SOURCE_CODE="$PWD" + --volume "$PWD":/code + --volume /var/run/docker.sock:/var/run/docker.sock + "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code artifacts: - paths: [codeclimate.json] + paths: [gl-code-quality-report.json] expire_in: 1 week sast: From 4b87d80499eb5c4fc89aee8cadf6c4ee4112dfc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Thu, 9 Aug 2018 23:18:49 -0400 Subject: [PATCH 13/31] Fix merge requests not showing any diff files for big patches --- GITALY_SERVER_VERSION | 2 +- Gemfile | 2 +- Gemfile.lock | 4 ++-- Gemfile.rails5.lock | 6 +++--- ...not-list-all-files-when-one-file-exceeds-size-limits.yml | 5 +++++ lib/gitlab/git/diff.rb | 1 + lib/gitlab/gitaly_client/diff.rb | 2 +- spec/lib/gitlab/gitaly_client/diff_spec.rb | 6 +++++- 8 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/49907-commits-and-merge-requests-does-not-list-all-files-when-one-file-exceeds-size-limits.yml diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 90bdef2ea8c..377d8aca07e 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.117.1 +0.117.2 diff --git a/Gemfile b/Gemfile index 5666e6cebc5..f01adaddd68 100644 --- a/Gemfile +++ b/Gemfile @@ -423,7 +423,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 0.112.0', require: 'gitaly' +gem 'gitaly-proto', '~> 0.113.0', require: 'gitaly' gem 'grpc', '~> 1.11.0' # Locked until https://github.com/google/protobuf/issues/4210 is closed diff --git a/Gemfile.lock b/Gemfile.lock index 1aadc3fd0b6..333e586949f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -284,7 +284,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.112.0) + gitaly-proto (0.113.0) google-protobuf (~> 3.1) grpc (~> 1.10) github-linguist (5.3.3) @@ -1045,7 +1045,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.112.0) + gitaly-proto (~> 0.113.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index af70e2c1939..c919f1a08ae 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -287,7 +287,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (0.112.0) + gitaly-proto (0.113.0) google-protobuf (~> 3.1) grpc (~> 1.10) github-linguist (5.3.3) @@ -1057,7 +1057,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 0.112.0) + gitaly-proto (~> 0.113.0) github-linguist (~> 5.3.3) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) @@ -1216,4 +1216,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.16.2 + 1.16.3 diff --git a/changelogs/unreleased/49907-commits-and-merge-requests-does-not-list-all-files-when-one-file-exceeds-size-limits.yml b/changelogs/unreleased/49907-commits-and-merge-requests-does-not-list-all-files-when-one-file-exceeds-size-limits.yml new file mode 100644 index 00000000000..2fce00a662f --- /dev/null +++ b/changelogs/unreleased/49907-commits-and-merge-requests-does-not-list-all-files-when-one-file-exceeds-size-limits.yml @@ -0,0 +1,5 @@ +--- +title: Fix merge requests not showing any diff files for big patches +merge_request: 21125 +author: +type: fixed diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb index b58296375ef..61ce10ca131 100644 --- a/lib/gitlab/git/diff.rb +++ b/lib/gitlab/git/diff.rb @@ -226,6 +226,7 @@ module Gitlab @new_file = diff.from_id == BLANK_SHA @renamed_file = diff.from_path != diff.to_path @deleted_file = diff.to_id == BLANK_SHA + @too_large = diff.too_large if diff.respond_to?(:too_large) collapse! if diff.respond_to?(:collapsed) && diff.collapsed end diff --git a/lib/gitlab/gitaly_client/diff.rb b/lib/gitlab/gitaly_client/diff.rb index d98a0ce988f..af9d674535b 100644 --- a/lib/gitlab/gitaly_client/diff.rb +++ b/lib/gitlab/gitaly_client/diff.rb @@ -1,7 +1,7 @@ module Gitlab module GitalyClient class Diff - ATTRS = %i(from_path to_path old_mode new_mode from_id to_id patch overflow_marker collapsed).freeze + ATTRS = %i(from_path to_path old_mode new_mode from_id to_id patch overflow_marker collapsed too_large).freeze include AttributesBag end diff --git a/spec/lib/gitlab/gitaly_client/diff_spec.rb b/spec/lib/gitlab/gitaly_client/diff_spec.rb index 00a31ac0b96..ec7ab2fdedb 100644 --- a/spec/lib/gitlab/gitaly_client/diff_spec.rb +++ b/spec/lib/gitlab/gitaly_client/diff_spec.rb @@ -9,7 +9,9 @@ describe Gitlab::GitalyClient::Diff do new_mode: 0100644, from_id: '357406f3075a57708d0163752905cc1576fceacc', to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0', - patch: 'a' * 100 + patch: 'a' * 100, + collapsed: false, + too_large: false } end @@ -22,6 +24,8 @@ describe Gitlab::GitalyClient::Diff do it { is_expected.to respond_to(:from_id) } it { is_expected.to respond_to(:to_id) } it { is_expected.to respond_to(:patch) } + it { is_expected.to respond_to(:collapsed) } + it { is_expected.to respond_to(:too_large) } describe '#==' do it { expect(subject).to eq(described_class.new(diff_fields)) } From 106fdb0b4343d786a179be327bdb8da04d6a60ae Mon Sep 17 00:00:00 2001 From: Ray Paik Date: Fri, 17 Aug 2018 21:51:19 +0000 Subject: [PATCH 14/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6e1cc9a432..869a9262450 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ You can access a new installation with the login **`root`** and password **`5ive ## Contributing -GitLab is an open source project and we are very happy to accept community contributions. Please refer to [CONTRIBUTING.md](/CONTRIBUTING.md) for details. +GitLab is an open source project and we are very happy to accept community contributions. Please refer to [Contributing to GitLab page](https://about.gitlab.com/contributing/) for more details. ## Licensing From c6d867fb0d382f2acd77318ea622f6b7116d37a9 Mon Sep 17 00:00:00 2001 From: Ray Paik Date: Fri, 17 Aug 2018 22:16:09 +0000 Subject: [PATCH 15/31] Update CONTRIBUTING.md --- CONTRIBUTING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e68e3b9cab0..fb7c0c88629 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,11 +64,11 @@ As of July 2018, all the documentation for contributing to the GitLab project ha ## Contribute to GitLab -For a first-time step-by-step guide to the contribution process, see -["Contributing to GitLab"](https://about.gitlab.com/contributing/). - Thank you for your interest in contributing to GitLab. This guide details how -to contribute to GitLab in a way that is efficient for everyone. +to contribute to GitLab in a way that is easy for everyone. + +For a first-time step-by-step guide to the contribution process, please see +["Contributing to GitLab"](https://about.gitlab.com/contributing/). Looking for something to work on? Look for issues with the label [Accepting Merge Requests](#i-want-to-contribute). @@ -77,10 +77,10 @@ source edition, and GitLab Enterprise Edition (EE) which is our commercial edition. Throughout this guide you will see references to CE and EE for abbreviation. -If you have read this guide and want to know how the GitLab [core team] +If you want to know how the GitLab [core team] operates please see [the GitLab contributing process](PROCESS.md). -- [GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/) +[GitLab Inc engineers should refer to the engineering workflow document](https://about.gitlab.com/handbook/engineering/workflow/) ## Security vulnerability disclosure From 98ba19b5f2c08a1c0a60be5f4137feeaa0b81f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 8 Aug 2018 19:05:13 +0200 Subject: [PATCH 16/31] [QA] Improve the fork scenario to take a username and password instead of always creating a new user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://gitlab.com/gitlab-org/quality/staging/issues/2. Signed-off-by: Rémy Coutable --- qa/qa/factory/resource/fork.rb | 7 ++- qa/qa/factory/resource/user.rb | 48 ++++++++++++++----- qa/qa/git/repository.rb | 2 +- qa/qa/page/main/login.rb | 25 +++++++--- qa/qa/page/main/sign_up.rb | 12 ++--- qa/qa/runtime/env.rb | 12 +++++ qa/qa/runtime/user.rb | 6 +-- qa/qa/specs/features/api/basics_spec.rb | 2 +- qa/qa/specs/features/api/users_spec.rb | 4 +- .../features/project/fork_project_spec.rb | 10 ++-- qa/spec/runtime/env_spec.rb | 25 ++++++++++ 11 files changed, 114 insertions(+), 39 deletions(-) diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb index 1d0c76a3d30..01969c31438 100644 --- a/qa/qa/factory/resource/fork.rb +++ b/qa/qa/factory/resource/fork.rb @@ -4,7 +4,12 @@ module QA class Fork < Factory::Base dependency Factory::Repository::ProjectPush, as: :push - dependency Factory::Resource::User, as: :user + dependency Factory::Resource::User, as: :user do |user| + if Runtime::Env.forker? + user.username = Runtime::Env.forker_username + user.password = Runtime::Env.forker_password + end + end product(:user) { |factory| factory.user } diff --git a/qa/qa/factory/resource/user.rb b/qa/qa/factory/resource/user.rb index e08df9e0cd0..eac2a873bd5 100644 --- a/qa/qa/factory/resource/user.rb +++ b/qa/qa/factory/resource/user.rb @@ -4,28 +4,52 @@ module QA module Factory module Resource class User < Factory::Base - attr_accessor :name, :username, :email, :password + attr_reader :unique_id + attr_writer :username, :password, :name, :email def initialize - @name = "name-#{SecureRandom.hex(8)}" - @username = "username-#{SecureRandom.hex(8)}" - @email = "mail#{SecureRandom.hex(8)}@mail.com" - @password = 'password' + @unique_id = SecureRandom.hex(8) + end + + def username + @username ||= "qa-user-#{unique_id}" + end + + def password + @password ||= 'password' + end + + def name + @name ||= username + end + + def email + @email ||= "#{username}@example.com" + end + + def credentials_given? + defined?(@username) && defined?(@password) end product(:name) { |factory| factory.name } - product(:username) { |factory| factory.username } - product(:email) { |factory| factory.email } - product(:password) { |factory| factory.password } def fabricate! - Page::Menu::Main.act { sign_out } - Page::Main::Login.act { switch_to_register_tab } - Page::Main::SignUp.perform do |page| - page.sign_up!(name: name, username: username, email: email, password: password) + Page::Menu::Main.perform { |main| main.sign_out } + + if credentials_given? + Page::Main::Login.perform do |login| + login.sign_in_using_credentials(self) + end + else + Page::Main::Login.perform do |login| + login.switch_to_register_tab + end + Page::Main::SignUp.perform do |signup| + signup.sign_up!(self) + end end end end diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb index 3df6db05970..bdbb18b5045 100644 --- a/qa/qa/git/repository.rb +++ b/qa/qa/git/repository.rb @@ -28,7 +28,7 @@ module QA end def use_default_credentials - self.username = Runtime::User.name + self.username = Runtime::User.username self.password = Runtime::User.password end diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb index 6cdfbd1c125..afc8b66d878 100644 --- a/qa/qa/page/main/login.rb +++ b/qa/qa/page/main/login.rb @@ -40,17 +40,19 @@ module QA end end - def sign_in_using_credentials + def sign_in_using_credentials(user = nil) # Don't try to log-in if we're already logged-in return if Page::Menu::Main.act { has_personal_area?(wait: 0) } using_wait_time 0 do set_initial_password_if_present + raise NotImplementedError if Runtime::User.ldap_user? && user&.credentials_given? + if Runtime::User.ldap_user? sign_in_using_ldap_credentials else - sign_in_using_gitlab_credentials + sign_in_using_gitlab_credentials(user || Runtime::User) end end @@ -69,21 +71,30 @@ module QA click_on 'Register' end + def switch_to_ldap_tab + click_on 'LDAP' + end + + def switch_to_standard_tab + click_on 'Standard' + end + private def sign_in_using_ldap_credentials - click_link 'LDAP' + switch_to_ldap_tab fill_in :username, with: Runtime::User.ldap_username fill_in :password, with: Runtime::User.ldap_password click_button 'Sign in' end - def sign_in_using_gitlab_credentials - click_link 'Standard' if page.has_content?('LDAP') + def sign_in_using_gitlab_credentials(user) + switch_to_sign_in_tab unless page.has_button?('Sign in') + switch_to_standard_tab if page.has_content?('LDAP') - fill_in :user_login, with: Runtime::User.name - fill_in :user_password, with: Runtime::User.password + fill_in :user_login, with: user.username + fill_in :user_password, with: user.password click_button 'Sign in' end diff --git a/qa/qa/page/main/sign_up.rb b/qa/qa/page/main/sign_up.rb index 9a834e94b81..33ab56236f4 100644 --- a/qa/qa/page/main/sign_up.rb +++ b/qa/qa/page/main/sign_up.rb @@ -11,12 +11,12 @@ module QA element :register_button, 'submit "Register"' end - def sign_up!(name:, username:, email:, password:) - fill_in :new_user_name, with: name - fill_in :new_user_username, with: username - fill_in :new_user_email, with: email - fill_in :new_user_email_confirmation, with: email - fill_in :new_user_password, with: password + def sign_up!(user) + fill_in :new_user_name, with: user.name + fill_in :new_user_username, with: user.username + fill_in :new_user_email, with: user.email + fill_in :new_user_email_confirmation, with: user.email + fill_in :new_user_password, with: user.password click_button 'Register' Page::Menu::Main.act { has_personal_area? } diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index 5dc194e0aef..841c959045f 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -39,6 +39,18 @@ module QA ENV['GITLAB_PASSWORD'] end + def forker? + forker_username && forker_password + end + + def forker_username + ENV['GITLAB_FORKER_USERNAME'] + end + + def forker_password + ENV['GITLAB_FORKER_PASSWORD'] + end + def ldap_username ENV['GITLAB_LDAP_USERNAME'] end diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb index c80ee6d4d96..b016777c987 100644 --- a/qa/qa/runtime/user.rb +++ b/qa/qa/runtime/user.rb @@ -3,12 +3,12 @@ module QA module User extend self - def default_name + def default_username 'root' end - def name - Runtime::Env.user_username || default_name + def username + Runtime::Env.user_username || default_username end def password diff --git a/qa/qa/specs/features/api/basics_spec.rb b/qa/qa/specs/features/api/basics_spec.rb index 6563b56d1b4..8234b6e0099 100644 --- a/qa/qa/specs/features/api/basics_spec.rb +++ b/qa/qa/specs/features/api/basics_spec.rb @@ -7,7 +7,7 @@ module QA end let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" } - let(:sanitized_project_path) { CGI.escape("#{Runtime::User.name}/#{project_name}") } + let(:sanitized_project_path) { CGI.escape("#{Runtime::User.username}/#{project_name}") } it 'user creates a project with a file and deletes them afterwards' do create_project_request = Runtime::API::Request.new(@api_client, '/projects') diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb index 8a63d8095c9..5edc72b7003 100644 --- a/qa/qa/specs/features/api/users_spec.rb +++ b/qa/qa/specs/features/api/users_spec.rb @@ -14,11 +14,11 @@ module QA end it 'submit request with a valid user name' do - get request.url, { params: { username: Runtime::User.name } } + get request.url, { params: { username: Runtime::User.username } } expect_status(200) expect(json_body).to contain_exactly( - a_hash_including(username: Runtime::User.name) + a_hash_including(username: Runtime::User.username) ) end diff --git a/qa/qa/specs/features/project/fork_project_spec.rb b/qa/qa/specs/features/project/fork_project_spec.rb index 8ad0120305a..38786a6550a 100644 --- a/qa/qa/specs/features/project/fork_project_spec.rb +++ b/qa/qa/specs/features/project/fork_project_spec.rb @@ -8,14 +8,12 @@ module QA merge_request.fork_branch = 'feature-branch' end - Page::Menu::Main.act { sign_out } - Page::Main::Login.act do - switch_to_sign_in_tab - sign_in_using_credentials - end + Page::Menu::Main.perform { |main| main.sign_out } + Page::Main::Login.perform { |login| login.sign_in_using_credentials } merge_request.visit! - Page::MergeRequest::Show.act { merge! } + + Page::MergeRequest::Show.perform { |show| show.merge! } expect(page).to have_content('The changes were merged') end diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb index 851026c71f0..ccc0b906845 100644 --- a/qa/spec/runtime/env_spec.rb +++ b/qa/spec/runtime/env_spec.rb @@ -77,6 +77,31 @@ describe QA::Runtime::Env do end end + describe '.forker?' do + it 'returns false if no forker credentials are defined' do + expect(described_class).not_to be_forker + end + + it 'returns false if only forker username is defined' do + stub_env('GITLAB_FORKER_USERNAME', 'foo') + + expect(described_class).not_to be_forker + end + + it 'returns false if only forker password is defined' do + stub_env('GITLAB_FORKER_PASSWORD', 'bar') + + expect(described_class).not_to be_forker + end + + it 'returns true if forker username and password are defined' do + stub_env('GITLAB_FORKER_USERNAME', 'foo') + stub_env('GITLAB_FORKER_PASSWORD', 'bar') + + expect(described_class).to be_forker + end + end + describe '.github_access_token' do it 'returns "" if GITHUB_ACCESS_TOKEN is not defined' do expect(described_class.github_access_token).to eq('') From e2118d831f68f844beaf173b6dbb62e7549456c7 Mon Sep 17 00:00:00 2001 From: Maciej Sokolowski Date: Mon, 20 Aug 2018 12:54:46 +0200 Subject: [PATCH 17/31] Test for failure_reason in job webhook --- spec/lib/gitlab/data_builder/build_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/lib/gitlab/data_builder/build_spec.rb b/spec/lib/gitlab/data_builder/build_spec.rb index ee91decafad..14fe196a986 100644 --- a/spec/lib/gitlab/data_builder/build_spec.rb +++ b/spec/lib/gitlab/data_builder/build_spec.rb @@ -15,6 +15,7 @@ describe Gitlab::DataBuilder::Build do it { expect(data[:build_id]).to eq(build.id) } it { expect(data[:build_status]).to eq(build.status) } it { expect(data[:build_allow_failure]).to eq(false) } + it { expect(data[:build_failure_reason]).to eq(build.failure_reason) } it { expect(data[:project_id]).to eq(build.project.id) } it { expect(data[:project_name]).to eq(build.project.full_name) } From e6656ab6801c0a8bd8c22fa15f015e6a2f6cc21f Mon Sep 17 00:00:00 2001 From: Maciej Sokolowski Date: Mon, 20 Aug 2018 12:59:44 +0200 Subject: [PATCH 18/31] Changelog for failure_reason in job webhook --- .../47845-propagate_failure_reason-to-job-webhook.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelogs/unreleased/47845-propagate_failure_reason-to-job-webhook.yml diff --git a/changelogs/unreleased/47845-propagate_failure_reason-to-job-webhook.yml b/changelogs/unreleased/47845-propagate_failure_reason-to-job-webhook.yml new file mode 100644 index 00000000000..3f85f75bef4 --- /dev/null +++ b/changelogs/unreleased/47845-propagate_failure_reason-to-job-webhook.yml @@ -0,0 +1,5 @@ +--- +title: "#47845 Add failure_reason to job webhook" +merge_request: 21143 +author: matemaciek +type: added From 7b2ead6ce85de1b9bd5fb05a01239891a287e094 Mon Sep 17 00:00:00 2001 From: Maciej Sokolowski Date: Mon, 20 Aug 2018 13:04:22 +0200 Subject: [PATCH 19/31] Documentation for failure_reason in job webhook --- doc/user/project/integrations/webhooks.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md index 77fa517b5b1..770b1810da1 100644 --- a/doc/user/project/integrations/webhooks.md +++ b/doc/user/project/integrations/webhooks.md @@ -1102,6 +1102,7 @@ X-Gitlab-Event: Build Hook "build_finished_at": null, "build_duration": null, "build_allow_failure": false, + "build_failure_reason": "script_failure", "project_id": 380, "project_name": "gitlab-org/gitlab-test", "user": { From feca142fe9fe165b850a4007303edc0e145fe612 Mon Sep 17 00:00:00 2001 From: Dylan Griffith Date: Mon, 20 Aug 2018 12:29:19 +0100 Subject: [PATCH 20/31] Disable code_quality check in auto devops QA integration test This test takes too long and leads to flakiness so we disable it for now until somebody can figure out why it takes longer than 20 minutes to finish. --- qa/qa/specs/features/project/auto_devops_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb index c2c3bef98e4..b3e4889abc0 100644 --- a/qa/qa/specs/features/project/auto_devops_spec.rb +++ b/qa/qa/specs/features/project/auto_devops_spec.rb @@ -15,6 +15,13 @@ module QA p.description = 'Project with Auto Devops' end + # Disable code_quality check in Auto DevOps pipeline as it takes + # too long and times out the test + Factory::Resource::SecretVariable.fabricate! do |resource| + resource.key = 'CODE_QUALITY_DISABLED' + resource.value = '1' + end + # Create Auto Devops compatible repo Factory::Repository::ProjectPush.fabricate! do |push| push.project = project From 6f3c490107f5fa7dd00bce0bbd89e4a0fa4d6389 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 30 Jul 2018 17:45:49 +0200 Subject: [PATCH 21/31] Refactor AutocompleteController This refactors the AutocompleteController according to the guidelines and boundaries discussed in https://gitlab.com/gitlab-org/gitlab-ce/issues/49653. Specifically, ActiveRecord logic is moved to different finders, which are then used in the controller. View logic in turn is moved to presenters, instead of directly using ActiveRecord's "to_json" method. The finder MoveToProjectFinder is also adjusted according to the abstraction guidelines and boundaries, resulting in a much more simple finder. By using finders (and other abstractions) more actively, we can push a lot of logic out of the controller. We also remove the need for various "before_action" hooks, though this could be achieved without using finders as well. The various finders related to AutcompleteController have also been moved into a namespace. This removes the need for calling everything "AutocompleteSmurfFinder", instead you can use "Autocomplete::SmurfFinder". --- app/controllers/autocomplete_controller.rb | 72 +++------ app/finders/autocomplete/group_finder.rb | 37 +++++ .../autocomplete/move_to_project_finder.rb | 35 +++++ app/finders/autocomplete/project_finder.rb | 35 +++++ app/finders/autocomplete/users_finder.rb | 85 +++++++++++ app/finders/autocomplete_users_finder.rb | 68 --------- app/finders/awarded_emoji_finder.rb | 21 +++ app/finders/move_to_project_finder.rb | 21 --- app/finders/user_finder.rb | 26 ++++ app/models/award_emoji.rb | 17 +++ app/models/concerns/optionally_search.rb | 19 +++ app/models/project.rb | 21 +++ app/models/user.rb | 45 +++++- app/serializers/move_to_project_entity.rb | 6 + app/serializers/move_to_project_serializer.rb | 5 + .../importer/diff_note_importer.rb | 2 +- .../github_import/importer/issue_importer.rb | 2 +- .../github_import/importer/note_importer.rb | 2 +- .../importer/pull_request_importer.rb | 2 +- .../autocomplete_controller_spec.rb | 13 +- .../finders/autocomplete/group_finder_spec.rb | 58 ++++++++ .../move_to_project_finder_spec.rb | 64 ++++---- .../autocomplete/project_finder_spec.rb | 55 +++++++ .../users_finder_spec.rb} | 2 +- spec/finders/awarded_emoji_finder_spec.rb | 25 ++++ spec/finders/user_finder_spec.rb | 43 ++++++ spec/models/award_emoji_spec.rb | 23 +++ .../models/concerns/optionally_search_spec.rb | 44 ++++++ spec/models/project_spec.rb | 47 ++++++ spec/models/user_spec.rb | 140 ++++++++++++++++-- .../move_to_project_entity_spec.rb | 19 +++ .../move_to_project_serializer_spec.rb | 14 ++ 32 files changed, 879 insertions(+), 189 deletions(-) create mode 100644 app/finders/autocomplete/group_finder.rb create mode 100644 app/finders/autocomplete/move_to_project_finder.rb create mode 100644 app/finders/autocomplete/project_finder.rb create mode 100644 app/finders/autocomplete/users_finder.rb delete mode 100644 app/finders/autocomplete_users_finder.rb create mode 100644 app/finders/awarded_emoji_finder.rb delete mode 100644 app/finders/move_to_project_finder.rb create mode 100644 app/finders/user_finder.rb create mode 100644 app/models/concerns/optionally_search.rb create mode 100644 app/serializers/move_to_project_entity.rb create mode 100644 app/serializers/move_to_project_serializer.rb create mode 100644 spec/finders/autocomplete/group_finder_spec.rb rename spec/finders/{ => autocomplete}/move_to_project_finder_spec.rb (52%) create mode 100644 spec/finders/autocomplete/project_finder_spec.rb rename spec/finders/{autocomplete_users_finder_spec.rb => autocomplete/users_finder_spec.rb} (99%) create mode 100644 spec/finders/awarded_emoji_finder_spec.rb create mode 100644 spec/finders/user_finder_spec.rb create mode 100644 spec/models/concerns/optionally_search_spec.rb create mode 100644 spec/serializers/move_to_project_entity_spec.rb create mode 100644 spec/serializers/move_to_project_serializer_spec.rb diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 86bade49ec9..9e30b982b06 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -1,67 +1,39 @@ class AutocompleteController < ApplicationController - AWARD_EMOJI_MAX = 100 - skip_before_action :authenticate_user!, only: [:users, :award_emojis] - before_action :load_project, only: [:users] - before_action :load_group, only: [:users] def users - @users = AutocompleteUsersFinder.new(params: params, current_user: current_user, project: @project, group: @group).execute + project = Autocomplete::ProjectFinder + .new(current_user, params) + .execute - render json: UserSerializer.new.represent(@users) + group = Autocomplete::GroupFinder + .new(current_user, project, params) + .execute + + users = Autocomplete::UsersFinder + .new(params: params, current_user: current_user, project: project, group: group) + .execute + + render json: UserSerializer.new.represent(users) end def user - @user = User.find(params[:id]) - render json: UserSerializer.new.represent(@user) + user = UserFinder.new(params).execute! + + render json: UserSerializer.new.represent(user) end + # Displays projects to use for the dropdown when moving a resource from one + # project to another. def projects - project = Project.find_by_id(params[:project_id]) - projects = projects_finder.execute(project, search: params[:search], offset_id: params[:offset_id]) + projects = Autocomplete::MoveToProjectFinder + .new(current_user, params) + .execute - render json: projects.to_json(only: [:id, :name_with_namespace], methods: :name_with_namespace) + render json: MoveToProjectSerializer.new.represent(projects) end def award_emojis - emoji_with_count = AwardEmoji - .limit(AWARD_EMOJI_MAX) - .where(user: current_user) - .group(:name) - .order('count_all DESC, name ASC') - .count - - # Transform from hash to array to guarantee json order - # e.g. { 'thumbsup' => 2, 'thumbsdown' = 1 } - # => [{ name: 'thumbsup' }, { name: 'thumbsdown' }] - render json: emoji_with_count.map { |k, v| { name: k } } - end - - private - - def load_group - @group ||= begin - if @project.blank? && params[:group_id].present? - group = Group.find(params[:group_id]) - return render_404 unless can?(current_user, :read_group, group) - - group - end - end - end - - def load_project - @project ||= begin - if params[:project_id].present? - project = Project.find(params[:project_id]) - return render_404 unless can?(current_user, :read_project, project) - - project - end - end - end - - def projects_finder - MoveToProjectFinder.new(current_user) + render json: AwardedEmojiFinder.new(current_user).execute end end diff --git a/app/finders/autocomplete/group_finder.rb b/app/finders/autocomplete/group_finder.rb new file mode 100644 index 00000000000..dd97ac4c817 --- /dev/null +++ b/app/finders/autocomplete/group_finder.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Autocomplete + # Finder for retrieving a group to use for autocomplete data sources. + class GroupFinder + attr_reader :current_user, :project, :group_id + + # current_user - The currently logged in user, if any. + # project - The Project (if any) to use for the autocomplete data sources. + # params - A Hash containing parameters to use for finding the project. + # + # The following parameters are supported: + # + # * group_id: The ID of the group to find. + def initialize(current_user = nil, project = nil, params = {}) + @current_user = current_user + @project = project + @group_id = params[:group_id] + end + + # Attempts to find a Group based on the current group ID. + def execute + return unless project.blank? && group_id.present? + + group = Group.find(group_id) + + # This removes the need for using `return render_404` and similar patterns + # in controllers that use this finder. + unless Ability.allowed?(current_user, :read_group, group) + raise ActiveRecord::RecordNotFound + .new("Could not find a Group with ID #{group_id}") + end + + group + end + end +end diff --git a/app/finders/autocomplete/move_to_project_finder.rb b/app/finders/autocomplete/move_to_project_finder.rb new file mode 100644 index 00000000000..edaf74c5f92 --- /dev/null +++ b/app/finders/autocomplete/move_to_project_finder.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Autocomplete + # Finder that retrieves a list of projects that an issue can be moved to. + class MoveToProjectFinder + attr_reader :current_user, :search, :project_id, :offset_id + + # current_user - The User object of the user that wants to view the list of + # projects. + # + # params - A Hash containing additional parameters to set. + # + # The following parameters can be set (as Symbols): + # + # * search: An optional search query to apply to the list of projects. + # * project_id: The ID of a project to exclude from the returned relation. + # * offset_id: The ID of a project to use for pagination. When given, only + # projects with a lower ID are included in the list. + def initialize(current_user, params = {}) + @current_user = current_user + @search = params[:search] + @project_id = params[:project_id] + @offset_id = params[:offset_id] + end + + def execute + current_user + .projects_where_can_admin_issues + .optionally_search(search) + .excluding_project(project_id) + .paginate_in_descending_order_using_id(before: offset_id) + .eager_load_namespace_and_owner + end + end +end diff --git a/app/finders/autocomplete/project_finder.rb b/app/finders/autocomplete/project_finder.rb new file mode 100644 index 00000000000..3a4696f4c2e --- /dev/null +++ b/app/finders/autocomplete/project_finder.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Autocomplete + # Finder for retrieving a project to use for autocomplete data sources. + class ProjectFinder + attr_reader :current_user, :project_id + + # current_user - The currently logged in user, if any. + # params - A Hash containing parameters to use for finding the project. + # + # The following parameters are supported: + # + # * project_id: The ID of the project to find. + def initialize(current_user = nil, params = {}) + @current_user = current_user + @project_id = params[:project_id] + end + + # Attempts to find a Project based on the current project ID. + def execute + return if project_id.blank? + + project = Project.find(project_id) + + # This removes the need for using `return render_404` and similar patterns + # in controllers that use this finder. + unless Ability.allowed?(current_user, :read_project, project) + raise ActiveRecord::RecordNotFound + .new("Could not find a Project with ID #{project_id}") + end + + project + end + end +end diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb new file mode 100644 index 00000000000..b2557469079 --- /dev/null +++ b/app/finders/autocomplete/users_finder.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module Autocomplete + class UsersFinder + # The number of users to display in the results is hardcoded to 20, and + # pagination is not supported. This ensures that performance remains + # consistent and removes the need for implementing keyset pagination to + # ensure good performance. + LIMIT = 20 + + attr_reader :current_user, :project, :group, :search, :skip_users, + :author_id, :todo_filter, :todo_state_filter, + :filter_by_current_user + + def initialize(params:, current_user:, project:, group:) + @current_user = current_user + @project = project + @group = group + @search = params[:search] + @skip_users = params[:skip_users] + @author_id = params[:author_id] + @todo_filter = params[:todo_filter] + @todo_state_filter = params[:todo_state_filter] + @filter_by_current_user = params[:current_user] + end + + def execute + items = limited_users + + if search.blank? + # Include current user if available to filter by "Me" + items.unshift(current_user) if prepend_current_user? + + if prepend_author? && (author = User.find_by_id(author_id)) + items.unshift(author) + end + end + + items.uniq + end + + private + + # Returns the users based on the input parameters, as an Array. + # + # This method is separate so it is easier to extend in EE. + def limited_users + # When changing the order of these method calls, make sure that + # reorder_by_name() is called _before_ optionally_search(), otherwise + # reorder_by_name will break the ORDER BY applied in optionally_search(). + find_users + .active + .reorder_by_name + .optionally_search(search) + .where_not_in(skip_users) + .limit_to_todo_authors( + user: current_user, + with_todos: todo_filter, + todo_state: todo_state_filter + ) + .limit(LIMIT) + .to_a + end + + def prepend_current_user? + filter_by_current_user.present? && current_user + end + + def prepend_author? + author_id.present? && current_user + end + + def find_users + if project + project.authorized_users.union_with_user(author_id) + elsif group + group.users_with_parents + elsif current_user + User.all + else + User.none + end + end + end +end diff --git a/app/finders/autocomplete_users_finder.rb b/app/finders/autocomplete_users_finder.rb deleted file mode 100644 index e8a03947f59..00000000000 --- a/app/finders/autocomplete_users_finder.rb +++ /dev/null @@ -1,68 +0,0 @@ -class AutocompleteUsersFinder - # The number of users to display in the results is hardcoded to 20, and - # pagination is not supported. This ensures that performance remains - # consistent and removes the need for implementing keyset pagination to ensure - # good performance. - LIMIT = 20 - - attr_reader :current_user, :project, :group, :search, :skip_users, - :author_id, :params - - def initialize(params:, current_user:, project:, group:) - @current_user = current_user - @project = project - @group = group - @search = params[:search] - @skip_users = params[:skip_users] - @author_id = params[:author_id] - @params = params - end - - def execute - items = find_users - items = items.active - items = items.reorder(:name) - items = items.search(search) if search.present? - items = items.where.not(id: skip_users) if skip_users.present? - items = items.limit(LIMIT) - - if params[:todo_filter].present? && current_user - items = items.todo_authors(current_user.id, params[:todo_state_filter]) - end - - if search.blank? - # Include current user if available to filter by "Me" - if params[:current_user].present? && current_user - items = [current_user, *items].uniq - end - - if author_id.present? && current_user - author = User.find_by_id(author_id) - items = [author, *items].uniq if author - end - end - - items - end - - private - - def find_users - return users_from_project if project - return group.users_with_parents if group - return User.all if current_user - - User.none - end - - def users_from_project - if author_id.present? - union = Gitlab::SQL::Union - .new([project.authorized_users, User.where(id: author_id)]) - - User.from("(#{union.to_sql}) #{User.table_name}") - else - project.authorized_users - end - end -end diff --git a/app/finders/awarded_emoji_finder.rb b/app/finders/awarded_emoji_finder.rb new file mode 100644 index 00000000000..f0cc17f3b26 --- /dev/null +++ b/app/finders/awarded_emoji_finder.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# Class for retrieving information about emoji awarded _by_ a particular user. +class AwardedEmojiFinder + attr_reader :current_user + + # current_user - The User to generate the data for. + def initialize(current_user = nil) + @current_user = current_user + end + + def execute + return [] unless current_user + + # We want the resulting data set to be an Array containing the emoji names + # in descending order, based on how often they were awarded. + AwardEmoji + .award_counts_for_user(current_user) + .map { |name, _| { name: name } } + end +end diff --git a/app/finders/move_to_project_finder.rb b/app/finders/move_to_project_finder.rb deleted file mode 100644 index 038d5565a1e..00000000000 --- a/app/finders/move_to_project_finder.rb +++ /dev/null @@ -1,21 +0,0 @@ -class MoveToProjectFinder - PAGE_SIZE = 50 - - def initialize(user) - @user = user - end - - def execute(from_project, search: nil, offset_id: nil) - projects = @user.projects_where_can_admin_issues - projects = projects.search(search) if search.present? - projects = projects.excluding_project(from_project) - projects = projects.order_id_desc - - # infinite scroll using offset - projects = projects.where('projects.id < ?', offset_id) if offset_id.present? - projects = projects.limit(PAGE_SIZE) - - # to ask for Project#name_with_namespace - projects.includes(namespace: :owner) - end -end diff --git a/app/finders/user_finder.rb b/app/finders/user_finder.rb new file mode 100644 index 00000000000..484a93c9873 --- /dev/null +++ b/app/finders/user_finder.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# A simple finding for obtaining a single User. +# +# While using `User.find_by` directly is straightforward, it can lead to a lot +# of code duplication. Sometimes we just want to find a user by an ID, other +# times we may want to exclude blocked user. By using this finder (and extending +# it whenever necessary) we can keep this logic in one place. +class UserFinder + attr_reader :params + + def initialize(params) + @params = params + end + + # Tries to find a User, returning nil if none could be found. + def execute + User.find_by(id: params[:id]) + end + + # Tries to find a User, raising a `ActiveRecord::RecordNotFound` if it could + # not be found. + def execute! + User.find(params[:id]) + end +end diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb index 99c7866d636..ddc516ccb60 100644 --- a/app/models/award_emoji.rb +++ b/app/models/award_emoji.rb @@ -28,6 +28,23 @@ class AwardEmoji < ActiveRecord::Base .where('name IN (?) AND awardable_type = ? AND awardable_id IN (?)', [DOWNVOTE_NAME, UPVOTE_NAME], type, ids) .group('name', 'awardable_id') end + + # Returns the top 100 emoji awarded by the given user. + # + # The returned value is a Hash mapping emoji names to the number of times + # they were awarded: + # + # { 'thumbsup' => 2, 'thumbsdown' => 1 } + # + # user - The User to get the awards for. + # limt - The maximum number of emoji to return. + def award_counts_for_user(user, limit = 100) + limit(limit) + .where(user: user) + .group(:name) + .order('count_all DESC, name ASC') + .count + end end def downvote? diff --git a/app/models/concerns/optionally_search.rb b/app/models/concerns/optionally_search.rb new file mode 100644 index 00000000000..dec97b7dee8 --- /dev/null +++ b/app/models/concerns/optionally_search.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module OptionallySearch + extend ActiveSupport::Concern + + module ClassMethods + def search(*) + raise( + NotImplementedError, + 'Your model must implement the "search" class method' + ) + end + + # Optionally limits a result set to those matching the given search query. + def optionally_search(query = nil) + query.present? ? search(query) : all + end + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 94c1d60f071..15336ec2ea2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -28,6 +28,7 @@ class Project < ActiveRecord::Base include WithUploads include BatchDestroyDependentAssociations include FeatureGate + include OptionallySearch extend Gitlab::Cache::RequestCache extend Gitlab::ConfigHelper @@ -384,6 +385,26 @@ class Project < ActiveRecord::Base only_integer: true, message: 'needs to be beetween 10 minutes and 1 month' } + # Paginates a collection using a `WHERE id < ?` condition. + # + # before - A project ID to use for filtering out projects with an equal or + # greater ID. If no ID is given, all projects are included. + # + # limit - The maximum number of rows to include. + def self.paginate_in_descending_order_using_id( + before: nil, + limit: Kaminari.config.default_per_page + ) + relation = order_id_desc.limit(limit) + relation = relation.where('projects.id < ?', before) if before + + relation + end + + def self.eager_load_namespace_and_owner + includes(namespace: :owner) + end + # Returns a collection of projects that is either public or visible to the # logged in user. def self.public_or_visible_to_user(user = nil) diff --git a/app/models/user.rb b/app/models/user.rb index 13b04270a4a..a6ba90794d6 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -19,6 +19,7 @@ class User < ActiveRecord::Base include BulkMemberAccessLoad include BlocksJsonSerialization include WithUploads + include OptionallySearch DEFAULT_NOTIFICATION_LEVEL = :participating @@ -253,11 +254,41 @@ class User < ActiveRecord::Base scope :external, -> { where(external: true) } scope :active, -> { with_state(:active).non_internal } scope :without_projects, -> { joins('LEFT JOIN project_authorizations ON users.id = project_authorizations.user_id').where(project_authorizations: { user_id: nil }) } - scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) } scope :order_recent_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'DESC')) } scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('current_sign_in_at', 'ASC')) } scope :confirmed, -> { where.not(confirmed_at: nil) } + # Limits the users to those that have TODOs, optionally in the given state. + # + # user - The user to get the todos for. + # + # with_todos - If we should limit the result set to users that are the + # authors of todos. + # + # todo_state - An optional state to require the todos to be in. + def self.limit_to_todo_authors(user: nil, with_todos: false, todo_state: nil) + if user && with_todos + where(id: Todo.where(user: user, state: todo_state).select(:author_id)) + else + all + end + end + + # Returns a relation that optionally includes the given user. + # + # user_id - The ID of the user to include. + def self.union_with_user(user_id = nil) + if user_id.present? + union = Gitlab::SQL::Union.new([all, User.unscoped.where(id: user_id)]) + + # We use "unscoped" here so that any inner conditions are not repeated for + # the outer query, which would be redundant. + User.unscoped.from("(#{union.to_sql}) #{User.table_name}") + else + all + end + end + def self.with_two_factor_indistinct joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id") .where("u2f.id IS NOT NULL OR users.otp_required_for_login = ?", true) @@ -365,6 +396,18 @@ class User < ActiveRecord::Base ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name) end + # Limits the result set to users _not_ in the given query/list of IDs. + # + # users - The list of users to ignore. This can be an + # `ActiveRecord::Relation`, or an Array. + def where_not_in(users = nil) + users ? where.not(id: users) : all + end + + def reorder_by_name + reorder(:name) + end + # searches user by given pattern # it compares name, email, username fields and user's secondary emails with given pattern # This method uses ILIKE on PostgreSQL and LIKE on MySQL. diff --git a/app/serializers/move_to_project_entity.rb b/app/serializers/move_to_project_entity.rb new file mode 100644 index 00000000000..dac1124b0b3 --- /dev/null +++ b/app/serializers/move_to_project_entity.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class MoveToProjectEntity < Grape::Entity + expose :id + expose :name_with_namespace +end diff --git a/app/serializers/move_to_project_serializer.rb b/app/serializers/move_to_project_serializer.rb new file mode 100644 index 00000000000..6a59317505c --- /dev/null +++ b/app/serializers/move_to_project_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class MoveToProjectSerializer < BaseSerializer + entity MoveToProjectEntity +end diff --git a/lib/gitlab/github_import/importer/diff_note_importer.rb b/lib/gitlab/github_import/importer/diff_note_importer.rb index 8274f37d358..d562958e955 100644 --- a/lib/gitlab/github_import/importer/diff_note_importer.rb +++ b/lib/gitlab/github_import/importer/diff_note_importer.rb @@ -13,7 +13,7 @@ module Gitlab @note = note @project = project @client = client - @user_finder = UserFinder.new(project, client) + @user_finder = GithubImport::UserFinder.new(project, client) end def execute diff --git a/lib/gitlab/github_import/importer/issue_importer.rb b/lib/gitlab/github_import/importer/issue_importer.rb index ead4215810f..cb4d7a6a0b6 100644 --- a/lib/gitlab/github_import/importer/issue_importer.rb +++ b/lib/gitlab/github_import/importer/issue_importer.rb @@ -19,7 +19,7 @@ module Gitlab @issue = issue @project = project @client = client - @user_finder = UserFinder.new(project, client) + @user_finder = GithubImport::UserFinder.new(project, client) @milestone_finder = MilestoneFinder.new(project) @issuable_finder = GithubImport::IssuableFinder.new(project, issue) end diff --git a/lib/gitlab/github_import/importer/note_importer.rb b/lib/gitlab/github_import/importer/note_importer.rb index c890f2df360..2b06d1b3baf 100644 --- a/lib/gitlab/github_import/importer/note_importer.rb +++ b/lib/gitlab/github_import/importer/note_importer.rb @@ -13,7 +13,7 @@ module Gitlab @note = note @project = project @client = client - @user_finder = UserFinder.new(project, client) + @user_finder = GithubImport::UserFinder.new(project, client) end def execute diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb index e4b49d2143a..ed17aa54373 100644 --- a/lib/gitlab/github_import/importer/pull_request_importer.rb +++ b/lib/gitlab/github_import/importer/pull_request_importer.rb @@ -15,7 +15,7 @@ module Gitlab @pull_request = pull_request @project = project @client = client - @user_finder = UserFinder.new(project, client) + @user_finder = GithubImport::UserFinder.new(project, client) @milestone_finder = MilestoneFinder.new(project) @issuable_finder = GithubImport::IssuableFinder.new(project, pull_request) diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 2c59d1929a1..883bb35f396 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -274,14 +274,11 @@ describe AutocompleteController do context 'authorized projects apply limit' do before do - authorized_project2 = create(:project) - authorized_project3 = create(:project) + allow(Kaminari.config).to receive(:default_per_page).and_return(2) - authorized_project.add_maintainer(user) - authorized_project2.add_maintainer(user) - authorized_project3.add_maintainer(user) - - stub_const 'MoveToProjectFinder::PAGE_SIZE', 2 + create_list(:project, 2) do |project| + project.add_maintainer(user) + end end describe 'GET #projects with project ID' do @@ -291,7 +288,7 @@ describe AutocompleteController do it 'returns projects' do expect(json_response).to be_kind_of(Array) - expect(json_response.size).to eq 2 # Of a total of 3 + expect(json_response.size).to eq(Kaminari.config.default_per_page) end end end diff --git a/spec/finders/autocomplete/group_finder_spec.rb b/spec/finders/autocomplete/group_finder_spec.rb new file mode 100644 index 00000000000..d7cb2c3bbe2 --- /dev/null +++ b/spec/finders/autocomplete/group_finder_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Autocomplete::GroupFinder do + let(:user) { create(:user) } + + describe '#execute' do + context 'with a project' do + it 'returns nil' do + project = create(:project) + + expect(described_class.new(user, project).execute).to be_nil + end + end + + context 'without a group ID' do + it 'returns nil' do + expect(described_class.new(user).execute).to be_nil + end + end + + context 'with an empty String as the group ID' do + it 'returns nil' do + expect(described_class.new(user, nil, group_id: '').execute).to be_nil + end + end + + context 'without a project and with a group ID' do + it 'raises ActiveRecord::RecordNotFound if the group does not exist' do + finder = described_class.new(user, nil, group_id: 1) + + expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises ActiveRecord::RecordNotFound if the user can not read the group' do + group = create(:group, :private) + finder = described_class.new(user, nil, group_id: group.id) + + expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises ActiveRecord::RecordNotFound if an anonymous user can not read the group' do + group = create(:group, :private) + finder = described_class.new(nil, nil, group_id: group.id) + + expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'returns the group if it exists and is readable' do + group = create(:group) + finder = described_class.new(user, nil, group_id: group.id) + + expect(finder.execute).to eq(group) + end + end + end +end diff --git a/spec/finders/move_to_project_finder_spec.rb b/spec/finders/autocomplete/move_to_project_finder_spec.rb similarity index 52% rename from spec/finders/move_to_project_finder_spec.rb rename to spec/finders/autocomplete/move_to_project_finder_spec.rb index 1b3f44cced1..c3bc410a7f6 100644 --- a/spec/finders/move_to_project_finder_spec.rb +++ b/spec/finders/autocomplete/move_to_project_finder_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MoveToProjectFinder do +describe Autocomplete::MoveToProjectFinder do let(:user) { create(:user) } let(:project) { create(:project) } @@ -10,14 +10,14 @@ describe MoveToProjectFinder do let(:developer_project) { create(:project) } let(:maintainer_project) { create(:project) } - subject { described_class.new(user) } - describe '#execute' do context 'filter' do it 'does not return projects under Gitlab::Access::REPORTER' do guest_project.add_guest(user) - expect(subject.execute(project)).to be_empty + finder = described_class.new(user, project_id: project.id) + + expect(finder.execute).to be_empty end it 'returns projects equal or above Gitlab::Access::REPORTER ordered by id in descending order' do @@ -25,13 +25,17 @@ describe MoveToProjectFinder do developer_project.add_developer(user) maintainer_project.add_maintainer(user) - expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project, reporter_project]) + finder = described_class.new(user, project_id: project.id) + + expect(finder.execute.to_a).to eq([maintainer_project, developer_project, reporter_project]) end it 'does not include the source project' do project.add_reporter(user) - expect(subject.execute(project).to_a).to be_empty + finder = described_class.new(user, project_id: project.id) + + expect(finder.execute.to_a).to be_empty end it 'does not return archived projects' do @@ -40,7 +44,9 @@ describe MoveToProjectFinder do other_reporter_project = create(:project) other_reporter_project.add_reporter(user) - expect(subject.execute(project).to_a).to eq([other_reporter_project]) + finder = described_class.new(user, project_id: project.id) + + expect(finder.execute.to_a).to eq([other_reporter_project]) end it 'does not return projects for which issues are disabled' do @@ -49,39 +55,42 @@ describe MoveToProjectFinder do other_reporter_project = create(:project) other_reporter_project.add_reporter(user) - expect(subject.execute(project).to_a).to eq([other_reporter_project]) + finder = described_class.new(user, project_id: project.id) + + expect(finder.execute.to_a).to eq([other_reporter_project]) end it 'returns a page of projects ordered by id in descending order' do - stub_const 'MoveToProjectFinder::PAGE_SIZE', 2 + allow(Kaminari.config).to receive(:default_per_page).and_return(2) - reporter_project.add_reporter(user) - developer_project.add_developer(user) - maintainer_project.add_maintainer(user) + projects = create_list(:project, 2) do |project| + project.add_developer(user) + end - expect(subject.execute(project).to_a).to eq([maintainer_project, developer_project]) + finder = described_class.new(user, project_id: project.id) + page = finder.execute.to_a + + expect(page.length).to eq(Kaminari.config.default_per_page) + expect(page[0]).to eq(projects.last) end it 'returns projects after the given offset id' do - stub_const 'MoveToProjectFinder::PAGE_SIZE', 2 - reporter_project.add_reporter(user) developer_project.add_developer(user) maintainer_project.add_maintainer(user) - expect(subject.execute(project, search: nil, offset_id: maintainer_project.id).to_a).to eq([developer_project, reporter_project]) - expect(subject.execute(project, search: nil, offset_id: developer_project.id).to_a).to eq([reporter_project]) - expect(subject.execute(project, search: nil, offset_id: reporter_project.id).to_a).to be_empty + expect(described_class.new(user, project_id: project.id, offset_id: maintainer_project.id).execute.to_a) + .to eq([developer_project, reporter_project]) + + expect(described_class.new(user, project_id: project.id, offset_id: developer_project.id).execute.to_a) + .to eq([reporter_project]) + + expect(described_class.new(user, project_id: project.id, offset_id: reporter_project.id).execute.to_a) + .to be_empty end end context 'search' do - it 'uses Project#search' do - expect(user).to receive_message_chain(:projects_where_can_admin_issues, :search) { Project.all } - - subject.execute(project, search: 'wadus') - end - it 'returns projects matching a search query' do foo_project = create(:project) foo_project.add_maintainer(user) @@ -89,8 +98,11 @@ describe MoveToProjectFinder do wadus_project = create(:project, name: 'wadus') wadus_project.add_maintainer(user) - expect(subject.execute(project).to_a).to eq([wadus_project, foo_project]) - expect(subject.execute(project, search: 'wadus').to_a).to eq([wadus_project]) + expect(described_class.new(user, project_id: project.id).execute.to_a) + .to eq([wadus_project, foo_project]) + + expect(described_class.new(user, project_id: project.id, search: 'wadus').execute.to_a) + .to eq([wadus_project]) end end end diff --git a/spec/finders/autocomplete/project_finder_spec.rb b/spec/finders/autocomplete/project_finder_spec.rb new file mode 100644 index 00000000000..207d0598c28 --- /dev/null +++ b/spec/finders/autocomplete/project_finder_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Autocomplete::ProjectFinder do + let(:user) { create(:user) } + + describe '#execute' do + context 'without a project ID' do + it 'returns nil' do + expect(described_class.new(user).execute).to be_nil + end + end + + context 'with an empty String as the project ID' do + it 'returns nil' do + expect(described_class.new(user, project_id: '').execute).to be_nil + end + end + + context 'with a project ID' do + it 'raises ActiveRecord::RecordNotFound if the project does not exist' do + finder = described_class.new(user, project_id: 1) + + expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises ActiveRecord::RecordNotFound if the user can not read the project' do + project = create(:project, :private) + + finder = described_class.new(user, project_id: project.id) + + expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'raises ActiveRecord::RecordNotFound if an anonymous user can not read the project' do + project = create(:project, :private) + + finder = described_class.new(nil, project_id: project.id) + + expect { finder.execute }.to raise_error(ActiveRecord::RecordNotFound) + end + + it 'returns the project if it exists and is readable' do + project = create(:project, :private) + + project.add_maintainer(user) + + finder = described_class.new(user, project_id: project.id) + + expect(finder.execute).to eq(project) + end + end + end +end diff --git a/spec/finders/autocomplete_users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb similarity index 99% rename from spec/finders/autocomplete_users_finder_spec.rb rename to spec/finders/autocomplete/users_finder_spec.rb index dcf9111776e..abd0d6b5185 100644 --- a/spec/finders/autocomplete_users_finder_spec.rb +++ b/spec/finders/autocomplete/users_finder_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe AutocompleteUsersFinder do +describe Autocomplete::UsersFinder do describe '#execute' do let!(:user1) { create(:user, username: 'johndoe') } let!(:user2) { create(:user, :blocked, username: 'notsorandom') } diff --git a/spec/finders/awarded_emoji_finder_spec.rb b/spec/finders/awarded_emoji_finder_spec.rb new file mode 100644 index 00000000000..d4479df7418 --- /dev/null +++ b/spec/finders/awarded_emoji_finder_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe AwardedEmojiFinder do + describe '#execute' do + it 'returns an Array containing the awarded emoji names' do + user = create(:user) + + create(:award_emoji, user: user, name: 'thumbsup') + create(:award_emoji, user: user, name: 'thumbsup') + create(:award_emoji, user: user, name: 'thumbsdown') + + awarded = described_class.new(user).execute + + expect(awarded).to eq([{ name: 'thumbsup' }, { name: 'thumbsdown' }]) + end + + it 'returns an empty Array when no user is given' do + awarded = described_class.new.execute + + expect(awarded).to be_empty + end + end +end diff --git a/spec/finders/user_finder_spec.rb b/spec/finders/user_finder_spec.rb new file mode 100644 index 00000000000..e53aa50dd33 --- /dev/null +++ b/spec/finders/user_finder_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe UserFinder do + describe '#execute' do + context 'when the user exists' do + it 'returns the user' do + user = create(:user) + found = described_class.new(id: user.id).execute + + expect(found).to eq(user) + end + end + + context 'when the user does not exist' do + it 'returns nil' do + found = described_class.new(id: 1).execute + + expect(found).to be_nil + end + end + end + + describe '#execute!' do + context 'when the user exists' do + it 'returns the user' do + user = create(:user) + found = described_class.new(id: user.id).execute! + + expect(found).to eq(user) + end + end + + context 'when the user does not exist' do + it 'raises ActiveRecord::RecordNotFound' do + finder = described_class.new(id: 1) + + expect { finder.execute! }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end +end diff --git a/spec/models/award_emoji_spec.rb b/spec/models/award_emoji_spec.rb index b909e04dfc3..3f52091698c 100644 --- a/spec/models/award_emoji_spec.rb +++ b/spec/models/award_emoji_spec.rb @@ -77,4 +77,27 @@ describe AwardEmoji do end end end + + describe '.award_counts_for_user' do + let(:user) { create(:user) } + + before do + create(:award_emoji, user: user, name: 'thumbsup') + create(:award_emoji, user: user, name: 'thumbsup') + create(:award_emoji, user: user, name: 'thumbsdown') + create(:award_emoji, user: user, name: '+1') + end + + it 'returns the awarded emoji in descending order' do + awards = described_class.award_counts_for_user(user) + + expect(awards).to eq('thumbsup' => 2, 'thumbsdown' => 1, '+1' => 1) + end + + it 'limits the returned number of rows' do + awards = described_class.award_counts_for_user(user, 1) + + expect(awards).to eq('thumbsup' => 2) + end + end end diff --git a/spec/models/concerns/optionally_search_spec.rb b/spec/models/concerns/optionally_search_spec.rb new file mode 100644 index 00000000000..ff4212ddf18 --- /dev/null +++ b/spec/models/concerns/optionally_search_spec.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe OptionallySearch do + let(:model) do + Class.new(ActiveRecord::Base) do + self.table_name = 'users' + + include OptionallySearch + end + end + + describe '.search' do + it 'raises NotImplementedError' do + expect { model.search('foo') }.to raise_error(NotImplementedError) + end + end + + describe '.optionally_search' do + context 'when a query is given' do + it 'delegates to the search method' do + expect(model) + .to receive(:search) + .with('foo') + + model.optionally_search('foo') + end + end + + context 'when no query is given' do + it 'returns the current relation' do + expect(model.optionally_search).to be_a_kind_of(ActiveRecord::Relation) + end + end + + context 'when an empty query is given' do + it 'returns the current relation' do + expect(model.optionally_search('')) + .to be_a_kind_of(ActiveRecord::Relation) + end + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index d8a5e5f6869..56c07f5793b 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1478,6 +1478,53 @@ describe Project do end end + describe '.optionally_search' do + let(:project) { create(:project) } + + it 'searches for projects matching the query if one is given' do + relation = described_class.optionally_search(project.name) + + expect(relation).to eq([project]) + end + + it 'returns the current relation if no search query is given' do + relation = described_class.where(id: project.id) + + expect(relation.optionally_search).to eq(relation) + end + end + + describe '.paginate_in_descending_order_using_id' do + let!(:project1) { create(:project) } + let!(:project2) { create(:project) } + + it 'orders the relation in descending order' do + expect(described_class.paginate_in_descending_order_using_id) + .to eq([project2, project1]) + end + + it 'applies a limit to the relation' do + expect(described_class.paginate_in_descending_order_using_id(limit: 1)) + .to eq([project2]) + end + + it 'limits projects by and ID when given' do + expect(described_class.paginate_in_descending_order_using_id(before: project2.id)) + .to eq([project1]) + end + end + + describe '.including_namespace_and_owner' do + it 'eager loads the namespace and namespace owner' do + create(:project) + + row = described_class.eager_load_namespace_and_owner.to_a.first + recorder = ActiveRecord::QueryRecorder.new { row.namespace.owner } + + expect(recorder.count).to be_zero + end + end + describe '#expire_caches_before_rename' do let(:project) { create(:project, :repository) } let(:repo) { double(:repo, exists?: true) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index f5e2c977104..9763477a711 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -346,17 +346,55 @@ describe User do end end - describe '.todo_authors' do - it 'filters users' do - create :user - user_2 = create :user - user_3 = create :user - current_user = create :user - create(:todo, user: current_user, author: user_2, state: :done) - create(:todo, user: current_user, author: user_3, state: :pending) + describe '.limit_to_todo_authors' do + context 'when filtering by todo authors' do + let(:user1) { create(:user) } + let(:user2) { create(:user) } - expect(described_class.todo_authors(current_user.id, 'pending')).to eq [user_3] - expect(described_class.todo_authors(current_user.id, 'done')).to eq [user_2] + before do + create(:todo, user: user1, author: user1, state: :done) + create(:todo, user: user2, author: user2, state: :pending) + end + + it 'only returns users that have authored todos' do + users = described_class.limit_to_todo_authors( + user: user2, + with_todos: true, + todo_state: :pending + ) + + expect(users).to eq([user2]) + end + + it 'ignores users that do not have a todo in the matching state' do + users = described_class.limit_to_todo_authors( + user: user1, + with_todos: true, + todo_state: :pending + ) + + expect(users).to be_empty + end + end + + context 'when not filtering by todo authors' do + it 'returns the input relation' do + user1 = create(:user) + user2 = create(:user) + rel = described_class.limit_to_todo_authors(user: user1) + + expect(rel).to include(user1, user2) + end + end + + context 'when no user is provided' do + it 'returns the input relation' do + user1 = create(:user) + user2 = create(:user) + rel = described_class.limit_to_todo_authors + + expect(rel).to include(user1, user2) + end end end end @@ -2901,4 +2939,86 @@ describe User do let(:uploader_class) { AttachmentUploader } end end + + describe '.union_with_user' do + context 'when no user ID is provided' do + it 'returns the input relation' do + user = create(:user) + + expect(described_class.union_with_user).to eq([user]) + end + end + + context 'when a user ID is provided' do + it 'includes the user object in the returned relation' do + user1 = create(:user) + user2 = create(:user) + users = described_class.where(id: user1.id).union_with_user(user2.id) + + expect(users).to include(user1) + expect(users).to include(user2) + end + + it 'does not re-apply any WHERE conditions on the outer query' do + relation = described_class.where(id: 1).union_with_user(2) + + expect(relation.arel.where_sql).to be_nil + end + end + end + + describe '.optionally_search' do + context 'using nil as the argument' do + it 'returns the current relation' do + user = create(:user) + + expect(described_class.optionally_search).to eq([user]) + end + end + + context 'using an empty String as the argument' do + it 'returns the current relation' do + user = create(:user) + + expect(described_class.optionally_search('')).to eq([user]) + end + end + + context 'using a non-empty String' do + it 'returns users matching the search query' do + user1 = create(:user) + create(:user) + + expect(described_class.optionally_search(user1.name)).to eq([user1]) + end + end + end + + describe '.where_not_in' do + context 'without an argument' do + it 'returns the current relation' do + user = create(:user) + + expect(described_class.where_not_in).to eq([user]) + end + end + + context 'using a list of user IDs' do + it 'excludes the users from the returned relation' do + user1 = create(:user) + user2 = create(:user) + + expect(described_class.where_not_in([user2.id])).to eq([user1]) + end + end + end + + describe '.reorder_by_name' do + it 'reorders the input relation' do + user1 = create(:user, name: 'A') + user2 = create(:user, name: 'B') + + expect(described_class.reorder_by_name).to eq([user1, user2]) + end + end end diff --git a/spec/serializers/move_to_project_entity_spec.rb b/spec/serializers/move_to_project_entity_spec.rb new file mode 100644 index 00000000000..ac495eadb68 --- /dev/null +++ b/spec/serializers/move_to_project_entity_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe MoveToProjectEntity do + describe '#as_json' do + let(:project) { build(:project, id: 1) } + + subject { described_class.new(project).as_json } + + it 'includes the project ID' do + expect(subject[:id]).to eq(project.id) + end + + it 'includes the full path' do + expect(subject[:name_with_namespace]).to eq(project.name_with_namespace) + end + end +end diff --git a/spec/serializers/move_to_project_serializer_spec.rb b/spec/serializers/move_to_project_serializer_spec.rb new file mode 100644 index 00000000000..841ac969eeb --- /dev/null +++ b/spec/serializers/move_to_project_serializer_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe MoveToProjectSerializer do + describe '#represent' do + it 'includes the name and name with namespace' do + project = build(:project, id: 1) + output = described_class.new.represent(project) + + expect(output).to include(:id, :name_with_namespace) + end + end +end From 67e0082ecee50040103928d600b264078046f63a Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Mon, 20 Aug 2018 14:58:08 +0000 Subject: [PATCH 22/31] clarify runners currently online text --- app/views/admin/runners/index.html.haml | 2 +- changelogs/unreleased/runners-online.yml | 5 +++++ spec/features/admin/admin_runners_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/runners-online.yml diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 8dfd176f1b7..9280ff4d478 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -49,7 +49,7 @@ = submit_tag 'Search', class: 'btn' .float-right.light - Runners with last contact more than a minute ago: #{@active_runners_cnt} + Runners currently online: #{@active_runners_cnt} %br diff --git a/changelogs/unreleased/runners-online.yml b/changelogs/unreleased/runners-online.yml new file mode 100644 index 00000000000..a732d9cb723 --- /dev/null +++ b/changelogs/unreleased/runners-online.yml @@ -0,0 +1,5 @@ +--- +title: Clarify current runners online text +merge_request: 21151 +author: Ben Bodenmiller +type: other diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index be8754a5315..5623e47eadf 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -20,7 +20,7 @@ describe "Admin Runners" do it 'has all necessary texts' do expect(page).to have_text "Setup a shared Runner manually" - expect(page).to have_text "Runners with last contact more than a minute ago: 1" + expect(page).to have_text "Runners currently online: 1" end describe 'search' do @@ -55,7 +55,7 @@ describe "Admin Runners" do it 'has all necessary texts including no runner message' do expect(page).to have_text "Setup a shared Runner manually" - expect(page).to have_text "Runners with last contact more than a minute ago: 0" + expect(page).to have_text "Runners currently online: 0" expect(page).to have_text 'No runners found' end end From 8ae1a585ea8e0e272891cdd06a2bdddf51c71c2c Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 20 Aug 2018 17:12:24 +0100 Subject: [PATCH 23/31] Define width as max-width to allow svgs to scale in smaller screens --- app/assets/stylesheets/framework/images.scss | 2 +- changelogs/unreleased/48145-illustration.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/48145-illustration.yml diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index f878ec1ca91..1e93bf2b751 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -25,7 +25,7 @@ &.svg-#{$width} { img, svg { - width: #{$width + 'px'}; + max-width: #{$width + 'px'}; } } } diff --git a/changelogs/unreleased/48145-illustration.yml b/changelogs/unreleased/48145-illustration.yml new file mode 100644 index 00000000000..7d84075c2b3 --- /dev/null +++ b/changelogs/unreleased/48145-illustration.yml @@ -0,0 +1,5 @@ +--- +title: Fixes SVGs for empty states in job page overflowing on mobile +merge_request: +author: +type: fixed From 5667422af780c095c684fc64b360474b78c05705 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 20 Aug 2018 17:49:26 +0100 Subject: [PATCH 24/31] Does not collapse runners section when using pagination Includes settings hash on runners pagination links --- app/views/projects/runners/_specific_runners.html.haml | 2 +- changelogs/unreleased/42754-runners-pagination.yml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/42754-runners-pagination.yml diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index 6c11ce3b394..314af44490e 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -13,4 +13,4 @@ %h4.underlined-title Available specific runners %ul.bordered-list.available-specific-runners = render partial: 'projects/runners/runner', collection: @assignable_runners, as: :runner - = paginate @assignable_runners, theme: "gitlab" + = paginate @assignable_runners, theme: "gitlab", :params => { :anchor => '#js-runners-settings' } diff --git a/changelogs/unreleased/42754-runners-pagination.yml b/changelogs/unreleased/42754-runners-pagination.yml new file mode 100644 index 00000000000..8e77b857538 --- /dev/null +++ b/changelogs/unreleased/42754-runners-pagination.yml @@ -0,0 +1,5 @@ +--- +title: Does not collapse runners section when using pagination +merge_request: +author: +type: fixed From 4d7d4a0c16bfcac87416a0ba51296039e4c8b1d4 Mon Sep 17 00:00:00 2001 From: Gilbert Roulot Date: Mon, 20 Aug 2018 18:09:57 +0000 Subject: [PATCH 25/31] Remove Gemnasium service --- Gemfile | 3 - Gemfile.lock | 3 - Gemfile.rails5.lock | 3 - app/models/project.rb | 1 - .../project_services/gemnasium_service.rb | 62 ---------------- .../6010_remove_gemnasium_service.yml | 5 ++ doc/api/services.md | 42 ----------- doc/integration/README.md | 2 +- .../project/integrations/project_services.md | 1 - lib/api/services.rb | 15 ---- spec/lib/gitlab/import_export/all_models.yml | 1 - .../gemnasium_service_spec.rb | 74 ------------------- spec/models/project_spec.rb | 1 - vendor/licenses.csv | 1 - 14 files changed, 6 insertions(+), 208 deletions(-) delete mode 100644 app/models/project_services/gemnasium_service.rb create mode 100644 changelogs/unreleased/6010_remove_gemnasium_service.yml delete mode 100644 spec/models/project_services/gemnasium_service_spec.rb diff --git a/Gemfile b/Gemfile index f01adaddd68..bcede83df12 100644 --- a/Gemfile +++ b/Gemfile @@ -214,9 +214,6 @@ gem 'jira-ruby', '~> 1.4' # Flowdock integration gem 'gitlab-flowdock-git-hook', '~> 1.0.1' -# Gemnasium integration -gem 'gemnasium-gitlab-service', '~> 0.2' - # Slack integration gem 'slack-notifier', '~> 1.5.1' diff --git a/Gemfile.lock b/Gemfile.lock index 333e586949f..15a105579fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -269,8 +269,6 @@ GEM fuubar (2.2.0) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - gemnasium-gitlab-service (0.2.6) - rugged (~> 0.21) gemojione (3.3.0) json get_process_mem (0.2.0) @@ -1040,7 +1038,6 @@ DEPENDENCIES font-awesome-rails (~> 4.7) foreman (~> 0.84.0) fuubar (~> 2.2.0) - gemnasium-gitlab-service (~> 0.2) gemojione (~> 3.3) gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index c919f1a08ae..7803d12c6b4 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -272,8 +272,6 @@ GEM fuubar (2.2.0) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - gemnasium-gitlab-service (0.2.6) - rugged (~> 0.21) gemojione (3.3.0) json get_process_mem (0.2.0) @@ -1052,7 +1050,6 @@ DEPENDENCIES font-awesome-rails (~> 4.7) foreman (~> 0.84.0) fuubar (~> 2.2.0) - gemnasium-gitlab-service (~> 0.2) gemojione (~> 3.3) gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) diff --git a/app/models/project.rb b/app/models/project.rb index 15336ec2ea2..8f631d7f0ed 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -141,7 +141,6 @@ class Project < ActiveRecord::Base has_one :flowdock_service has_one :assembla_service has_one :asana_service - has_one :gemnasium_service has_one :mattermost_slash_commands_service has_one :mattermost_service has_one :slack_slash_commands_service diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb deleted file mode 100644 index 67a92c441b1..00000000000 --- a/app/models/project_services/gemnasium_service.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -require "gemnasium/gitlab_service" - -class GemnasiumService < Service - prop_accessor :token, :api_key - validates :token, :api_key, presence: true, if: :activated? - validate :deprecation_validation - - def title - 'Gemnasium' - end - - def description - 'Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities.' - end - - def self.to_param - 'gemnasium' - end - - def fields - [ - { type: 'text', name: 'api_key', placeholder: 'Your personal API KEY on gemnasium.com ', required: true }, - { type: 'text', name: 'token', placeholder: 'The project\'s slug on gemnasium.com', required: true } - ] - end - - def self.supported_events - %w(push) - end - - def deprecated? - true - end - - def deprecation_message - "Gemnasium has been acquired by GitLab in January 2018. Since May 15, 2018, the service provided by Gemnasium is no longer available." - end - - def deprecation_validation - errors[:base] << deprecation_message - end - - def execute(data) - return unless supported_events.include?(data[:object_kind]) - - # Gitaly: this class will be removed https://gitlab.com/gitlab-org/gitlab-ee/issues/6010 - repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.path_to_repo - end - - Gemnasium::GitlabService.execute( - ref: data[:ref], - before: data[:before], - after: data[:after], - token: token, - api_key: api_key, - repo: repo_path - ) - end -end diff --git a/changelogs/unreleased/6010_remove_gemnasium_service.yml b/changelogs/unreleased/6010_remove_gemnasium_service.yml new file mode 100644 index 00000000000..900e84c9eed --- /dev/null +++ b/changelogs/unreleased/6010_remove_gemnasium_service.yml @@ -0,0 +1,5 @@ +--- +title: Remove Gemnasium service +merge_request: 21185 +author: +type: removed diff --git a/doc/api/services.md b/doc/api/services.md index efa173180bb..8c59b232b6d 100644 --- a/doc/api/services.md +++ b/doc/api/services.md @@ -401,48 +401,6 @@ Get Flowdock service settings for a project. GET /projects/:id/services/flowdock ``` -## Gemnasium - -Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities. - -CAUTION: **Warning:** -Gemnasium service integration has been deprecated in GitLab 11.0. Gemnasium has been -[acquired by GitLab](https://about.gitlab.com/press/releases/2018-01-30-gemnasium-acquisition.html) -in January 2018 and since May 15, 2018, the service provided by Gemnasium is no longer available. -You can [migrate from Gemnasium to GitLab](https://docs.gitlab.com/ee/user/project/import/gemnasium.html) -to keep monitoring your dependencies. - -### Create/Edit Gemnasium service - -Set Gemnasium service for a project. - -``` -PUT /projects/:id/services/gemnasium -``` - -Parameters: - -| Parameter | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `api_key` | string | true | Your personal API KEY on gemnasium.com | -| `token` | string | true | The project's slug on gemnasium.com | - -### Delete Gemnasium service - -Delete Gemnasium service for a project. - -``` -DELETE /projects/:id/services/gemnasium -``` - -### Get Gemnasium service settings - -Get Gemnasium service settings for a project. - -``` -GET /projects/:id/services/gemnasium -``` - ## Hangouts Chat Google GSuite team collaboration tool. diff --git a/doc/integration/README.md b/doc/integration/README.md index 54e78bdef54..8a93d4cb84b 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -30,7 +30,7 @@ Bitbucket.org account ## Project services -Integration with services such as Campfire, Flowdock, Gemnasium, HipChat, +Integration with services such as Campfire, Flowdock, HipChat, Pivotal Tracker, and Slack are available in the form of a [Project Service][]. [Project Service]: ../user/project/integrations/project_services.md diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md index 05ee1b4e6d7..efb0381d7aa 100644 --- a/doc/user/project/integrations/project_services.md +++ b/doc/user/project/integrations/project_services.md @@ -34,7 +34,6 @@ Click on the service links to see further configuration instructions and details | [Emails on push](emails_on_push.md) | Email the commits and diff of each push to a list of recipients | | External Wiki | Replaces the link to the internal wiki with a link to an external wiki | | Flowdock | Flowdock is a collaboration web app for technical teams | -| Gemnasium _(Has been deprecated in GitLab 11.0)_ | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities | | [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat | | [HipChat](hipchat.md) | Private group chat and IM | | [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway | diff --git a/lib/api/services.rb b/lib/api/services.rb index 1f2bf546cd7..d1a5ee7db35 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -354,20 +354,6 @@ module API desc: 'Flowdock token' } ], - 'gemnasium' => [ - { - required: true, - name: :api_key, - type: String, - desc: 'Your personal API key on gemnasium.com' - }, - { - required: true, - name: :token, - type: String, - desc: "The project's slug on gemnasium.com" - } - ], 'hangouts-chat' => [ { required: true, @@ -695,7 +681,6 @@ module API EmailsOnPushService, ExternalWikiService, FlowdockService, - GemnasiumService, HangoutsChatService, HipchatService, IrkerService, diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index e9a1932407d..b4269bd5786 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -210,7 +210,6 @@ project: - flowdock_service - assembla_service - asana_service -- gemnasium_service - slack_service - microsoft_teams_service - mattermost_service diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb deleted file mode 100644 index 6e417323e40..00000000000 --- a/spec/models/project_services/gemnasium_service_spec.rb +++ /dev/null @@ -1,74 +0,0 @@ -require 'spec_helper' - -describe GemnasiumService do - describe "Associations" do - it { is_expected.to belong_to :project } - it { is_expected.to have_one :service_hook } - end - - describe 'Validations' do - context 'when service is active' do - before do - subject.active = true - end - - it { is_expected.to validate_presence_of(:token) } - it { is_expected.to validate_presence_of(:api_key) } - end - - context 'when service is inactive' do - before do - subject.active = false - end - - it { is_expected.not_to validate_presence_of(:token) } - it { is_expected.not_to validate_presence_of(:api_key) } - end - end - - describe "deprecated?" do - let(:project) { create(:project, :repository) } - let(:gemnasium_service) { described_class.new } - - before do - allow(gemnasium_service).to receive_messages( - project_id: project.id, - project: project, - service_hook: true, - token: 'verySecret', - api_key: 'GemnasiumUserApiKey' - ) - end - - it "is true" do - expect(gemnasium_service.deprecated?).to be true - end - - it "can't create a new service" do - expect(gemnasium_service.save).to be false - expect(gemnasium_service.errors[:base].first) - .to eq('Gemnasium has been acquired by GitLab in January 2018. Since May 15, 2018, the service provided by Gemnasium is no longer available.') - end - end - - describe "Execute" do - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } - let(:gemnasium_service) { described_class.new } - let(:sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) } - - before do - allow(gemnasium_service).to receive_messages( - project_id: project.id, - project: project, - service_hook: true, - token: 'verySecret', - api_key: 'GemnasiumUserApiKey' - ) - end - it "calls Gemnasium service" do - expect(Gemnasium::GitlabService).to receive(:execute).with(an_instance_of(Hash)).once - gemnasium_service.execute(sample_data) - end - end -end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 56c07f5793b..8cb706b54b0 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -42,7 +42,6 @@ describe Project do it { is_expected.to have_one(:assembla_service) } it { is_expected.to have_one(:slack_slash_commands_service) } it { is_expected.to have_one(:mattermost_slash_commands_service) } - it { is_expected.to have_one(:gemnasium_service) } it { is_expected.to have_one(:buildkite_service) } it { is_expected.to have_one(:bamboo_service) } it { is_expected.to have_one(:teamcity_service) } diff --git a/vendor/licenses.csv b/vendor/licenses.csv index a462daf3067..ffe56286684 100644 --- a/vendor/licenses.csv +++ b/vendor/licenses.csv @@ -615,7 +615,6 @@ function-bind,1.1.1,MIT functional-red-black-tree,1.0.1,MIT fuzzaldrin-plus,0.5.0,MIT gauge,2.7.4,ISC -gemnasium-gitlab-service,0.2.6,MIT gemojione,3.3.0,MIT generate-function,2.0.0,MIT generate-object-property,1.2.0,MIT From d67936b68f2ba872f5335be1f672f862410c03e5 Mon Sep 17 00:00:00 2001 From: Dan Davison Date: Mon, 20 Aug 2018 18:13:54 +0000 Subject: [PATCH 26/31] add initial smoke tests and documentation --- doc/development/testing_guide/smoke.md | 16 +++++++ .../testing_guide/testing_levels.md | 8 ++++ qa/README.md | 12 ++--- qa/qa.rb | 6 ++- qa/qa/scenario/taggable.rb | 17 ------- qa/qa/scenario/template.rb | 33 +++++++++++--- qa/qa/scenario/test/instance.rb | 36 --------------- qa/qa/scenario/test/instance/all.rb | 15 +++++++ qa/qa/scenario/test/instance/smoke.rb | 17 +++++++ qa/qa/specs/features/api/basics_spec.rb | 2 +- qa/qa/specs/features/api/users_spec.rb | 2 +- qa/qa/specs/features/login/basic_spec.rb | 15 +++++++ qa/qa/specs/features/login/ldap_spec.rb | 2 +- .../features/mattermost/group_create_spec.rb | 2 +- qa/qa/specs/features/mattermost/login_spec.rb | 2 +- .../features/merge_request/create_spec.rb | 23 +++++++++- .../features/merge_request/rebase_spec.rb | 2 +- .../features/merge_request/squash_spec.rb | 2 +- qa/qa/specs/features/project/activity_spec.rb | 2 +- .../features/project/add_deploy_key_spec.rb | 2 +- .../project/add_secret_variable_spec.rb | 2 +- .../features/project/auto_devops_spec.rb | 2 +- .../features/project/create_issue_spec.rb | 2 +- qa/qa/specs/features/project/create_spec.rb | 2 +- .../features/project/deploy_key_clone_spec.rb | 2 +- .../features/project/fork_project_spec.rb | 2 +- .../project/import_from_github_spec.rb | 2 +- .../specs/features/project/pipelines_spec.rb | 2 +- qa/qa/specs/features/project/wikis_spec.rb | 2 +- qa/qa/specs/features/repository/clone_spec.rb | 2 +- .../repository/protected_branches_spec.rb | 2 +- qa/qa/specs/features/repository/push_spec.rb | 2 +- qa/qa/specs/runner.rb | 8 +++- .../all_spec.rb} | 14 ++---- qa/spec/scenario/test/instance/smoke_spec.rb | 45 +++++++++++++++++++ 35 files changed, 207 insertions(+), 100 deletions(-) create mode 100644 doc/development/testing_guide/smoke.md delete mode 100644 qa/qa/scenario/taggable.rb delete mode 100644 qa/qa/scenario/test/instance.rb create mode 100644 qa/qa/scenario/test/instance/all.rb create mode 100644 qa/qa/scenario/test/instance/smoke.rb create mode 100644 qa/qa/specs/features/login/basic_spec.rb rename qa/spec/scenario/test/{instance_spec.rb => instance/all_spec.rb} (74%) create mode 100644 qa/spec/scenario/test/instance/smoke_spec.rb diff --git a/doc/development/testing_guide/smoke.md b/doc/development/testing_guide/smoke.md new file mode 100644 index 00000000000..3f2843cba6e --- /dev/null +++ b/doc/development/testing_guide/smoke.md @@ -0,0 +1,16 @@ +# Smoke Tests + +It is imperative in any testing suite that we have Smoke Tests. In short, smoke tests are will run quick sanity +end-to-end functional tests from GitLab QA and are designed to run against the specified environment to ensure that +basic functionality is working. + +Currently, our suite consists of this basic functionality coverage: + +- User Login (Standard Auth) +- Project Creation +- Issue Creation +- Merge Request Creation + +--- + +[Return to Testing documentation](index.md) diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md index 07ced36f0c1..4403072e96f 100644 --- a/doc/development/testing_guide/testing_levels.md +++ b/doc/development/testing_guide/testing_levels.md @@ -120,6 +120,14 @@ running feature tests (i.e. using Capybara) against it. The actual test scenarios and steps are [part of GitLab Rails] so that they're always in-sync with the codebase. +### Smoke tests + +Smoke tests are quick tests that may be run at any time (especially after the pre-deployment migrations). + +Much like feature tests - these tests run against the UI and ensure that basic functionality is working. + +> See [Smoke Tests](smoke.md) for more information. + Read a separate document about [end-to-end tests](end_to_end_tests.md) to learn more. diff --git a/qa/README.md b/qa/README.md index be4cf89ebbc..f8a5c00efd4 100644 --- a/qa/README.md +++ b/qa/README.md @@ -35,7 +35,7 @@ following call would login to a local [GDK] instance and run all specs in `qa/specs/features`: ``` -bin/qa Test::Instance http://localhost:3000 +bin/qa Test::Instance::All http://localhost:3000 ``` ### Writing tests @@ -48,14 +48,14 @@ You can also supply specific tests to run as another parameter. For example, to run the repository-related specs, you can execute: ``` -bin/qa Test::Instance http://localhost qa/specs/features/repository/ +bin/qa Test::Instance::All http://localhost qa/specs/features/repository/ ``` Since the arguments would be passed to `rspec`, you could use all `rspec` options there. For example, passing `--backtrace` and also line number: ``` -bin/qa Test::Instance http://localhost qa/specs/features/project/create_spec.rb:3 --backtrace +bin/qa Test::Instance::All http://localhost qa/specs/features/project/create_spec.rb:3 --backtrace ``` ### Overriding the authenticated user @@ -67,7 +67,7 @@ If you need to authenticate as a different user, you can provide the `GITLAB_USERNAME` and `GITLAB_PASSWORD` environment variables: ``` -GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password bin/qa Test::Instance https://gitlab.example.com +GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password bin/qa Test::Instance::All https://gitlab.example.com ``` If your user doesn't have permission to default sandbox group @@ -75,13 +75,13 @@ If your user doesn't have permission to default sandbox group `GITLAB_SANDBOX_NAME`: ``` -GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance https://gitlab.example.com +GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance::All https://gitlab.example.com ``` In addition, the `GITLAB_USER_TYPE` can be set to "ldap" to sign in as an LDAP user: ``` -GITLAB_USER_TYPE=ldap GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance https://gitlab.example.com +GITLAB_USER_TYPE=ldap GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance::All https://gitlab.example.com ``` All [supported environment variables are here](https://gitlab.com/gitlab-org/gitlab-qa#supported-environment-variables). diff --git a/qa/qa.rb b/qa/qa.rb index 0b48cf58766..4b927067449 100644 --- a/qa/qa.rb +++ b/qa/qa.rb @@ -77,14 +77,16 @@ module QA # autoload :Bootable, 'qa/scenario/bootable' autoload :Actable, 'qa/scenario/actable' - autoload :Taggable, 'qa/scenario/taggable' autoload :Template, 'qa/scenario/template' ## # Test scenario entrypoints. # module Test - autoload :Instance, 'qa/scenario/test/instance' + module Instance + autoload :All, 'qa/scenario/test/instance/all' + autoload :Smoke, 'qa/scenario/test/instance/smoke' + end module Integration autoload :Github, 'qa/scenario/test/integration/github' diff --git a/qa/qa/scenario/taggable.rb b/qa/qa/scenario/taggable.rb deleted file mode 100644 index b1f24d742e0..00000000000 --- a/qa/qa/scenario/taggable.rb +++ /dev/null @@ -1,17 +0,0 @@ -module QA - module Scenario - module Taggable - # rubocop:disable Gitlab/ModuleWithInstanceVariables - - def tags(*tags) - @tags = tags - end - - def focus - @tags.to_a - end - - # rubocop:enable Gitlab/ModuleWithInstanceVariables - end - end -end diff --git a/qa/qa/scenario/template.rb b/qa/qa/scenario/template.rb index d21a9d52997..765b7d317e0 100644 --- a/qa/qa/scenario/template.rb +++ b/qa/qa/scenario/template.rb @@ -1,15 +1,36 @@ module QA module Scenario class Template - def self.perform(*args) - new.tap do |scenario| - yield scenario if block_given? - break scenario.perform(*args) + class << self + def perform(*args) + new.tap do |scenario| + yield scenario if block_given? + break scenario.perform(*args) + end + end + + def tags(*tags) + @tags = tags + end + + def focus + @tags.to_a end end - def perform(*_args) - raise NotImplementedError + def perform(address, *rspec_options) + Runtime::Scenario.define(:gitlab_address, address) + + Specs::Runner.perform do |specs| + specs.tty = true + specs.tags = self.class.focus + specs.options = + if rspec_options.any? + rspec_options + else + File.expand_path('../../specs/features', __dir__) + end + end end end end diff --git a/qa/qa/scenario/test/instance.rb b/qa/qa/scenario/test/instance.rb deleted file mode 100644 index 46eb2dabb11..00000000000 --- a/qa/qa/scenario/test/instance.rb +++ /dev/null @@ -1,36 +0,0 @@ -module QA - module Scenario - module Test - ## - # Base class for running the suite against any GitLab instance, - # including staging and on-premises installation. - # - class Instance < Template - include Bootable - extend Taggable - - tags :core - - def perform(address, *rspec_options) - Runtime::Scenario.define(:gitlab_address, address) - - ## - # Perform before hooks, which are different for CE and EE - # - Runtime::Release.perform_before_hooks - - Specs::Runner.perform do |specs| - specs.tty = true - specs.tags = self.class.focus - specs.options = - if rspec_options.any? - rspec_options - else - ::File.expand_path('../../specs/features', __dir__) - end - end - end - end - end - end -end diff --git a/qa/qa/scenario/test/instance/all.rb b/qa/qa/scenario/test/instance/all.rb new file mode 100644 index 00000000000..a07c26431bd --- /dev/null +++ b/qa/qa/scenario/test/instance/all.rb @@ -0,0 +1,15 @@ +module QA + module Scenario + module Test + ## + # Base class for running the suite against any GitLab instance, + # including staging and on-premises installation. + # + module Instance + class All < Template + include Bootable + end + end + end + end +end diff --git a/qa/qa/scenario/test/instance/smoke.rb b/qa/qa/scenario/test/instance/smoke.rb new file mode 100644 index 00000000000..a7d2cb27f27 --- /dev/null +++ b/qa/qa/scenario/test/instance/smoke.rb @@ -0,0 +1,17 @@ +module QA + module Scenario + module Test + module Instance + ## + # Base class for running the suite against any GitLab instance, + # including staging and on-premises installation. + # + class Smoke < Template + include Bootable + + tags :smoke + end + end + end + end +end diff --git a/qa/qa/specs/features/api/basics_spec.rb b/qa/qa/specs/features/api/basics_spec.rb index 6563b56d1b4..7defb2ea93e 100644 --- a/qa/qa/specs/features/api/basics_spec.rb +++ b/qa/qa/specs/features/api/basics_spec.rb @@ -1,7 +1,7 @@ require 'securerandom' module QA - describe 'API basics', :core do + describe 'API basics' do before(:context) do @api_client = Runtime::API::Client.new(:gitlab) end diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb index 8a63d8095c9..2a9360734bb 100644 --- a/qa/qa/specs/features/api/users_spec.rb +++ b/qa/qa/specs/features/api/users_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'API users', :core do + describe 'API users' do before(:context) do @api_client = Runtime::API::Client.new(:gitlab) end diff --git a/qa/qa/specs/features/login/basic_spec.rb b/qa/qa/specs/features/login/basic_spec.rb new file mode 100644 index 00000000000..f866466c7bf --- /dev/null +++ b/qa/qa/specs/features/login/basic_spec.rb @@ -0,0 +1,15 @@ +module QA + describe 'basic user login', :smoke do + it 'user logs in using basic credentials' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + # TODO, since `Signed in successfully` message was removed + # this is the only way to tell if user is signed in correctly. + # + Page::Menu::Main.perform do |menu| + expect(menu).to have_personal_area + end + end + end +end diff --git a/qa/qa/specs/features/login/ldap_spec.rb b/qa/qa/specs/features/login/ldap_spec.rb index b7a284c584b..de6111eea64 100644 --- a/qa/qa/specs/features/login/ldap_spec.rb +++ b/qa/qa/specs/features/login/ldap_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'LDAP user login', :ldap do + describe 'LDAP user login', :orchestrated, :ldap do before do Runtime::Env.user_type = 'ldap' end diff --git a/qa/qa/specs/features/mattermost/group_create_spec.rb b/qa/qa/specs/features/mattermost/group_create_spec.rb index a59761da341..097e1713aef 100644 --- a/qa/qa/specs/features/mattermost/group_create_spec.rb +++ b/qa/qa/specs/features/mattermost/group_create_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'create a new group', :mattermost do + describe 'create a new group', :orchestrated, :mattermost do it 'creating a group with a mattermost team' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/mattermost/login_spec.rb b/qa/qa/specs/features/mattermost/login_spec.rb index b140191e160..27f7d4c245f 100644 --- a/qa/qa/specs/features/mattermost/login_spec.rb +++ b/qa/qa/specs/features/mattermost/login_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'logging in to Mattermost', :mattermost do + describe 'logging in to Mattermost', :orchestrated, :mattermost do it 'can use gitlab oauth' do Runtime::Browser.visit(:gitlab, Page::Main::Login) do Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb index 36d7efb02e1..71e79956b85 100644 --- a/qa/qa/specs/features/merge_request/create_spec.rb +++ b/qa/qa/specs/features/merge_request/create_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'creates a merge request', :core do + describe 'creates a merge request with milestone' do it 'user creates a new merge request' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } @@ -29,4 +29,25 @@ module QA end end end + + describe 'creates a merge request', :smoke do + it 'user creates a new merge request' do + Runtime::Browser.visit(:gitlab, Page::Main::Login) + Page::Main::Login.act { sign_in_using_credentials } + + current_project = Factory::Resource::Project.fabricate! do |project| + project.name = 'project-with-merge-request' + end + + Factory::Resource::MergeRequest.fabricate! do |merge_request| + merge_request.title = 'This is a merge request' + merge_request.description = 'Great feature' + merge_request.project = current_project + end + + expect(page).to have_content('This is a merge request') + expect(page).to have_content('Great feature') + expect(page).to have_content(/Opened [\w\s]+ ago/) + end + end end diff --git a/qa/qa/specs/features/merge_request/rebase_spec.rb b/qa/qa/specs/features/merge_request/rebase_spec.rb index 163dcbe7963..c36d28e4237 100644 --- a/qa/qa/specs/features/merge_request/rebase_spec.rb +++ b/qa/qa/specs/features/merge_request/rebase_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'merge request rebase', :core do + describe 'merge request rebase' do it 'rebases source branch of merge request' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/merge_request/squash_spec.rb b/qa/qa/specs/features/merge_request/squash_spec.rb index 4856bbe1a69..3ecc36a5ae1 100644 --- a/qa/qa/specs/features/merge_request/squash_spec.rb +++ b/qa/qa/specs/features/merge_request/squash_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'merge request squash commits', :core do + describe 'merge request squash commits' do it 'when squash commits is marked before merge' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/project/activity_spec.rb b/qa/qa/specs/features/project/activity_spec.rb index 02074e386b6..c7ce8dfdcc6 100644 --- a/qa/qa/specs/features/project/activity_spec.rb +++ b/qa/qa/specs/features/project/activity_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'activity page', :core do + describe 'activity page' do it 'push creates an event in the activity page' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb index 14642af97ad..24f9f4c77f8 100644 --- a/qa/qa/specs/features/project/add_deploy_key_spec.rb +++ b/qa/qa/specs/features/project/add_deploy_key_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'deploy keys support', :core do + describe 'deploy keys support' do it 'user adds a deploy key' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/project/add_secret_variable_spec.rb b/qa/qa/specs/features/project/add_secret_variable_spec.rb index 32c91dd9d4d..04d9fe488e2 100644 --- a/qa/qa/specs/features/project/add_secret_variable_spec.rb +++ b/qa/qa/specs/features/project/add_secret_variable_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'secret variables support', :core do + describe 'secret variables support' do it 'user adds a secret variable' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb index b3e4889abc0..248669b6046 100644 --- a/qa/qa/specs/features/project/auto_devops_spec.rb +++ b/qa/qa/specs/features/project/auto_devops_spec.rb @@ -1,7 +1,7 @@ require 'pathname' module QA - describe 'Auto Devops', :kubernetes do + describe 'Auto Devops', :orchestrated, :kubernetes do after do @cluster&.remove! end diff --git a/qa/qa/specs/features/project/create_issue_spec.rb b/qa/qa/specs/features/project/create_issue_spec.rb index ac2ed2ca2a1..793e7db87cb 100644 --- a/qa/qa/specs/features/project/create_issue_spec.rb +++ b/qa/qa/specs/features/project/create_issue_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'creates issue', :core do + describe 'creates issue', :smoke do let(:issue_title) { 'issue title' } it 'user creates issue' do diff --git a/qa/qa/specs/features/project/create_spec.rb b/qa/qa/specs/features/project/create_spec.rb index 14ecd464685..5e19e490778 100644 --- a/qa/qa/specs/features/project/create_spec.rb +++ b/qa/qa/specs/features/project/create_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'create a new project', :core do + describe 'create a new project', :smoke do it 'user creates a new project' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb index 054f49b408a..1d099508c24 100644 --- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb +++ b/qa/qa/specs/features/project/deploy_key_clone_spec.rb @@ -1,7 +1,7 @@ require 'digest/sha1' module QA - describe 'cloning code using a deploy key', :core, :docker do + describe 'cloning code using a deploy key', :docker do def login Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/project/fork_project_spec.rb b/qa/qa/specs/features/project/fork_project_spec.rb index 8ad0120305a..d3534d736e4 100644 --- a/qa/qa/specs/features/project/fork_project_spec.rb +++ b/qa/qa/specs/features/project/fork_project_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'Project fork', :core do + describe 'Project fork' do it 'can submit merge requests to upstream master' do Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/project/import_from_github_spec.rb b/qa/qa/specs/features/project/import_from_github_spec.rb index 221b5c27fba..57695d2c726 100644 --- a/qa/qa/specs/features/project/import_from_github_spec.rb +++ b/qa/qa/specs/features/project/import_from_github_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'user imports a GitHub repo', :core, :github do + describe 'user imports a GitHub repo', :orchestrated, :github do let(:imported_project) do Factory::Resource::ProjectImportedFromGithub.fabricate! do |project| project.name = 'imported-project' diff --git a/qa/qa/specs/features/project/pipelines_spec.rb b/qa/qa/specs/features/project/pipelines_spec.rb index ddedde7a8bc..6c6b4e80626 100644 --- a/qa/qa/specs/features/project/pipelines_spec.rb +++ b/qa/qa/specs/features/project/pipelines_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'CI/CD Pipelines', :core, :docker do + describe 'CI/CD Pipelines', :orchestrated, :docker do let(:executor) { "qa-runner-#{Time.now.to_i}" } after do diff --git a/qa/qa/specs/features/project/wikis_spec.rb b/qa/qa/specs/features/project/wikis_spec.rb index 59cc455fffc..9af2dbd1264 100644 --- a/qa/qa/specs/features/project/wikis_spec.rb +++ b/qa/qa/specs/features/project/wikis_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'Wiki Functionality', :core do + describe 'Wiki Functionality' do def login Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.act { sign_in_using_credentials } diff --git a/qa/qa/specs/features/repository/clone_spec.rb b/qa/qa/specs/features/repository/clone_spec.rb index a04ce4e44d9..8b0613c5f78 100644 --- a/qa/qa/specs/features/repository/clone_spec.rb +++ b/qa/qa/specs/features/repository/clone_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'clone code from the repository', :core do + describe 'clone code from the repository' do context 'with regular account over http' do let(:location) do Page::Project::Show.act do diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb index c2de94516d9..aa23145478d 100644 --- a/qa/qa/specs/features/repository/protected_branches_spec.rb +++ b/qa/qa/specs/features/repository/protected_branches_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'branch protection support', :core do + describe 'branch protection support' do let(:branch_name) { 'protected-branch' } let(:commit_message) { 'Protected push commit message' } let(:project) do diff --git a/qa/qa/specs/features/repository/push_spec.rb b/qa/qa/specs/features/repository/push_spec.rb index fc40b60d915..1e89942e932 100644 --- a/qa/qa/specs/features/repository/push_spec.rb +++ b/qa/qa/specs/features/repository/push_spec.rb @@ -1,5 +1,5 @@ module QA - describe 'push code to repository', :core do + describe 'push code to repository' do context 'with regular account over http' do it 'user pushes code to the repository' do Runtime::Browser.visit(:gitlab, Page::Main::Login) diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb index f8f6fe65599..ccb9d5591de 100644 --- a/qa/qa/specs/runner.rb +++ b/qa/qa/specs/runner.rb @@ -14,7 +14,13 @@ module QA def perform args = [] args.push('--tty') if tty - tags.to_a.each { |tag| args.push(['-t', tag.to_s]) } + + if tags.any? + tags.each { |tag| args.push(['-t', tag.to_s]) } + else + args.push(%w[-t ~orchestrated]) + end + args.push(options) Runtime::Browser.configure! diff --git a/qa/spec/scenario/test/instance_spec.rb b/qa/spec/scenario/test/instance/all_spec.rb similarity index 74% rename from qa/spec/scenario/test/instance_spec.rb rename to qa/spec/scenario/test/instance/all_spec.rb index 0d0b534911f..bc0b21c6494 100644 --- a/qa/spec/scenario/test/instance_spec.rb +++ b/qa/spec/scenario/test/instance/all_spec.rb @@ -1,10 +1,4 @@ -describe QA::Scenario::Test::Instance do - subject do - Class.new(described_class) do - tags :rspec - end - end - +describe QA::Scenario::Test::Instance::All do context '#perform' do let(:arguments) { spy('Runtime::Scenario') } let(:release) { spy('Runtime::Release') } @@ -26,16 +20,16 @@ describe QA::Scenario::Test::Instance do end context 'no paths' do - it 'should call runner with default arguments' do + it 'calls runner with default arguments' do subject.perform("test") expect(runner).to have_received(:options=) - .with(::File.expand_path('../../../qa/specs/features', __dir__)) + .with(::File.expand_path('../../../../../qa/specs/features', __dir__)) end end context 'specifying paths' do - it 'should call runner with paths' do + it 'calls runner with paths' do subject.perform('test', 'path1', 'path2') expect(runner).to have_received(:options=).with(%w[path1 path2]) diff --git a/qa/spec/scenario/test/instance/smoke_spec.rb b/qa/spec/scenario/test/instance/smoke_spec.rb new file mode 100644 index 00000000000..66d71610341 --- /dev/null +++ b/qa/spec/scenario/test/instance/smoke_spec.rb @@ -0,0 +1,45 @@ +describe QA::Scenario::Test::Instance::Smoke do + subject { Class.new(described_class) { tags :smoke } } + + context '#perform' do + let(:arguments) { spy('Runtime::Scenario') } + let(:release) { spy('Runtime::Release') } + let(:runner) { spy('Specs::Runner') } + + before do + stub_const('QA::Runtime::Release', release) + stub_const('QA::Runtime::Scenario', arguments) + stub_const('QA::Specs::Runner', runner) + + allow(runner).to receive(:perform).and_yield(runner) + end + + it 'sets an address of the subject' do + subject.perform("hello") + + expect(arguments).to have_received(:define) + .with(:gitlab_address, "hello") + end + + it 'has a smoke tag' do + expect(subject.focus).to eq([:smoke]) # rubocop:disable Focus + end + + context 'no paths' do + it 'calls runner with default arguments' do + subject.perform("test") + + expect(runner).to have_received(:options=) + .with(File.expand_path('../../../../../qa/specs/features', __dir__)) + end + end + + context 'specifying paths' do + it 'calls runner with paths' do + subject.perform('test', 'path1', 'path2') + + expect(runner).to have_received(:options=).with(%w[path1 path2]) + end + end + end +end From a89883b1c91928c6171b309a86ba425965dfbd54 Mon Sep 17 00:00:00 2001 From: Adriel Santiago Date: Mon, 20 Aug 2018 20:03:57 +0000 Subject: [PATCH 27/31] Resolve "Remove redundant header from metrics page" --- app/assets/javascripts/monitoring/components/dashboard.vue | 2 +- app/views/projects/environments/metrics.html.haml | 7 ------- .../50019-remove-redundant-header-from-metrics-page.yml | 5 +++++ 3 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/50019-remove-redundant-header-from-metrics-page.yml diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 6afaefc56f8..ae96ac3b80c 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -172,7 +172,7 @@ export default {