From d97ad73c82e5299fba893cc9b273eced8ec37d3e Mon Sep 17 00:00:00 2001 From: Ryan O'Boyle Date: Wed, 9 Nov 2016 03:35:07 -0500 Subject: [PATCH 001/140] Fix typos in API doc examples --- doc/api/build_triggers.md | 8 ++++---- doc/api/build_variables.md | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/api/build_triggers.md b/doc/api/build_triggers.md index 1b7a1840138..b6459971420 100644 --- a/doc/api/build_triggers.md +++ b/doc/api/build_triggers.md @@ -15,7 +15,7 @@ GET /projects/:id/triggers | `id` | integer | yes | The ID of a project | ``` -curl --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers" ``` ```json @@ -51,7 +51,7 @@ GET /projects/:id/triggers/:token | `token` | string | yes | The `token` of a trigger | ``` -curl --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d" ``` ```json @@ -77,7 +77,7 @@ POST /projects/:id/triggers | `id` | integer | yes | The ID of a project | ``` -curl --request POST --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers" +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers" ``` ```json @@ -104,7 +104,7 @@ DELETE /projects/:id/triggers/:token | `token` | string | yes | The `token` of a trigger | ``` -curl --request DELETE --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d" +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d" ``` ```json diff --git a/doc/api/build_variables.md b/doc/api/build_variables.md index a21751a49ea..917e9773913 100644 --- a/doc/api/build_variables.md +++ b/doc/api/build_variables.md @@ -13,7 +13,7 @@ GET /projects/:id/variables | `id` | integer | yes | The ID of a project | ``` -curl --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables" ``` ```json @@ -43,7 +43,7 @@ GET /projects/:id/variables/:key | `key` | string | yes | The `key` of a variable | ``` -curl --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/TEST_VARIABLE_1" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/TEST_VARIABLE_1" ``` ```json @@ -68,7 +68,7 @@ POST /projects/:id/variables | `value` | string | yes | The `value` of a variable | ``` -curl --request POST --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value" +curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables" --form "key=NEW_VARIABLE" --form "value=new value" ``` ```json @@ -93,7 +93,7 @@ PUT /projects/:id/variables/:key | `value` | string | yes | The `value` of a variable | ``` -curl --request PUT --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/NEW_VARIABLE" --form "value=updated value" +curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/NEW_VARIABLE" --form "value=updated value" ``` ```json @@ -117,7 +117,7 @@ DELETE /projects/:id/variables/:key | `key` | string | yes | The `key` of a variable | ``` -curl --request DELETE --header "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/VARIABLE_1" +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/VARIABLE_1" ``` ```json From c47d8ab69e108ef0cb30463fc2f02fbd1d03409b Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Sat, 19 Nov 2016 15:40:41 +0000 Subject: [PATCH 002/140] Removed leave buttons from settings dropdowns Updated specs --- .../layouts/nav/_group_settings.html.haml | 12 ++------- app/views/layouts/nav/_project.html.haml | 17 +++--------- .../members/_access_request_buttons.html.haml | 26 ++++++++++++------- ...ject-and-leave-group-should-be-buttons.yml | 5 ++++ .../last_owner_cannot_leave_group_spec.rb | 4 +-- .../members/member_leaves_group_spec.rb | 2 +- .../members/user_requests_access_spec.rb | 2 +- ..._member_cannot_leave_group_project_spec.rb | 2 +- ...r_cannot_request_access_to_project_spec.rb | 2 +- .../members/member_leaves_project_spec.rb | 2 +- .../owner_cannot_leave_project_spec.rb | 4 +-- 11 files changed, 36 insertions(+), 42 deletions(-) create mode 100644 changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index c0328fe8842..1579d8f1662 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -1,10 +1,8 @@ - if current_user - can_admin_group = can?(current_user, :admin_group, @group) - can_edit = can?(current_user, :admin_group, @group) - - member = @group.members.find_by(user_id: current_user.id) - - can_leave = member && can?(current_user, :destroy_group_member, member) - - if can_admin_group || can_edit || can_leave + - if can_admin_group || can_edit .controls .dropdown.group-settings-dropdown %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} @@ -14,13 +12,7 @@ - if can_admin_group = nav_link(path: 'groups#projects') do = link_to 'Projects', projects_group_path(@group), title: 'Projects' - - if (can_edit || can_leave) && can_admin_group + - if can_edit && can_admin_group %li.divider - - if can_edit %li = link_to 'Edit Group', edit_group_path(@group) - - if can_leave - %li - = link_to polymorphic_path([:leave, @group, :members]), - data: { confirm: leave_confirmation_message(@group) }, method: :delete, title: 'Leave group' do - Leave Group diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 99a58bbb676..cc24e51b268 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -6,23 +6,14 @@ = icon('caret-down') %ul.dropdown-menu.dropdown-menu-align-right - can_edit = can?(current_user, :admin_project, @project) - -# We don't use @project.team.find_member because it searches for group members too... - - member = @project.members.find_by(user_id: current_user.id) - - can_leave = member && can?(current_user, :destroy_project_member, member) = render 'layouts/nav/project_settings', can_edit: can_edit - - if can_edit || can_leave + - if can_edit %li.divider - - if can_edit - %li - = link_to edit_project_path(@project) do - Edit Project - - if can_leave - %li - = link_to polymorphic_path([:leave, @project, :members]), - data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do - Leave Project + %li + = link_to edit_project_path(@project) do + Edit Project .scrolling-tabs-container{ class: nav_control_class } .fade-left diff --git a/app/views/shared/members/_access_request_buttons.html.haml b/app/views/shared/members/_access_request_buttons.html.haml index eff914398bb..e166dfab710 100644 --- a/app/views/shared/members/_access_request_buttons.html.haml +++ b/app/views/shared/members/_access_request_buttons.html.haml @@ -1,10 +1,16 @@ -- if can?(current_user, :request_access, source) - - if requester = source.requesters.find_by(user_id: current_user.id) - = link_to 'Withdraw Access Request', polymorphic_path([:leave, source, :members]), - method: :delete, - data: { confirm: remove_member_message(requester) }, - class: 'btn' - - else - = link_to 'Request Access', polymorphic_path([:request_access, source, :members]), - method: :post, - class: 'btn' +- model_name = source.model_name.to_s.downcase + +- if can?(current_user, :"destroy_#{model_name}_member", source.members.find_by(user_id: current_user.id)) + = link_to "Leave #{model_name}", polymorphic_path([:leave, source, :members]), + method: :delete, + data: { confirm: leave_confirmation_message(source) }, + class: 'btn' +- elsif requester = source.requesters.find_by(user_id: current_user.id) + = link_to 'Withdraw Access Request', polymorphic_path([:leave, source, :members]), + method: :delete, + data: { confirm: remove_member_message(requester) }, + class: 'btn' +- elsif source.request_access_enabled && can?(current_user, :request_access, source) + = link_to 'Request Access', polymorphic_path([:request_access, source, :members]), + method: :post, + class: 'btn' diff --git a/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml b/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml new file mode 100644 index 00000000000..99dbe4a32a0 --- /dev/null +++ b/changelogs/unreleased/23305-leave-project-and-leave-group-should-be-buttons.yml @@ -0,0 +1,5 @@ +--- +title: Moved Leave Project and Leave Group buttons to access_request_buttons from + the settings dropdown +merge_request: 7600 +author: diff --git a/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb index 33bf6d3752f..be60b0489c7 100644 --- a/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb +++ b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb @@ -10,7 +10,7 @@ feature 'Groups > Members > Last owner cannot leave group', feature: true do visit group_path(group) end - scenario 'user does not see a "Leave Group" link' do - expect(page).not_to have_content 'Leave Group' + scenario 'user does not see a "Leave group" link' do + expect(page).not_to have_content 'Leave group' end end diff --git a/spec/features/groups/members/member_leaves_group_spec.rb b/spec/features/groups/members/member_leaves_group_spec.rb index 3185ff924b9..ac4d94658ae 100644 --- a/spec/features/groups/members/member_leaves_group_spec.rb +++ b/spec/features/groups/members/member_leaves_group_spec.rb @@ -13,7 +13,7 @@ feature 'Groups > Members > Member leaves group', feature: true do end scenario 'user leaves group' do - click_link 'Leave Group' + click_link 'Leave group' expect(current_path).to eq(dashboard_groups_path) expect(group.users.exists?(user.id)).to be_falsey diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb index d8c9c487996..e4b5ea91bd3 100644 --- a/spec/features/groups/members/user_requests_access_spec.rb +++ b/spec/features/groups/members/user_requests_access_spec.rb @@ -29,7 +29,7 @@ feature 'Groups > Members > User requests access', feature: true do expect(page).to have_content 'Your request for access has been queued for review.' expect(page).to have_content 'Withdraw Access Request' - expect(page).not_to have_content 'Leave Group' + expect(page).not_to have_content 'Leave group' end scenario 'user does not see private projects' do diff --git a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb index 728c0e16361..b483ba4c54c 100644 --- a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb +++ b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb @@ -12,6 +12,6 @@ feature 'Projects > Members > Group member cannot leave group project', feature: end scenario 'user does not see a "Leave project" link' do - expect(page).not_to have_content 'Leave Project' + expect(page).not_to have_content 'Leave project' end end diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb index 4973e0aee85..bdeeef57273 100644 --- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb +++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -feature 'Projects > Members > Group requester cannot request access to project', feature: true do +feature 'Projects > Members > Group requester cannot request access to project', feature: true, js: true do let(:user) { create(:user) } let(:owner) { create(:user) } let(:group) { create(:group, :public, :access_requestable) } diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb index 79dec442818..5daa932e4e6 100644 --- a/spec/features/projects/members/member_leaves_project_spec.rb +++ b/spec/features/projects/members/member_leaves_project_spec.rb @@ -11,7 +11,7 @@ feature 'Projects > Members > Member leaves project', feature: true do end scenario 'user leaves project' do - click_link 'Leave Project' + click_link 'Leave project' expect(current_path).to eq(dashboard_projects_path) expect(project.users.exists?(user.id)).to be_falsey diff --git a/spec/features/projects/members/owner_cannot_leave_project_spec.rb b/spec/features/projects/members/owner_cannot_leave_project_spec.rb index 6e948b7a616..b26d55c5d5d 100644 --- a/spec/features/projects/members/owner_cannot_leave_project_spec.rb +++ b/spec/features/projects/members/owner_cannot_leave_project_spec.rb @@ -8,7 +8,7 @@ feature 'Projects > Members > Owner cannot leave project', feature: true do visit namespace_project_path(project.namespace, project) end - scenario 'user does not see a "Leave Project" link' do - expect(page).not_to have_content 'Leave Project' + scenario 'user does not see a "Leave project" link' do + expect(page).not_to have_content 'Leave project' end end From 86f726a5e969fd69c82c94713bc25bd0f0bba598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 1 Dec 2016 16:17:57 +0100 Subject: [PATCH 003/140] Bump Git version requirement to 2.8.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- README.md | 2 +- changelogs/unreleased/update-git-version-in-doc.yml | 4 ++++ doc/install/installation.md | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/update-git-version-in-doc.yml diff --git a/README.md b/README.md index 61204630fd2..68b709b85d5 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ GitLab is a Ruby on Rails application that runs on the following software: - Ubuntu/Debian/CentOS/RHEL - Ruby (MRI) 2.3 -- Git 2.7.4+ +- Git 2.8.4+ - Redis 2.8+ - MySQL or PostgreSQL diff --git a/changelogs/unreleased/update-git-version-in-doc.yml b/changelogs/unreleased/update-git-version-in-doc.yml new file mode 100644 index 00000000000..cb3260f71cd --- /dev/null +++ b/changelogs/unreleased/update-git-version-in-doc.yml @@ -0,0 +1,4 @@ +--- +title: Bump Git version requirement to 2.8.4 +merge_request: +author: diff --git a/doc/install/installation.md b/doc/install/installation.md index 77adb4c9f7b..026e69b8ab6 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -76,7 +76,7 @@ Make sure you have the right version of Git installed # Install Git sudo apt-get install -y git-core - # Make sure Git is version 2.7.4 or higher + # Make sure Git is version 2.8.4 or higher git --version Is the system packaged Git too old? Remove it and compile from source. @@ -89,9 +89,9 @@ Is the system packaged Git too old? Remove it and compile from source. # Download and compile from source cd /tmp - curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.7.4.tar.gz - echo '7104c4f5d948a75b499a954524cb281fe30c6649d8abe20982936f75ec1f275b git-2.7.4.tar.gz' | shasum -a256 -c - && tar -xzf git-2.7.4.tar.gz - cd git-2.7.4/ + curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.8.4.tar.gz + echo '626e319f8a24fc0866167ea5f6bf3e2f38f69d6cb2e59e150f13709ca3ebf301 git-2.8.4.tar.gz' | shasum -a256 -c - && tar -xzf git-2.8.4.tar.gz + cd git-2.8.4/ ./configure make prefix=/usr/local all From fde754e2676e40dcf2600190983ef54030c5d5a5 Mon Sep 17 00:00:00 2001 From: Guyzmo Date: Sat, 26 Nov 2016 16:37:26 +0100 Subject: [PATCH 004/140] API: Endpoint to expose personal snippets as /snippets Adding the necessary API for the new /snippets Restful resource added with this commit. Added a new Grape class `Snippets`, as well as a `PersonalSnippet` entity. Issue: #20042 Merge-Request: !6373 Signed-off-by: Guyzmo --- app/finders/snippets_finder.rb | 5 +- app/helpers/gitlab_routing_helper.rb | 5 + app/policies/personal_snippet_policy.rb | 5 + .../unreleased/features-api-snippets.yml | 4 + doc/api/snippets.md | 232 ++++++++++++++++++ lib/api/api.rb | 1 + lib/api/entities.rb | 13 + lib/api/snippets.rb | 137 +++++++++++ lib/gitlab/url_builder.rb | 2 + spec/finders/snippets_finder_spec.rb | 59 +++-- spec/requests/api/snippets_spec.rb | 157 ++++++++++++ 11 files changed, 594 insertions(+), 26 deletions(-) create mode 100644 changelogs/unreleased/features-api-snippets.yml create mode 100644 doc/api/snippets.md create mode 100644 lib/api/snippets.rb create mode 100644 spec/requests/api/snippets_spec.rb diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index 00ff1611039..0586a923a74 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -1,12 +1,15 @@ class SnippetsFinder def execute(current_user, params = {}) filter = params[:filter] + user = params.fetch(:user, current_user) case filter when :all then snippets(current_user).fresh + when :public then + Snippet.are_public.fresh when :by_user then - by_user(current_user, params[:user], params[:scope]) + by_user(current_user, user, params[:scope]) when :by_project by_project(current_user, params[:project]) end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index af9087d8326..99db73c9ee0 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -159,6 +159,11 @@ module GitlabRoutingHelper resend_invite_namespace_project_project_member_path(project_member.source.namespace, project_member.source, project_member) end + # Snippets + def personal_snippet_url(snippet, *args) + snippet_url(snippet) + end + # Groups ## Members diff --git a/app/policies/personal_snippet_policy.rb b/app/policies/personal_snippet_policy.rb index 46c5aa1a5be..d3913986cd8 100644 --- a/app/policies/personal_snippet_policy.rb +++ b/app/policies/personal_snippet_policy.rb @@ -6,9 +6,14 @@ class PersonalSnippetPolicy < BasePolicy if @subject.author == @user can! :read_personal_snippet can! :update_personal_snippet + can! :destroy_personal_snippet can! :admin_personal_snippet end + unless @user.external? + can! :create_personal_snippet + end + if @subject.internal? && !@user.external? can! :read_personal_snippet end diff --git a/changelogs/unreleased/features-api-snippets.yml b/changelogs/unreleased/features-api-snippets.yml new file mode 100644 index 00000000000..80c7bb75359 --- /dev/null +++ b/changelogs/unreleased/features-api-snippets.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Endpoint to expose personal snippets as /snippets' +merge_request: 6373 +author: Bernard Guyzmo Pratz diff --git a/doc/api/snippets.md b/doc/api/snippets.md new file mode 100644 index 00000000000..5a5dc162ffe --- /dev/null +++ b/doc/api/snippets.md @@ -0,0 +1,232 @@ +# Snippets + +> [Introduced][ce-6373] in GitLab 8.15. + +### Snippet visibility level + +Snippets in GitLab can be either private, internal, or public. +You can set it with the `visibility_level` field in the snippet. + +Constants for snippet visibility levels are: + +| Visibility | Visibility level | Description | +| ---------- | ---------------- | ----------- | +| Private | `0` | The snippet is visible only to the snippet creator | +| Internal | `10` | The snippet is visible for any logged in user | +| Public | `20` | The snippet can be accessed without any authentication | + +## List snippets + +Get a list of current user's snippets. + +``` +GET /snippets +``` + +## Single snippet + +Get a single snippet. + +``` +GET /snippets/:id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | Integer | yes | The ID of a snippet | + +``` bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/snippets/1 +``` + +Example response: + +``` json +{ + "id": 1, + "title": "test", + "file_name": "add.rb", + "author": { + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "name": "John Smith", + "state": "active", + "created_at": "2012-05-23T08:00:58Z" + }, + "expires_at": null, + "updated_at": "2012-06-28T10:52:04Z", + "created_at": "2012-06-28T10:52:04Z", + "web_url": "http://example.com/snippets/1", +} +``` + +## Create new snippet + +Creates a new snippet. The user must have permission to create new snippets. + +``` +POST /snippets +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `title` | String | yes | The title of a snippet | +| `file_name` | String | yes | The name of a snippet file | +| `content` | String | yes | The content of a snippet | +| `visibility_level` | Integer | yes | The snippet's visibility | + + +``` bash +curl --request POST --data '{"title": "This is a snippet", "content": "Hello world", "file_name": "test.txt", "visibility_level": 10 }' --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/snippets +``` + +Example response: + +``` json +{ + "id": 1, + "title": "This is a snippet", + "file_name": "test.txt", + "author": { + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "name": "John Smith", + "state": "active", + "created_at": "2012-05-23T08:00:58Z" + }, + "expires_at": null, + "updated_at": "2012-06-28T10:52:04Z", + "created_at": "2012-06-28T10:52:04Z", + "web_url": "http://example.com/snippets/1", +} +``` + +## Update snippet + +Updates an existing snippet. The user must have permission to change an existing snippet. + +``` +PUT /snippets/:id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | Integer | yes | The ID of a snippet | +| `title` | String | no | The title of a snippet | +| `file_name` | String | no | The name of a snippet file | +| `content` | String | no | The content of a snippet | +| `visibility_level` | Integer | no | The snippet's visibility | + + +``` bash +curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data '{"title": "foo", "content": "bar"}' https://gitlab.example.com/api/v3/snippets/1 +``` + +Example response: + +``` json +{ + "id": 1, + "title": "test", + "file_name": "add.rb", + "author": { + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "name": "John Smith", + "state": "active", + "created_at": "2012-05-23T08:00:58Z" + }, + "expires_at": null, + "updated_at": "2012-06-28T10:52:04Z", + "created_at": "2012-06-28T10:52:04Z", + "web_url": "http://example.com/snippets/1", +} +``` + +## Delete snippet + +Deletes an existing snippet. + +``` +DELETE /snippets/:id +``` + +Parameters: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | Integer | yes | The ID of a snippet | + + +``` +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/snippets/1" +``` + +upon successful delete a `204 No content` HTTP code shall be expected, with no data, +but if the snippet is non-existent, a `404 Not Found` will be returned. + +## Explore all public snippets + +``` +GET /snippets/public +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `per_page` | Integer | no | number of snippets to return per page | +| `page` | Integer | no | the page to retrieve | + +``` bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/snippets/public?per_page=2&page=1 +``` + +Example response: + +``` json +[ + { + "author": { + "avatar_url": "http://www.gravatar.com/avatar/edaf55a9e363ea263e3b981d09e0f7f7?s=80&d=identicon", + "id": 12, + "name": "Libby Rolfson", + "state": "active", + "username": "elton_wehner", + "web_url": "http://localhost:3000/elton_wehner" + }, + "created_at": "2016-11-25T16:53:34.504Z", + "file_name": "oconnerrice.rb", + "id": 49, + "raw_url": "http://localhost:3000/snippets/49/raw", + "title": "Ratione cupiditate et laborum temporibus.", + "updated_at": "2016-11-25T16:53:34.504Z", + "web_url": "http://localhost:3000/snippets/49" + }, + { + "author": { + "avatar_url": "http://www.gravatar.com/avatar/36583b28626de71061e6e5a77972c3bd?s=80&d=identicon", + "id": 16, + "name": "Llewellyn Flatley", + "state": "active", + "username": "adaline", + "web_url": "http://localhost:3000/adaline" + }, + "created_at": "2016-11-25T16:53:34.479Z", + "file_name": "muellershields.rb", + "id": 48, + "raw_url": "http://localhost:3000/snippets/48/raw", + "title": "Minus similique nesciunt vel fugiat qui ullam sunt.", + "updated_at": "2016-11-25T16:53:34.479Z", + "web_url": "http://localhost:3000/snippets/48" + } +] +``` + diff --git a/lib/api/api.rb b/lib/api/api.rb index 67109ceeef9..cec2702e44d 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -64,6 +64,7 @@ module API mount ::API::Session mount ::API::Settings mount ::API::SidekiqMetrics + mount ::API::Snippets mount ::API::Subscriptions mount ::API::SystemHooks mount ::API::Tags diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d5dfb8d00be..dc6d085925d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -200,6 +200,19 @@ module API end end + class PersonalSnippet < Grape::Entity + expose :id, :title, :file_name + expose :author, using: Entities::UserBasic + expose :updated_at, :created_at + + expose :web_url do |snippet| + Gitlab::UrlBuilder.build(snippet) + end + expose :raw_url do |snippet| + Gitlab::UrlBuilder.build(snippet) + "/raw" + end + end + class ProjectEntity < Grape::Entity expose :id, :iid expose(:project_id) { |entity| entity.project.id } diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb new file mode 100644 index 00000000000..e096e636806 --- /dev/null +++ b/lib/api/snippets.rb @@ -0,0 +1,137 @@ +module API + # Snippets API + class Snippets < Grape::API + include PaginationParams + + before { authenticate! } + + resource :snippets do + helpers do + def snippets_for_current_user + SnippetsFinder.new.execute(current_user, filter: :by_user, user: current_user) + end + + def public_snippets + SnippetsFinder.new.execute(current_user, filter: :public) + end + end + + desc 'Get a snippets list for authenticated user' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + use :pagination + end + get do + present paginate(snippets_for_current_user), with: Entities::PersonalSnippet + end + + desc 'List all public snippets current_user has access to' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + use :pagination + end + get 'public' do + present paginate(public_snippets), with: Entities::PersonalSnippet + end + + desc 'Get a single snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + get ':id' do + snippet = snippets_for_current_user.find(params[:id]) + present snippet, with: Entities::PersonalSnippet + end + + desc 'Create new snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :title, type: String, desc: 'The title of a snippet' + requires :file_name, type: String, desc: 'The name of a snippet file' + requires :content, type: String, desc: 'The content of a snippet' + optional :visibility_level, type: Integer, + values: Gitlab::VisibilityLevel.values, + default: Gitlab::VisibilityLevel::INTERNAL, + desc: 'The visibility level of the snippet' + end + post do + attrs = declared_params(include_missing: false) + snippet = CreateSnippetService.new(nil, current_user, attrs).execute + + if snippet.persisted? + present snippet, with: Entities::PersonalSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Update an existing snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + optional :title, type: String, desc: 'The title of a snippet' + optional :file_name, type: String, desc: 'The name of a snippet file' + optional :content, type: String, desc: 'The content of a snippet' + optional :visibility_level, type: Integer, + values: Gitlab::VisibilityLevel.values, + desc: 'The visibility level of the snippet' + at_least_one_of :title, :file_name, :content, :visibility_level + end + put ':id' do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + authorize! :update_personal_snippet, snippet + + attrs = declared_params(include_missing: false) + + UpdateSnippetService.new(nil, current_user, snippet, attrs).execute + if snippet.persisted? + present snippet, with: Entities::PersonalSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Remove snippet' do + detail 'This feature was introduced in GitLab 8.15.' + success Entities::PersonalSnippet + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + delete ':id' do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + authorize! :destroy_personal_snippet, snippet + snippet.destroy + no_content! + end + + desc 'Get a raw snippet' do + detail 'This feature was introduced in GitLab 8.15.' + end + params do + requires :id, type: Integer, desc: 'The ID of a snippet' + end + get ":id/raw" do + snippet = snippets_for_current_user.find_by(id: params.delete(:id)) + return not_found!('Snippet') unless snippet + + env['api.format'] = :txt + content_type 'text/plain' + present snippet.content + end + end + end +end diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index 99d0c28e749..ccb456bcc94 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -24,6 +24,8 @@ module Gitlab wiki_page_url when ProjectSnippet project_snippet_url(object) + when Snippet + personal_snippet_url(object) else raise NotImplementedError.new("No URL builder defined for #{object.class}") end diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb index 28bdc18e840..4427443208a 100644 --- a/spec/finders/snippets_finder_spec.rb +++ b/spec/finders/snippets_finder_spec.rb @@ -9,65 +9,74 @@ describe SnippetsFinder do let(:project2) { create(:empty_project, :private, group: group) } context ':all filter' do - before do - @snippet1 = create(:personal_snippet, :private) - @snippet2 = create(:personal_snippet, :internal) - @snippet3 = create(:personal_snippet, :public) - end + let!(:snippet1) { create(:personal_snippet, :private) } + let!(:snippet2) { create(:personal_snippet, :internal) } + let!(:snippet3) { create(:personal_snippet, :public) } it "returns all private and internal snippets" do snippets = SnippetsFinder.new.execute(user, filter: :all) - expect(snippets).to include(@snippet2, @snippet3) - expect(snippets).not_to include(@snippet1) + expect(snippets).to include(snippet2, snippet3) + expect(snippets).not_to include(snippet1) end it "returns all public snippets" do snippets = SnippetsFinder.new.execute(nil, filter: :all) - expect(snippets).to include(@snippet3) - expect(snippets).not_to include(@snippet1, @snippet2) + expect(snippets).to include(snippet3) + expect(snippets).not_to include(snippet1, snippet2) + end + end + + context ':public filter' do + let!(:snippet1) { create(:personal_snippet, :private) } + let!(:snippet2) { create(:personal_snippet, :internal) } + let!(:snippet3) { create(:personal_snippet, :public) } + + it "returns public public snippets" do + snippets = SnippetsFinder.new.execute(nil, filter: :public) + + expect(snippets).to include(snippet3) + expect(snippets).not_to include(snippet1, snippet2) end end context ':by_user filter' do - before do - @snippet1 = create(:personal_snippet, :private, author: user) - @snippet2 = create(:personal_snippet, :internal, author: user) - @snippet3 = create(:personal_snippet, :public, author: user) - end + let!(:snippet1) { create(:personal_snippet, :private, author: user) } + let!(:snippet2) { create(:personal_snippet, :internal, author: user) } + let!(:snippet3) { create(:personal_snippet, :public, author: user) } it "returns all public and internal snippets" do snippets = SnippetsFinder.new.execute(user1, filter: :by_user, user: user) - expect(snippets).to include(@snippet2, @snippet3) - expect(snippets).not_to include(@snippet1) + expect(snippets).to include(snippet2, snippet3) + expect(snippets).not_to include(snippet1) end it "returns internal snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_internal") - expect(snippets).to include(@snippet2) - expect(snippets).not_to include(@snippet1, @snippet3) + expect(snippets).to include(snippet2) + expect(snippets).not_to include(snippet1, snippet3) end it "returns private snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_private") - expect(snippets).to include(@snippet1) - expect(snippets).not_to include(@snippet2, @snippet3) + expect(snippets).to include(snippet1) + expect(snippets).not_to include(snippet2, snippet3) end it "returns public snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user, scope: "are_public") - expect(snippets).to include(@snippet3) - expect(snippets).not_to include(@snippet1, @snippet2) + expect(snippets).to include(snippet3) + expect(snippets).not_to include(snippet1, snippet2) end it "returns all snippets" do snippets = SnippetsFinder.new.execute(user, filter: :by_user, user: user) - expect(snippets).to include(@snippet1, @snippet2, @snippet3) + expect(snippets).to include(snippet1, snippet2, snippet3) end it "returns only public snippets if unauthenticated user" do snippets = SnippetsFinder.new.execute(nil, filter: :by_user, user: user) - expect(snippets).to include(@snippet3) - expect(snippets).not_to include(@snippet2, @snippet1) + expect(snippets).to include(snippet3) + expect(snippets).not_to include(snippet2, snippet1) end end diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb new file mode 100644 index 00000000000..f6fb6ea5506 --- /dev/null +++ b/spec/requests/api/snippets_spec.rb @@ -0,0 +1,157 @@ +require 'rails_helper' + +describe API::Snippets, api: true do + include ApiHelpers + let!(:user) { create(:user) } + + describe 'GET /snippets/' do + it 'returns snippets available' do + public_snippet = create(:personal_snippet, :public, author: user) + private_snippet = create(:personal_snippet, :private, author: user) + internal_snippet = create(:personal_snippet, :internal, author: user) + + get api("/snippets/", user) + + expect(response).to have_http_status(200) + expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly( + public_snippet.id, + internal_snippet.id, + private_snippet.id) + expect(json_response.last).to have_key('web_url') + expect(json_response.last).to have_key('raw_url') + end + + it 'hides private snippets from regular user' do + create(:personal_snippet, :private) + + get api("/snippets/", user) + expect(response).to have_http_status(200) + expect(json_response.size).to eq(0) + end + end + + describe 'GET /snippets/public' do + let!(:other_user) { create(:user) } + let!(:public_snippet) { create(:personal_snippet, :public, author: user) } + let!(:private_snippet) { create(:personal_snippet, :private, author: user) } + let!(:internal_snippet) { create(:personal_snippet, :internal, author: user) } + let!(:public_snippet_other) { create(:personal_snippet, :public, author: other_user) } + let!(:private_snippet_other) { create(:personal_snippet, :private, author: other_user) } + let!(:internal_snippet_other) { create(:personal_snippet, :internal, author: other_user) } + + it 'returns all snippets with public visibility from all users' do + get api("/snippets/public", user) + + expect(response).to have_http_status(200) + expect(json_response.map { |snippet| snippet['id']} ).to contain_exactly( + public_snippet.id, + public_snippet_other.id) + expect(json_response.map{ |snippet| snippet['web_url']} ).to include( + "http://localhost/snippets/#{public_snippet.id}", + "http://localhost/snippets/#{public_snippet_other.id}") + expect(json_response.map{ |snippet| snippet['raw_url']} ).to include( + "http://localhost/snippets/#{public_snippet.id}/raw", + "http://localhost/snippets/#{public_snippet_other.id}/raw") + end + end + + describe 'GET /snippets/:id/raw' do + let(:snippet) { create(:personal_snippet, author: user) } + + it 'returns raw text' do + get api("/snippets/#{snippet.id}/raw", user) + + expect(response).to have_http_status(200) + expect(response.content_type).to eq 'text/plain' + expect(response.body).to eq(snippet.content) + end + + it 'returns 404 for invalid snippet id' do + delete api("/snippets/1234", user) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Snippet Not Found') + end + end + + describe 'POST /snippets/' do + let(:params) do + { + title: 'Test Title', + file_name: 'test.rb', + content: 'puts "hello world"', + visibility_level: Gitlab::VisibilityLevel::PUBLIC + } + end + + it 'creates a new snippet' do + expect do + post api("/snippets/", user), params + end.to change { PersonalSnippet.count }.by(1) + + expect(response).to have_http_status(201) + expect(json_response['title']).to eq(params[:title]) + expect(json_response['file_name']).to eq(params[:file_name]) + end + + it 'returns 400 for missing parameters' do + params.delete(:title) + + post api("/snippets/", user), params + + expect(response).to have_http_status(400) + end + end + + describe 'PUT /snippets/:id' do + let(:other_user) { create(:user) } + let(:public_snippet) { create(:personal_snippet, :public, author: user) } + it 'updates snippet' do + new_content = 'New content' + + put api("/snippets/#{public_snippet.id}", user), content: new_content + + expect(response).to have_http_status(200) + public_snippet.reload + expect(public_snippet.content).to eq(new_content) + end + + it 'returns 404 for invalid snippet id' do + put api("/snippets/1234", user), title: 'foo' + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Snippet Not Found') + end + + it "returns 404 for another user's snippet" do + put api("/snippets/#{public_snippet.id}", other_user), title: 'fubar' + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Snippet Not Found') + end + + it 'returns 400 for missing parameters' do + put api("/snippets/1234", user) + + expect(response).to have_http_status(400) + end + end + + describe 'DELETE /snippets/:id' do + let!(:public_snippet) { create(:personal_snippet, :public, author: user) } + it 'deletes snippet' do + expect do + delete api("/snippets/#{public_snippet.id}", user) + + expect(response).to have_http_status(204) + end.to change { PersonalSnippet.count }.by(-1) + end + + it 'returns 404 for invalid snippet id' do + delete api("/snippets/1234", user) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Snippet Not Found') + end + end +end From 48d89919e6a0eebc43780c41a4d789cdcb716bb5 Mon Sep 17 00:00:00 2001 From: Sam Carrington Date: Thu, 1 Dec 2016 18:08:37 +0000 Subject: [PATCH 005/140] Update php.md - /.dockerinit was removed in v1.11 so the test always results in an early exit https://github.com/docker/docker/pull/19490 --- doc/ci/examples/php.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md index 175e9d79904..82ffb841729 100644 --- a/doc/ci/examples/php.md +++ b/doc/ci/examples/php.md @@ -40,7 +40,7 @@ repository with the following content: #!/bin/bash # We need to install dependencies only for Docker -[[ ! -e /.dockerenv ]] && [[ ! -e /.dockerinit ]] && exit 0 +[[ ! -e /.dockerenv ]] && exit 0 set -xe From 8235f8301571a9b55a14b94b2ed687e53cbc2f4e Mon Sep 17 00:00:00 2001 From: Jacopo Date: Sun, 6 Nov 2016 19:41:23 +0100 Subject: [PATCH 006/140] Fixed Wrong Tab Selected When Loggin Fails And Multiple Login Tabs Exists When ldap is enabled and use "Standard" authentication method, if authentication fails the correct tab remain selected. This is done by saving into localStorage when the active tab changes and by always selecting that tab when the page is loaded. --- .eslintrc | 3 +- app/assets/javascripts/dispatcher.js.es6 | 1 + .../javascripts/signin_tabs_memoizer.js.es6 | 49 +++++++++++++++++ app/views/devise/sessions/new.html.haml | 1 + .../15081-wrong-login-tab-ldap-frontend.yml | 4 ++ .../fixtures/signin_tabs.html.haml | 5 ++ .../signin_tabs_memoizer_spec.js.es6 | 53 +++++++++++++++++++ 7 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/signin_tabs_memoizer.js.es6 create mode 100644 changelogs/unreleased/15081-wrong-login-tab-ldap-frontend.yml create mode 100644 spec/javascripts/fixtures/signin_tabs.html.haml create mode 100644 spec/javascripts/signin_tabs_memoizer_spec.js.es6 diff --git a/.eslintrc b/.eslintrc index b80dcec9d1d..e13f76b213c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,7 +8,8 @@ "globals": { "_": false, "gl": false, - "gon": false + "gon": false, + "localStorage": false }, "plugins": [ "filenames" diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index 16df4b0b005..fb366e2eb88 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -24,6 +24,7 @@ switch (page) { case 'sessions:new': new UsernameValidator(); + new ActiveTabMemoizer(); break; case 'projects:boards:show': case 'projects:boards:index': diff --git a/app/assets/javascripts/signin_tabs_memoizer.js.es6 b/app/assets/javascripts/signin_tabs_memoizer.js.es6 new file mode 100644 index 00000000000..d811d1cd53a --- /dev/null +++ b/app/assets/javascripts/signin_tabs_memoizer.js.es6 @@ -0,0 +1,49 @@ +/* eslint no-param-reassign: ["error", { "props": false }]*/ +/* eslint no-new: "off" */ +((global) => { + /** + * Memorize the last selected tab after reloading a page. + * Does that setting the current selected tab in the localStorage + */ + class ActiveTabMemoizer { + constructor({ currentTabKey = 'current_signin_tab', tabSelector = 'ul.nav-tabs' } = {}) { + this.currentTabKey = currentTabKey; + this.tabSelector = tabSelector; + this.bootstrap(); + } + + bootstrap() { + const tabs = document.querySelectorAll(this.tabSelector); + if (tabs.length > 0) { + tabs[0].addEventListener('click', (e) => { + if (e.target && e.target.nodeName === 'A') { + const anchorName = e.target.getAttribute('href'); + this.saveData(anchorName); + } + }); + } + + this.showTab(); + } + + showTab() { + const anchorName = this.readData(); + if (anchorName) { + const tab = document.querySelector(`${this.tabSelector} a[href="${anchorName}"]`); + if (tab) { + tab.click(); + } + } + } + + saveData(val) { + localStorage.setItem(this.currentTabKey, val); + } + + readData() { + return localStorage.getItem(this.currentTabKey); + } + } + + global.ActiveTabMemoizer = ActiveTabMemoizer; +})(window); diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml index fa8e7979461..af87129e49e 100644 --- a/app/views/devise/sessions/new.html.haml +++ b/app/views/devise/sessions/new.html.haml @@ -1,4 +1,5 @@ - page_title "Sign in" + %div - if form_based_providers.any? = render 'devise/shared/tabs_ldap' diff --git a/changelogs/unreleased/15081-wrong-login-tab-ldap-frontend.yml b/changelogs/unreleased/15081-wrong-login-tab-ldap-frontend.yml new file mode 100644 index 00000000000..19c76b5b437 --- /dev/null +++ b/changelogs/unreleased/15081-wrong-login-tab-ldap-frontend.yml @@ -0,0 +1,4 @@ +--- +title: Fix wrong tab selected when loggin fails and multiple login tabs exists +merge_request: 7314 +author: Jacopo Beschi @jacopo-beschi diff --git a/spec/javascripts/fixtures/signin_tabs.html.haml b/spec/javascripts/fixtures/signin_tabs.html.haml new file mode 100644 index 00000000000..12b8d423cbe --- /dev/null +++ b/spec/javascripts/fixtures/signin_tabs.html.haml @@ -0,0 +1,5 @@ +%ul.nav-tabs + %li + %a.active{ id: 'standard', href: '#standard'} Standard + %li + %a{ id: 'ldap', href: '#ldap'} Ldap diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js.es6 b/spec/javascripts/signin_tabs_memoizer_spec.js.es6 new file mode 100644 index 00000000000..9a9fb22255b --- /dev/null +++ b/spec/javascripts/signin_tabs_memoizer_spec.js.es6 @@ -0,0 +1,53 @@ +/*= require signin_tabs_memoizer */ + +((global) => { + describe('SigninTabsMemoizer', () => { + const fixtureTemplate = 'signin_tabs.html'; + const tabSelector = 'ul.nav-tabs'; + const currentTabKey = 'current_signin_tab'; + let memo; + + function createMemoizer() { + memo = new global.ActiveTabMemoizer({ + currentTabKey, + tabSelector, + }); + return memo; + } + + fixture.preload(fixtureTemplate); + + beforeEach(() => { + fixture.load(fixtureTemplate); + }); + + it('does nothing if no tab was previously selected', () => { + createMemoizer(); + + expect(document.querySelector('li a.active').getAttribute('id')).toEqual('standard'); + }); + + it('shows last selected tab on boot', () => { + createMemoizer().saveData('#ldap'); + const fakeTab = { + click: () => {}, + }; + spyOn(document, 'querySelector').and.returnValue(fakeTab); + spyOn(fakeTab, 'click'); + + memo.bootstrap(); + + // verify that triggers click on the last selected tab + expect(document.querySelector).toHaveBeenCalledWith(`${tabSelector} a[href="#ldap"]`); + expect(fakeTab.click).toHaveBeenCalled(); + }); + + it('saves last selected tab on change', () => { + createMemoizer(); + + document.getElementById('standard').click(); + + expect(memo.readData()).toEqual('#standard'); + }); + }); +})(window); From be797097df258f306dca1cf821b7ed9b46e7919f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 11:17:48 +0100 Subject: [PATCH 007/140] Add scaffold for each class of core CI status --- lib/gitlab/ci/status/core/canceled.rb | 8 ++++++++ lib/gitlab/ci/status/core/created.rb | 8 ++++++++ lib/gitlab/ci/status/core/failed.rb | 8 ++++++++ lib/gitlab/ci/status/core/pending.rb | 8 ++++++++ lib/gitlab/ci/status/core/running.rb | 8 ++++++++ lib/gitlab/ci/status/core/skipped.rb | 8 ++++++++ lib/gitlab/ci/status/core/success.rb | 8 ++++++++ 7 files changed, 56 insertions(+) create mode 100644 lib/gitlab/ci/status/core/canceled.rb create mode 100644 lib/gitlab/ci/status/core/created.rb create mode 100644 lib/gitlab/ci/status/core/failed.rb create mode 100644 lib/gitlab/ci/status/core/pending.rb create mode 100644 lib/gitlab/ci/status/core/running.rb create mode 100644 lib/gitlab/ci/status/core/skipped.rb create mode 100644 lib/gitlab/ci/status/core/success.rb diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/core/canceled.rb new file mode 100644 index 00000000000..3dcddd6e3ef --- /dev/null +++ b/lib/gitlab/ci/status/core/canceled.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Canceled + end + end + end +end diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/core/created.rb new file mode 100644 index 00000000000..590f14d6b57 --- /dev/null +++ b/lib/gitlab/ci/status/core/created.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Created + end + end + end +end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/core/failed.rb new file mode 100644 index 00000000000..d5af40b53cb --- /dev/null +++ b/lib/gitlab/ci/status/core/failed.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Failed + end + end + end +end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/core/pending.rb new file mode 100644 index 00000000000..ef57886234e --- /dev/null +++ b/lib/gitlab/ci/status/core/pending.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Pending + end + end + end +end diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/core/running.rb new file mode 100644 index 00000000000..0b027f4dc9c --- /dev/null +++ b/lib/gitlab/ci/status/core/running.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Running + end + end + end +end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/core/skipped.rb new file mode 100644 index 00000000000..b8b07a69156 --- /dev/null +++ b/lib/gitlab/ci/status/core/skipped.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Skipped + end + end + end +end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb new file mode 100644 index 00000000000..511a34901f8 --- /dev/null +++ b/lib/gitlab/ci/status/core/success.rb @@ -0,0 +1,8 @@ +module Gitlab::Ci + module Status + module Core + class Success + end + end + end +end From 5df3e8b81b4115f44b1ee56a3531dca6d912756c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 12:14:10 +0100 Subject: [PATCH 008/140] Add initial implmentation for core success status --- lib/gitlab/ci/status/core/success.rb | 7 +++++++ spec/lib/gitlab/ci/status/core/success_spec.rb | 11 +++++++++++ 2 files changed, 18 insertions(+) create mode 100644 spec/lib/gitlab/ci/status/core/success_spec.rb diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index 511a34901f8..7bae4f612cc 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -2,6 +2,13 @@ module Gitlab::Ci module Status module Core class Success + def label + 'passed' + end + + def icon + 'success' + end end end end diff --git a/spec/lib/gitlab/ci/status/core/success_spec.rb b/spec/lib/gitlab/ci/status/core/success_spec.rb new file mode 100644 index 00000000000..80587956336 --- /dev/null +++ b/spec/lib/gitlab/ci/status/core/success_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Core::Success do + describe '#label' do + it { expect(subject.label).to eq 'passed' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'success' } + end +end From d4ed5b2e0c7fc94309499a0a268c543a82e00e9b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 12:38:07 +0100 Subject: [PATCH 009/140] Add abstract base class for core CI status --- lib/gitlab/ci/status/core/base.rb | 41 +++++++++++++++++++ lib/gitlab/ci/status/core/success.rb | 2 +- .../lib/gitlab/ci/status/core/success_spec.rb | 2 + 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/ci/status/core/base.rb diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core/base.rb new file mode 100644 index 00000000000..96d09dcbfc5 --- /dev/null +++ b/lib/gitlab/ci/status/core/base.rb @@ -0,0 +1,41 @@ +module Gitlab::Ci + module Status + module Core + # Base abstract class fore core status + # + class Base + def initialize(subject) + @subject = subject + end + + def icon + raise NotImplementedError + end + + def label + raise NotImplementedError + end + + def has_details? + raise NotImplementedError + end + + def details_path + raise NotImplementedError + end + + def has_action? + raise NotImplementedError + end + + def action_icon + raise NotImplementedError + end + + def action_path + raise NotImplementedError + end + end + end + end +end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index 7bae4f612cc..e32a5228619 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -1,7 +1,7 @@ module Gitlab::Ci module Status module Core - class Success + class Success < Core::Base def label 'passed' end diff --git a/spec/lib/gitlab/ci/status/core/success_spec.rb b/spec/lib/gitlab/ci/status/core/success_spec.rb index 80587956336..93a656a46cd 100644 --- a/spec/lib/gitlab/ci/status/core/success_spec.rb +++ b/spec/lib/gitlab/ci/status/core/success_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Success do + subject { described_class.new(double('subject')) } + describe '#label' do it { expect(subject.label).to eq 'passed' } end From 943b3d0e0007d2f48a64ffdef6bf0ff0fcb774f2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 13:08:21 +0100 Subject: [PATCH 010/140] Implement the rest of core CI statuses with specs --- lib/gitlab/ci/status/core/base.rb | 4 ++++ lib/gitlab/ci/status/core/canceled.rb | 9 ++++++++- lib/gitlab/ci/status/core/created.rb | 9 ++++++++- lib/gitlab/ci/status/core/failed.rb | 9 ++++++++- lib/gitlab/ci/status/core/pending.rb | 9 ++++++++- lib/gitlab/ci/status/core/running.rb | 9 ++++++++- lib/gitlab/ci/status/core/skipped.rb | 9 ++++++++- lib/gitlab/ci/status/core/success.rb | 2 +- spec/lib/gitlab/ci/status/core/canceled_spec.rb | 17 +++++++++++++++++ spec/lib/gitlab/ci/status/core/created_spec.rb | 17 +++++++++++++++++ spec/lib/gitlab/ci/status/core/failed_spec.rb | 17 +++++++++++++++++ spec/lib/gitlab/ci/status/core/pending_spec.rb | 17 +++++++++++++++++ spec/lib/gitlab/ci/status/core/running_spec.rb | 17 +++++++++++++++++ spec/lib/gitlab/ci/status/core/skipped_spec.rb | 17 +++++++++++++++++ spec/lib/gitlab/ci/status/core/success_spec.rb | 6 +++++- 15 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 spec/lib/gitlab/ci/status/core/canceled_spec.rb create mode 100644 spec/lib/gitlab/ci/status/core/created_spec.rb create mode 100644 spec/lib/gitlab/ci/status/core/failed_spec.rb create mode 100644 spec/lib/gitlab/ci/status/core/pending_spec.rb create mode 100644 spec/lib/gitlab/ci/status/core/running_spec.rb create mode 100644 spec/lib/gitlab/ci/status/core/skipped_spec.rb diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core/base.rb index 96d09dcbfc5..f7687c49ffd 100644 --- a/lib/gitlab/ci/status/core/base.rb +++ b/lib/gitlab/ci/status/core/base.rb @@ -16,6 +16,10 @@ module Gitlab::Ci raise NotImplementedError end + def title + "#{@subject.class.name.demodulize}: #{label}" + end + def has_details? raise NotImplementedError end diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/core/canceled.rb index 3dcddd6e3ef..5e06b946a99 100644 --- a/lib/gitlab/ci/status/core/canceled.rb +++ b/lib/gitlab/ci/status/core/canceled.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Canceled + class Canceled < Core::Base + def label + 'canceled' + end + + def icon + 'icon_status_canceled' + end end end end diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/core/created.rb index 590f14d6b57..c116f9a97f1 100644 --- a/lib/gitlab/ci/status/core/created.rb +++ b/lib/gitlab/ci/status/core/created.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Created + class Created < Core::Base + def label + 'created' + end + + def icon + 'icon_status_created' + end end end end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/core/failed.rb index d5af40b53cb..467ef71e819 100644 --- a/lib/gitlab/ci/status/core/failed.rb +++ b/lib/gitlab/ci/status/core/failed.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Failed + class Failed < Core::Base + def label + 'failed' + end + + def icon + 'icon_status_failed' + end end end end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/core/pending.rb index ef57886234e..05c9e41091b 100644 --- a/lib/gitlab/ci/status/core/pending.rb +++ b/lib/gitlab/ci/status/core/pending.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Pending + class Pending < Core::Base + def label + 'pending' + end + + def icon + 'icon_status_pending' + end end end end diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/core/running.rb index 0b027f4dc9c..01f0c57ef5f 100644 --- a/lib/gitlab/ci/status/core/running.rb +++ b/lib/gitlab/ci/status/core/running.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Running + class Running < Core::Base + def label + 'running' + end + + def icon + 'icon_status_running' + end end end end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/core/skipped.rb index b8b07a69156..e791341b7e0 100644 --- a/lib/gitlab/ci/status/core/skipped.rb +++ b/lib/gitlab/ci/status/core/skipped.rb @@ -1,7 +1,14 @@ module Gitlab::Ci module Status module Core - class Skipped + class Skipped < Core::Base + def label + 'skipped' + end + + def icon + 'icon_status_skipped' + end end end end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index e32a5228619..bcfe7e63a6c 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -7,7 +7,7 @@ module Gitlab::Ci end def icon - 'success' + 'icon_status_success' end end end diff --git a/spec/lib/gitlab/ci/status/core/canceled_spec.rb b/spec/lib/gitlab/ci/status/core/canceled_spec.rb new file mode 100644 index 00000000000..d03b7a18aa0 --- /dev/null +++ b/spec/lib/gitlab/ci/status/core/canceled_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Core::Canceled do + subject { described_class.new(double('subject')) } + + describe '#label' do + it { expect(subject.label).to eq 'canceled' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'icon_status_canceled' } + end + + describe '#title' do + it { expect(subject.title).to eq 'Double: canceled' } + end +end diff --git a/spec/lib/gitlab/ci/status/core/created_spec.rb b/spec/lib/gitlab/ci/status/core/created_spec.rb new file mode 100644 index 00000000000..5a6d7523e83 --- /dev/null +++ b/spec/lib/gitlab/ci/status/core/created_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Core::Created do + subject { described_class.new(double('subject')) } + + describe '#label' do + it { expect(subject.label).to eq 'created' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'icon_status_created' } + end + + describe '#title' do + it { expect(subject.title).to eq 'Double: created' } + end +end diff --git a/spec/lib/gitlab/ci/status/core/failed_spec.rb b/spec/lib/gitlab/ci/status/core/failed_spec.rb new file mode 100644 index 00000000000..0b7e03f1684 --- /dev/null +++ b/spec/lib/gitlab/ci/status/core/failed_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Core::Failed do + subject { described_class.new(double('subject')) } + + describe '#label' do + it { expect(subject.label).to eq 'failed' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'icon_status_failed' } + end + + describe '#title' do + it { expect(subject.title).to eq 'Double: failed' } + end +end diff --git a/spec/lib/gitlab/ci/status/core/pending_spec.rb b/spec/lib/gitlab/ci/status/core/pending_spec.rb new file mode 100644 index 00000000000..95f2255e66d --- /dev/null +++ b/spec/lib/gitlab/ci/status/core/pending_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Core::Pending do + subject { described_class.new(double('subject')) } + + describe '#label' do + it { expect(subject.label).to eq 'pending' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'icon_status_pending' } + end + + describe '#title' do + it { expect(subject.title).to eq 'Double: pending' } + end +end diff --git a/spec/lib/gitlab/ci/status/core/running_spec.rb b/spec/lib/gitlab/ci/status/core/running_spec.rb new file mode 100644 index 00000000000..648de678618 --- /dev/null +++ b/spec/lib/gitlab/ci/status/core/running_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Core::Running do + subject { described_class.new(double('subject')) } + + describe '#label' do + it { expect(subject.label).to eq 'running' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'icon_status_running' } + end + + describe '#title' do + it { expect(subject.title).to eq 'Double: running' } + end +end diff --git a/spec/lib/gitlab/ci/status/core/skipped_spec.rb b/spec/lib/gitlab/ci/status/core/skipped_spec.rb new file mode 100644 index 00000000000..dc2b56a1e34 --- /dev/null +++ b/spec/lib/gitlab/ci/status/core/skipped_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Core::Skipped do + subject { described_class.new(double('subject')) } + + describe '#label' do + it { expect(subject.label).to eq 'skipped' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'icon_status_skipped' } + end + + describe '#title' do + it { expect(subject.title).to eq 'Double: skipped' } + end +end diff --git a/spec/lib/gitlab/ci/status/core/success_spec.rb b/spec/lib/gitlab/ci/status/core/success_spec.rb index 93a656a46cd..fc67e58adb5 100644 --- a/spec/lib/gitlab/ci/status/core/success_spec.rb +++ b/spec/lib/gitlab/ci/status/core/success_spec.rb @@ -8,6 +8,10 @@ describe Gitlab::Ci::Status::Core::Success do end describe '#icon' do - it { expect(subject.icon).to eq 'success' } + it { expect(subject.icon).to eq 'icon_status_success' } + end + + describe '#title' do + it { expect(subject.title).to eq 'Double: passed' } end end From 119757ac9c2e968a4e97bd3a7f7d7a783456da83 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 13:27:26 +0100 Subject: [PATCH 011/140] Add scaffold for remaining statuses-related classes --- lib/gitlab/ci/status/extended/base.rb | 11 +++++++++ .../ci/status/extended/pipeline/common.rb | 24 +++++++++++++++++++ .../pipeline/success_with_warnings.rb | 23 ++++++++++++++++++ lib/gitlab/ci/status/factory.rb | 6 +++++ .../gitlab/ci/status/extended/base_spec.rb | 0 .../status/extended/pipeline/common_spec.rb | 0 .../pipeline/success_with_warnings_spec.rb | 0 spec/lib/gitlab/ci/status/factory_spec.rb | 0 8 files changed, 64 insertions(+) create mode 100644 lib/gitlab/ci/status/extended/base.rb create mode 100644 lib/gitlab/ci/status/extended/pipeline/common.rb create mode 100644 lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb create mode 100644 lib/gitlab/ci/status/factory.rb create mode 100644 spec/lib/gitlab/ci/status/extended/base_spec.rb create mode 100644 spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb create mode 100644 spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb create mode 100644 spec/lib/gitlab/ci/status/factory_spec.rb diff --git a/lib/gitlab/ci/status/extended/base.rb b/lib/gitlab/ci/status/extended/base.rb new file mode 100644 index 00000000000..1d7819c6891 --- /dev/null +++ b/lib/gitlab/ci/status/extended/base.rb @@ -0,0 +1,11 @@ +module Gitlab::Ci + module Status + module Extended + module Base + def matches?(_subject) + raise NotImplementedError + end + end + end + end +end diff --git a/lib/gitlab/ci/status/extended/pipeline/common.rb b/lib/gitlab/ci/status/extended/pipeline/common.rb new file mode 100644 index 00000000000..75d392fab6c --- /dev/null +++ b/lib/gitlab/ci/status/extended/pipeline/common.rb @@ -0,0 +1,24 @@ +module Gitlab::Ci + module Status + module Extended + module Pipeline + module Common + def initialize(pipeline) + @pipeline = pipeline + end + + def has_details? + true + end + + def details_path + end + + def has_action? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb new file mode 100644 index 00000000000..5e92bb97eec --- /dev/null +++ b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb @@ -0,0 +1,23 @@ +module Gitlab::Ci + module Status + module Extended + module Pipeline + class SuccessWithWarnings < SimpleDelegator + extend Status::Extended::Base + + def label + 'passed with warnings' + end + + def icon + 'icon_status_warning' + end + + def self.matches?(pipeline) + pipeline.success? && pipeline.has_warnings? + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb new file mode 100644 index 00000000000..212cd1a1687 --- /dev/null +++ b/lib/gitlab/ci/status/factory.rb @@ -0,0 +1,6 @@ +module Gitlab::Ci + module Status + class Factory + end + end +end diff --git a/spec/lib/gitlab/ci/status/extended/base_spec.rb b/spec/lib/gitlab/ci/status/extended/base_spec.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb new file mode 100644 index 00000000000..e69de29bb2d From 0c7168b98d69dc4071873a2787e4974a4c24ea20 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 13:56:33 +0100 Subject: [PATCH 012/140] Add information about badge test to core statuses --- lib/gitlab/ci/status/core/canceled.rb | 4 ++++ lib/gitlab/ci/status/core/created.rb | 4 ++++ lib/gitlab/ci/status/core/failed.rb | 4 ++++ lib/gitlab/ci/status/core/pending.rb | 4 ++++ lib/gitlab/ci/status/core/running.rb | 4 ++++ lib/gitlab/ci/status/core/skipped.rb | 4 ++++ lib/gitlab/ci/status/core/success.rb | 4 ++++ .../ci/status/extended/pipeline/success_with_warnings.rb | 4 ++++ spec/lib/gitlab/ci/status/core/canceled_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/core/created_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/core/failed_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/core/pending_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/core/running_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/core/skipped_spec.rb | 4 ++++ spec/lib/gitlab/ci/status/core/success_spec.rb | 4 ++++ 15 files changed, 60 insertions(+) diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/core/canceled.rb index 5e06b946a99..a05ac8ee3cc 100644 --- a/lib/gitlab/ci/status/core/canceled.rb +++ b/lib/gitlab/ci/status/core/canceled.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Canceled < Core::Base + def text + 'canceled' + end + def label 'canceled' end diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/core/created.rb index c116f9a97f1..ee8bf2e8dac 100644 --- a/lib/gitlab/ci/status/core/created.rb +++ b/lib/gitlab/ci/status/core/created.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Created < Core::Base + def text + 'created' + end + def label 'created' end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/core/failed.rb index 467ef71e819..ea1615853c0 100644 --- a/lib/gitlab/ci/status/core/failed.rb +++ b/lib/gitlab/ci/status/core/failed.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Failed < Core::Base + def text + 'failed' + end + def label 'failed' end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/core/pending.rb index 05c9e41091b..95fbb710735 100644 --- a/lib/gitlab/ci/status/core/pending.rb +++ b/lib/gitlab/ci/status/core/pending.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Pending < Core::Base + def text + 'pending' + end + def label 'pending' end diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/core/running.rb index 01f0c57ef5f..5580c1a5154 100644 --- a/lib/gitlab/ci/status/core/running.rb +++ b/lib/gitlab/ci/status/core/running.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Running < Core::Base + def text + 'running' + end + def label 'running' end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/core/skipped.rb index e791341b7e0..0e8e42f525b 100644 --- a/lib/gitlab/ci/status/core/skipped.rb +++ b/lib/gitlab/ci/status/core/skipped.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Skipped < Core::Base + def text + 'skipped' + end + def label 'skipped' end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/core/success.rb index bcfe7e63a6c..7efafdb615f 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/core/success.rb @@ -2,6 +2,10 @@ module Gitlab::Ci module Status module Core class Success < Core::Base + def text + 'passed' + end + def label 'passed' end diff --git a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb index 5e92bb97eec..8f1d9cf87c7 100644 --- a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb @@ -5,6 +5,10 @@ module Gitlab::Ci class SuccessWithWarnings < SimpleDelegator extend Status::Extended::Base + def text + 'passed' + end + def label 'passed with warnings' end diff --git a/spec/lib/gitlab/ci/status/core/canceled_spec.rb b/spec/lib/gitlab/ci/status/core/canceled_spec.rb index d03b7a18aa0..fd90eb6beda 100644 --- a/spec/lib/gitlab/ci/status/core/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/core/canceled_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Canceled do subject { described_class.new(double('subject')) } + describe '#text' do + it { expect(subject.label).to eq 'canceled' } + end + describe '#label' do it { expect(subject.label).to eq 'canceled' } end diff --git a/spec/lib/gitlab/ci/status/core/created_spec.rb b/spec/lib/gitlab/ci/status/core/created_spec.rb index 5a6d7523e83..a35a3e14929 100644 --- a/spec/lib/gitlab/ci/status/core/created_spec.rb +++ b/spec/lib/gitlab/ci/status/core/created_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Created do subject { described_class.new(double('subject')) } + describe '#text' do + it { expect(subject.label).to eq 'created' } + end + describe '#label' do it { expect(subject.label).to eq 'created' } end diff --git a/spec/lib/gitlab/ci/status/core/failed_spec.rb b/spec/lib/gitlab/ci/status/core/failed_spec.rb index 0b7e03f1684..41ce63b3a6f 100644 --- a/spec/lib/gitlab/ci/status/core/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/core/failed_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Failed do subject { described_class.new(double('subject')) } + describe '#text' do + it { expect(subject.label).to eq 'failed' } + end + describe '#label' do it { expect(subject.label).to eq 'failed' } end diff --git a/spec/lib/gitlab/ci/status/core/pending_spec.rb b/spec/lib/gitlab/ci/status/core/pending_spec.rb index 95f2255e66d..988d3c0a9e2 100644 --- a/spec/lib/gitlab/ci/status/core/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/core/pending_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Pending do subject { described_class.new(double('subject')) } + describe '#text' do + it { expect(subject.label).to eq 'pending' } + end + describe '#label' do it { expect(subject.label).to eq 'pending' } end diff --git a/spec/lib/gitlab/ci/status/core/running_spec.rb b/spec/lib/gitlab/ci/status/core/running_spec.rb index 648de678618..dbb0d37659c 100644 --- a/spec/lib/gitlab/ci/status/core/running_spec.rb +++ b/spec/lib/gitlab/ci/status/core/running_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Running do subject { described_class.new(double('subject')) } + describe '#text' do + it { expect(subject.label).to eq 'running' } + end + describe '#label' do it { expect(subject.label).to eq 'running' } end diff --git a/spec/lib/gitlab/ci/status/core/skipped_spec.rb b/spec/lib/gitlab/ci/status/core/skipped_spec.rb index dc2b56a1e34..624348af2d1 100644 --- a/spec/lib/gitlab/ci/status/core/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/core/skipped_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Skipped do subject { described_class.new(double('subject')) } + describe '#text' do + it { expect(subject.label).to eq 'skipped' } + end + describe '#label' do it { expect(subject.label).to eq 'skipped' } end diff --git a/spec/lib/gitlab/ci/status/core/success_spec.rb b/spec/lib/gitlab/ci/status/core/success_spec.rb index fc67e58adb5..c4bc0d5e234 100644 --- a/spec/lib/gitlab/ci/status/core/success_spec.rb +++ b/spec/lib/gitlab/ci/status/core/success_spec.rb @@ -3,6 +3,10 @@ require 'spec_helper' describe Gitlab::Ci::Status::Core::Success do subject { described_class.new(double('subject')) } + describe '#text' do + it { expect(subject.label).to eq 'passed' } + end + describe '#label' do it { expect(subject.label).to eq 'passed' } end From c7c249407e98bf5fc099cd89901e67b000fdf69d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 14:21:04 +0100 Subject: [PATCH 013/140] Add implementation of common pipeline extended status --- lib/gitlab/ci/status/core/base.rb | 2 ++ .../ci/status/extended/pipeline/common.rb | 7 +++--- .../status/extended/pipeline/common_spec.rb | 23 +++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core/base.rb index f7687c49ffd..d1896a610b7 100644 --- a/lib/gitlab/ci/status/core/base.rb +++ b/lib/gitlab/ci/status/core/base.rb @@ -4,6 +4,8 @@ module Gitlab::Ci # Base abstract class fore core status # class Base + include Gitlab::Routing.url_helpers + def initialize(subject) @subject = subject end diff --git a/lib/gitlab/ci/status/extended/pipeline/common.rb b/lib/gitlab/ci/status/extended/pipeline/common.rb index 75d392fab6c..1b70ba303dc 100644 --- a/lib/gitlab/ci/status/extended/pipeline/common.rb +++ b/lib/gitlab/ci/status/extended/pipeline/common.rb @@ -3,15 +3,14 @@ module Gitlab::Ci module Extended module Pipeline module Common - def initialize(pipeline) - @pipeline = pipeline - end - def has_details? true end def details_path + namespace_project_pipeline_path(@subject.project.namespace, + @subject.project, + @subject) end def has_action? diff --git a/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb index e69de29bb2d..32939800c70 100644 --- a/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Extended::Pipeline::Common do + let(:pipeline) { create(:ci_pipeline) } + + subject do + Gitlab::Ci::Status::Core::Success + .new(pipeline).extend(described_class) + end + + it 'does not have action' do + expect(subject).not_to have_action + end + + it 'has details' do + expect(subject).to have_details + end + + it 'links to the pipeline details page' do + expect(subject.details_path) + .to include "pipelines/#{pipeline.id}" + end +end From d55ff247569a2bf5c78c80f966a56b28d5c8332f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 2 Dec 2016 14:37:29 +0100 Subject: [PATCH 014/140] Implement extended pipeline - status with warnings --- .../gitlab/ci/status/extended/base_spec.rb | 12 ++++ .../pipeline/success_with_warnings_spec.rb | 65 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/spec/lib/gitlab/ci/status/extended/base_spec.rb b/spec/lib/gitlab/ci/status/extended/base_spec.rb index e69de29bb2d..7cdc68c927f 100644 --- a/spec/lib/gitlab/ci/status/extended/base_spec.rb +++ b/spec/lib/gitlab/ci/status/extended/base_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Extended::Base do + subject do + Class.new.extend(described_class) + end + + it 'requires subclass to implement matcher' do + expect { subject.matches?(double) } + .to raise_error(NotImplementedError) + end +end diff --git a/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb index e69de29bb2d..b1a63c5f2f9 100644 --- a/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb +++ b/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Extended::Pipeline::SuccessWithWarnings do + subject do + described_class.new(double('status')) + end + + describe '#test' do + it { expect(subject.text).to eq 'passed' } + end + + describe '#label' do + it { expect(subject.label).to eq 'passed with warnings' } + end + + describe '#icon' do + it { expect(subject.icon).to eq 'icon_status_warning' } + end + + describe '.matches?' do + context 'when pipeline is successful' do + let(:pipeline) do + create(:ci_pipeline, status: :success) + end + + context 'when pipeline has warnings' do + before do + allow(pipeline).to receive(:has_warnings?).and_return(true) + end + + it 'is a correct match' do + expect(described_class.matches?(pipeline)).to eq true + end + end + + context 'when pipeline does not have warnings' do + it 'does not match' do + expect(described_class.matches?(pipeline)).to eq false + end + end + end + + context 'when pipeline is not successful' do + let(:pipeline) do + create(:ci_pipeline, status: :skipped) + end + + context 'when pipeline has warnings' do + before do + allow(pipeline).to receive(:has_warnings?).and_return(true) + end + + it 'does not match' do + expect(described_class.matches?(pipeline)).to eq false + end + end + + context 'when pipeline does not have warnings' do + it 'does not match' do + expect(described_class.matches?(pipeline)).to eq false + end + end + end + end +end From 89c22ed5af490f250800030ee4342c185dbc5358 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 2 Dec 2016 14:34:08 +0000 Subject: [PATCH 015/140] Shows group members in the project members list Closes #24122 --- .../projects/project_members_controller.rb | 20 +++++++++++++++++- app/views/shared/members/_member.html.haml | 6 +++++- .../group-members-in-project-members-view.yml | 4 ++++ .../projects/members/group_members_spec.rb | 21 +++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/group-members-in-project-members-view.yml create mode 100644 spec/features/projects/members/group_members_spec.rb diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 699a56ae2f8..ccf5ff35171 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -10,14 +10,32 @@ class Projects::ProjectMembersController < Projects::ApplicationController @project_members = @project.project_members @project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project) + @group = @project.group + + if @group + @group_members = @group.group_members + @group_members = @group_members.non_invite unless can?(current_user, :admin_group, @group) + end + if params[:search].present? users = @project.users.search(params[:search]).to_a @project_members = @project_members.where(user_id: users) + if @group_members + users = @group.users.search(params[:search]).to_a + @group_members = @group_members.where(user_id: users) + end + @group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) end - @project_members = @project_members.order(access_level: :desc).page(params[:page]) + members_id = @project_members.pluck(:id) + + if @group_members + members_id << @group_members.select{ |member| !@project_members.find_by(user_id: member.user_id) }.select(&:id) + end + + @project_members = Member.where(id: members_id.flatten).order(access_level: :desc).page(params[:page]) @requesters = AccessRequestsFinder.new(@project).execute(current_user) diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 432047a1c4e..bf42c9080a6 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -12,6 +12,10 @@ = link_to user.name, user_path(user) %span.cgray= user.to_reference + - if member.real_source_type == 'Group' + · + %span.cblue=member.group.name + - if user == current_user %span.label.label-success.prepend-left-5 It's you @@ -45,7 +49,7 @@ = time_ago_with_tooltip(member.created_at) - if show_roles .controls.member-controls - - if show_controls + - if show_controls && member.real_source_type == 'Project' - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member diff --git a/changelogs/unreleased/group-members-in-project-members-view.yml b/changelogs/unreleased/group-members-in-project-members-view.yml new file mode 100644 index 00000000000..415e2b6b1e2 --- /dev/null +++ b/changelogs/unreleased/group-members-in-project-members-view.yml @@ -0,0 +1,4 @@ +--- +title: Shows group members in project members list +merge_request: +author: diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb new file mode 100644 index 00000000000..39235a1cd4f --- /dev/null +++ b/spec/features/projects/members/group_members_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +feature 'Projects members', feature: true do + let(:user) { create(:user) } + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } + + background do + group.add_owner(user) + login_as(user) + visit namespace_project_project_members_path(project.namespace, project) + end + + it 'shows group members in list' do + expect(page).to have_selector('.group_member') + + page.within first('.content-list .member') do + expect(page).to have_content(group.name) + end + end +end From cc66ec2b73d6fa581f5300957597615ed1b58c55 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 2 Dec 2016 16:48:34 +0000 Subject: [PATCH 016/140] Fixed Ruby to be better for performance Fixed controls not showing in groups which fixes tests --- .../projects/project_members_controller.rb | 22 +++++++++---------- app/views/shared/members/_member.html.haml | 10 +++------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index ccf5ff35171..10bc75b4c5e 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -10,20 +10,20 @@ class Projects::ProjectMembersController < Projects::ApplicationController @project_members = @project.project_members @project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project) - @group = @project.group + group = @project.group - if @group - @group_members = @group.group_members - @group_members = @group_members.non_invite unless can?(current_user, :admin_group, @group) + if group + group_members = group.group_members.where.not(user_id: @project_members.select(:user_id)) + group_members = group_members.non_invite unless can?(current_user, :admin_group, @group) end if params[:search].present? - users = @project.users.search(params[:search]).to_a - @project_members = @project_members.where(user_id: users) + user_ids = @project.users.search(params[:search]).select(:id) + @project_members = @project_members.where(user_id: user_ids) - if @group_members - users = @group.users.search(params[:search]).to_a - @group_members = @group_members.where(user_id: users) + if group_members + user_ids = group.users.search(params[:search]).select(:id) + group_members = group_members.where(user_id: user_ids) end @group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) @@ -31,8 +31,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController members_id = @project_members.pluck(:id) - if @group_members - members_id << @group_members.select{ |member| !@project_members.find_by(user_id: member.user_id) }.select(&:id) + if group_members + members_id << group_members.pluck(:id) end @project_members = Member.where(id: members_id.flatten).order(access_level: :desc).page(params[:page]) diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index bf42c9080a6..aa5b39151e6 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -12,10 +12,6 @@ = link_to user.name, user_path(user) %span.cgray= user.to_reference - - if member.real_source_type == 'Group' - · - %span.cblue=member.group.name - - if user == current_user %span.label.label-success.prepend-left-5 It's you @@ -24,8 +20,8 @@ %strong Blocked - if source.instance_of?(Group) && !@group - = link_to source, class: "member-group-link prepend-left-5" do - = "· #{source.name}" + · + = link_to source.name, source, class: "member-group-link" .hidden-xs.cgray - if member.request? @@ -49,7 +45,7 @@ = time_ago_with_tooltip(member.created_at) - if show_roles .controls.member-controls - - if show_controls && member.real_source_type == 'Project' + - if show_controls && (member.respond_to?(:group) && @members) || (member.respond_to?(:project) && @project_members) - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member From 617f43c74b967a085f6cd7afb1408cfa28187b52 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 13 Oct 2016 09:38:03 +0200 Subject: [PATCH 017/140] Guests can read builds if those are public Fixes #18448 --- app/policies/ci/build_policy.rb | 2 + app/policies/project_policy.rb | 5 ++ .../zj-guest-reads-public-builds.yml | 4 ++ .../projects/guest_navigation_menu_spec.rb | 4 +- .../security/project/private_access_spec.rb | 52 +++++++++++++++++++ spec/policies/project_policy_spec.rb | 36 ++++++++++--- spec/requests/api/builds_spec.rb | 2 +- 7 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/zj-guest-reads-public-builds.yml diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb index 8b25332b73c..7b1752df0e1 100644 --- a/app/policies/ci/build_policy.rb +++ b/app/policies/ci/build_policy.rb @@ -1,6 +1,8 @@ module Ci class BuildPolicy < CommitStatusPolicy def rules + can! :read_build if @subject.project.public_builds? + super # If we can't read build we should also not have that diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 8ac4bd9df6d..b4c1fcabefd 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -46,6 +46,11 @@ class ProjectPolicy < BasePolicy can! :create_note can! :upload_file can! :read_cycle_analytics + + if project.public_builds? + can! :read_pipeline + can! :read_build + end end def reporter_access! diff --git a/changelogs/unreleased/zj-guest-reads-public-builds.yml b/changelogs/unreleased/zj-guest-reads-public-builds.yml new file mode 100644 index 00000000000..1859addd606 --- /dev/null +++ b/changelogs/unreleased/zj-guest-reads-public-builds.yml @@ -0,0 +1,4 @@ +--- +title: Guests can read builds when public +merge_request: 6842 +author: diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb index c22441f8929..8120a51c515 100644 --- a/spec/features/projects/guest_navigation_menu_spec.rb +++ b/spec/features/projects/guest_navigation_menu_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe "Guest navigation menu" do - let(:project) { create :empty_project, :private } - let(:guest) { create :user } + let(:project) { create(:empty_project, :private, public_builds: false) } + let(:guest) { create(:user) } before do project.team << [guest, :guest] diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 290ddb4c6dd..a942a1ace3b 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -260,6 +260,19 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:user) } it { is_expected.to be_denied_for(:external) } it { is_expected.to be_denied_for(:visitor) } + + context 'when public builds is enabled' do + it { is_expected.to be_allowed_for guest } + end + + context 'when public buils are disabled' do + before do + project.public_builds = false + project.save + end + + it { is_expected.to be_denied_for guest } + end end describe "GET /:project_path/pipelines/:id" do @@ -275,6 +288,19 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:user) } it { is_expected.to be_denied_for(:external) } it { is_expected.to be_denied_for(:visitor) } + + context 'when public builds is enabled' do + it { is_expected.to be_allowed_for guest } + end + + context 'when public buils are disabled' do + before do + project.public_builds = false + project.save + end + + it { is_expected.to be_denied_for guest } + end end describe "GET /:project_path/builds" do @@ -289,6 +315,19 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:user) } it { is_expected.to be_denied_for(:external) } it { is_expected.to be_denied_for(:visitor) } + + context 'when public builds is enabled' do + it { is_expected.to be_allowed_for guest } + end + + context 'when public buils are disabled' do + before do + project.public_builds = false + project.save + end + + it { is_expected.to be_denied_for guest } + end end describe "GET /:project_path/builds/:id" do @@ -305,6 +344,19 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:user) } it { is_expected.to be_denied_for(:external) } it { is_expected.to be_denied_for(:visitor) } + + context 'when public builds is enabled' do + it { is_expected.to be_allowed_for guest } + end + + context 'when public buils are disabled' do + before do + project.public_builds = false + project.save + end + + it { is_expected.to be_denied_for guest } + end end describe "GET /:project_path/environments" do diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index b49e4f3a8bc..34a0937d9bc 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -111,13 +111,35 @@ describe ProjectPolicy, models: true do context 'guests' do let(:current_user) { guest } - it do - is_expected.to include(*guest_permissions) - is_expected.not_to include(*reporter_permissions) - is_expected.not_to include(*team_member_reporter_permissions) - is_expected.not_to include(*developer_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + context 'public builds enabled' do + let(:reporter_public_build_permissions) do + reporter_permissions - [:read_build, :read_pipeline] + end + + it do + is_expected.to include(*guest_permissions) + is_expected.not_to include(*reporter_public_build_permissions) + is_expected.not_to include(*team_member_reporter_permissions) + is_expected.not_to include(*developer_permissions) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + end + + context 'public builds disabled' do + before do + project.public_builds = false + project.save + end + + it do + is_expected.to include(*guest_permissions) + is_expected.not_to include(*reporter_permissions) + is_expected.not_to include(*team_member_reporter_permissions) + is_expected.not_to include(*developer_permissions) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end end end diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 0ea991b18b8..7be7acebb19 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -5,7 +5,7 @@ describe API::Builds, api: true do let(:user) { create(:user) } let(:api_user) { user } - let!(:project) { create(:project, creator_id: user.id) } + let!(:project) { create(:project, creator_id: user.id, public_builds: false) } let!(:developer) { create(:project_member, :developer, user: user, project: project) } let(:reporter) { create(:project_member, :reporter, project: project) } let(:guest) { create(:project_member, :guest, project: project) } From 8b5c16e4b1d54745ba6ca65ecfbf14c1683db3b4 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 28 Nov 2016 21:33:12 +0100 Subject: [PATCH 018/140] API: Ability to remove source branch --- .../unreleased/api-remove-source-branch.yml | 4 ++ doc/api/merge_requests.md | 49 ++++++++++--------- lib/api/merge_requests.rb | 8 ++- spec/requests/api/merge_requests_spec.rb | 12 ++++- 4 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 changelogs/unreleased/api-remove-source-branch.yml diff --git a/changelogs/unreleased/api-remove-source-branch.yml b/changelogs/unreleased/api-remove-source-branch.yml new file mode 100644 index 00000000000..d1b6507aedb --- /dev/null +++ b/changelogs/unreleased/api-remove-source-branch.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Ability to set ''should_remove_source_branch'' on merge requests' +merge_request: +author: Robert Schilling diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 12d24543bbe..8d9d3cd5580 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -271,17 +271,18 @@ Creates a new merge request. POST /projects/:id/merge_requests ``` -Parameters: - -- `id` (required) - The ID of a project -- `source_branch` (required) - The source branch -- `target_branch` (required) - The target branch -- `assignee_id` (optional) - Assignee user ID -- `title` (required) - Title of MR -- `description` (optional) - Description of MR -- `target_project_id` (optional) - The target project (numeric id) -- `labels` (optional) - Labels for MR as a comma-separated list -- `milestone_id` (optional) - Milestone ID +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | string | yes | The ID of a project | +| `source_branch` | string | yes | The source branch | +| `target_branch` | string | yes | The target branch | +| `title` | string | yes | Title of MR | +| `assignee_id` | integer | no | Assignee user ID | +| `description` | string | no | Description of MR | +| `target_project_id` | integer | no | The target project (numeric id) | +| `labels` | string | no | Labels for MR as a comma-separated list | +| `milestone_id` | integer | no | The ID of a milestone | +| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging | ```json { @@ -346,17 +347,19 @@ Updates an existing merge request. You can change the target branch, title, or e PUT /projects/:id/merge_requests/:merge_request_id ``` -Parameters: - -- `id` (required) - The ID of a project -- `merge_request_id` (required) - ID of MR -- `target_branch` - The target branch -- `assignee_id` - Assignee user ID -- `title` - Title of MR -- `description` - Description of MR -- `state_event` - New state (close|reopen|merge) -- `labels` (optional) - Labels for MR as a comma-separated list -- `milestone_id` (optional) - Milestone ID +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | string | yes | The ID of a project | +| `merge_request_id` | integer | yes | The ID of a merge request | +| `source_branch` | string | yes | The source branch | +| `target_branch` | string | yes | The target branch | +| `title` | string | yes | Title of MR | +| `assignee_id` | integer | no | Assignee user ID | +| `description` | string | no | Description of MR | +| `target_project_id` | integer | no | The target project (numeric id) | +| `labels` | string | no | Labels for MR as a comma-separated list | +| `milestone_id` | integer | no | The ID of a milestone | +| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging | ```json { @@ -807,7 +810,7 @@ Example response: ## Create a todo -Manually creates a todo for the current user on a merge request. +Manually creates a todo for the current user on a merge request. If there already exists a todo for the user on that merge request, status code `304` is returned. diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 97baebc1d27..5535c807d21 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -28,6 +28,7 @@ module API optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request' optional :labels, type: String, desc: 'Comma-separated list of label names' + optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging' end end @@ -75,7 +76,8 @@ module API post ":id/merge_requests" do authorize! :create_merge_request, user_project - mr_params = declared_params + mr_params = declared_params(include_missing: false) + mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present? merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute @@ -144,13 +146,15 @@ module API desc: 'Status of the merge request' use :optional_params at_least_one_of :title, :target_branch, :description, :assignee_id, - :milestone_id, :labels, :state_event + :milestone_id, :labels, :state_event, + :remove_source_branch end put path do merge_request = user_project.merge_requests.find(params.delete(:merge_request_id)) authorize! :update_merge_request, merge_request mr_params = declared_params(include_missing: false) + mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present? merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request) diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 918a71129f7..894896b95e4 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -234,11 +234,14 @@ describe API::MergeRequests, api: true do target_branch: 'master', author: user, labels: 'label, label2', - milestone_id: milestone.id + milestone_id: milestone.id, + remove_source_branch: true + expect(response).to have_http_status(201) expect(json_response['title']).to eq('Test merge_request') expect(json_response['labels']).to eq(['label', 'label2']) expect(json_response['milestone']['id']).to eq(milestone.id) + expect(json_response['force_remove_source_branch']).to be_truthy end it "returns 422 when source_branch equals target_branch" do @@ -511,6 +514,13 @@ describe API::MergeRequests, api: true do expect(json_response['target_branch']).to eq('wiki') end + it "returns merge_request that removes the source branch" do + put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), remove_source_branch: true + + expect(response).to have_http_status(200) + expect(json_response['force_remove_source_branch']).to be_truthy + end + it 'allows special label names' do put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), title: 'new issue', From 10960400245ca338e32a3c55538ace976df962c6 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 29 Nov 2016 13:43:58 +0100 Subject: [PATCH 019/140] Update effected tests --- app/policies/project_policy.rb | 3 -- features/steps/shared/project.rb | 2 +- .../security/project/private_access_spec.rb | 49 ++++++++++--------- .../cycle_analytics/permissions_spec.rb | 2 +- spec/policies/project_policy_spec.rb | 32 ++++++------ .../projects/cycle_analytics_events_spec.rb | 2 +- .../pipeline_notification_worker_spec.rb | 2 +- 7 files changed, 45 insertions(+), 47 deletions(-) diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index b4c1fcabefd..d5aadfce76a 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -12,9 +12,6 @@ class ProjectPolicy < BasePolicy guest_access! public_access! - # Allow to read builds for internal projects - can! :read_build if project.public_builds? - if project.request_access_enabled && !(owner || user.admin? || project.team.member?(user) || project_group_member?(user)) can! :request_access diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index cab85a48396..b51152c79c6 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -9,7 +9,7 @@ module SharedProject step "project exists in some group namespace" do @group = create(:group, name: 'some group') - @project = create(:project, namespace: @group) + @project = create(:project, namespace: @group, public_builds: false) end # Create a specific project called "Shop" diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index a942a1ace3b..f52e23f9433 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe "Private Project Access", feature: true do include AccessMatchers - let(:project) { create(:project, :private) } + let(:project) { create(:project, :private, public_builds: false) } describe "Project should be private" do describe '#private?' do @@ -262,16 +262,15 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } context 'when public builds is enabled' do - it { is_expected.to be_allowed_for guest } + before do + project.update(public_builds: true) + end + + it { is_expected.to be_allowed_for(:guest).of(project) } end context 'when public buils are disabled' do - before do - project.public_builds = false - project.save - end - - it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for(:guest).of(project) } end end @@ -290,16 +289,15 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } context 'when public builds is enabled' do - it { is_expected.to be_allowed_for guest } + before do + project.update(public_builds: true) + end + + it { is_expected.to be_allowed_for(:guest).of(project) } end context 'when public buils are disabled' do - before do - project.public_builds = false - project.save - end - - it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for(:guest).of(project) } end end @@ -317,16 +315,15 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } context 'when public builds is enabled' do - it { is_expected.to be_allowed_for guest } + before do + project.update(public_builds: true) + end + + it { is_expected.to be_allowed_for(:guest).of(project) } end context 'when public buils are disabled' do - before do - project.public_builds = false - project.save - end - - it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for(:guest).of(project) } end end @@ -346,7 +343,11 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for(:visitor) } context 'when public builds is enabled' do - it { is_expected.to be_allowed_for guest } + before do + project.update(public_builds: true) + end + + it { is_expected.to be_allowed_for(:guest).of(project) } end context 'when public buils are disabled' do @@ -355,7 +356,7 @@ describe "Private Project Access", feature: true do project.save end - it { is_expected.to be_denied_for guest } + it { is_expected.to be_denied_for(:guest).of(project) } end end diff --git a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb index dc4f7dc69db..2d85e712db0 100644 --- a/spec/lib/gitlab/cycle_analytics/permissions_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/permissions_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::CycleAnalytics::Permissions do - let(:project) { create(:empty_project) } + let(:project) { create(:empty_project, public_builds: false) } let(:user) { create(:user) } subject { described_class.get(user: user, project: project) } diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 34a0937d9bc..eeab9827d99 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -111,34 +111,34 @@ describe ProjectPolicy, models: true do context 'guests' do let(:current_user) { guest } - context 'public builds enabled' do - let(:reporter_public_build_permissions) do - reporter_permissions - [:read_build, :read_pipeline] - end + let(:reporter_public_build_permissions) do + reporter_permissions - [:read_build, :read_pipeline] + end + it do + is_expected.to include(*guest_permissions) + is_expected.not_to include(*reporter_public_build_permissions) + is_expected.not_to include(*team_member_reporter_permissions) + is_expected.not_to include(*developer_permissions) + is_expected.not_to include(*master_permissions) + is_expected.not_to include(*owner_permissions) + end + + context 'public builds enabled' do it do is_expected.to include(*guest_permissions) - is_expected.not_to include(*reporter_public_build_permissions) - is_expected.not_to include(*team_member_reporter_permissions) - is_expected.not_to include(*developer_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + is_expected.to include(:read_build, :read_pipeline) end end context 'public builds disabled' do before do - project.public_builds = false - project.save + project.update(public_builds: false) end it do is_expected.to include(*guest_permissions) - is_expected.not_to include(*reporter_permissions) - is_expected.not_to include(*team_member_reporter_permissions) - is_expected.not_to include(*developer_permissions) - is_expected.not_to include(*master_permissions) - is_expected.not_to include(*owner_permissions) + is_expected.not_to include(:read_build, :read_pipeline) end end end diff --git a/spec/requests/projects/cycle_analytics_events_spec.rb b/spec/requests/projects/cycle_analytics_events_spec.rb index f5e0fdcda2d..e0368e6001f 100644 --- a/spec/requests/projects/cycle_analytics_events_spec.rb +++ b/spec/requests/projects/cycle_analytics_events_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe 'cycle analytics events' do let(:user) { create(:user) } - let(:project) { create(:project) } + let(:project) { create(:project, public_builds: false) } let(:issue) { create(:issue, project: project, created_at: 2.days.ago) } describe 'GET /:namespace/:project/cycle_analytics/events/issues' do diff --git a/spec/workers/pipeline_notification_worker_spec.rb b/spec/workers/pipeline_notification_worker_spec.rb index 739f9b63967..603ae52ed1e 100644 --- a/spec/workers/pipeline_notification_worker_spec.rb +++ b/spec/workers/pipeline_notification_worker_spec.rb @@ -11,7 +11,7 @@ describe PipelineNotificationWorker do status: status) end - let(:project) { create(:project) } + let(:project) { create(:project, public_builds: false) } let(:user) { create(:user) } let(:pusher) { user } let(:watcher) { pusher } From bb447383c527d28bf4f884df18462e22be05f3b4 Mon Sep 17 00:00:00 2001 From: Oxan van Leeuwen Date: Tue, 22 Nov 2016 18:11:34 +0100 Subject: [PATCH 020/140] Add issue events filter and make sure "All" really shows everything Currently, the EventFilter whitelists event types for the "All" filter. This has gotten outdated, which causes the confusing behaviour of the "All" tab not really showing all events. To make matters worse, by default no tab at all is selected, which does show all events, so selecting the "All" tab actually hides some events. Fix this by: - Making sure All always includes all activity, by abolishing the whitelist and just returning all events instead. - Make the All tab selected by default. - Add Issue events tab to include the specific events around opening and closing issues, since there was no specific filter to see them yet. Fixes #24826 --- app/views/shared/_event_filter.html.haml | 2 + changelogs/unreleased/issue-events-filter.yml | 4 ++ lib/event_filter.rb | 38 +++++++++---------- .../fixtures/event_filter.html.haml | 4 ++ spec/lib/event_filter_spec.rb | 15 ++++++-- 5 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 changelogs/unreleased/issue-events-filter.yml diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml index c367ae336db..67c145cef17 100644 --- a/app/views/shared/_event_filter.html.haml +++ b/app/views/shared/_event_filter.html.haml @@ -4,6 +4,8 @@ = event_filter_link EventFilter.push, 'Push events' - if event_filter_visible(:merge_requests) = event_filter_link EventFilter.merged, 'Merge events' + - if event_filter_visible(:issues) + = event_filter_link EventFilter.issue, 'Issue events' - if event_filter_visible(:issues) = event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.team, 'Team' diff --git a/changelogs/unreleased/issue-events-filter.yml b/changelogs/unreleased/issue-events-filter.yml new file mode 100644 index 00000000000..a3b08bde6e7 --- /dev/null +++ b/changelogs/unreleased/issue-events-filter.yml @@ -0,0 +1,4 @@ +--- +title: Add issue events filter and make all really show all events +merge_request: 7673 +author: Oxan van Leeuwen diff --git a/lib/event_filter.rb b/lib/event_filter.rb index 21f6a9a762b..515095af1c2 100644 --- a/lib/event_filter.rb +++ b/lib/event_filter.rb @@ -14,6 +14,10 @@ class EventFilter 'merged' end + def issue + 'issue' + end + def comments 'comments' end @@ -32,32 +36,20 @@ class EventFilter end def apply_filter(events) - return events unless params.present? + return events if params.blank? || params == EventFilter.all - filter = params.dup - actions = [] - - case filter + case params when EventFilter.push - actions = [Event::PUSHED] + events.where(action: Event::PUSHED) when EventFilter.merged - actions = [Event::MERGED] + events.where(action: Event::MERGED) when EventFilter.comments - actions = [Event::COMMENTED] + events.where(action: Event::COMMENTED) when EventFilter.team - actions = [Event::JOINED, Event::LEFT, Event::EXPIRED] - when EventFilter.all - actions = [ - Event::PUSHED, - Event::MERGED, - Event::COMMENTED, - Event::JOINED, - Event::LEFT, - Event::EXPIRED - ] + events.where(action: [Event::JOINED, Event::LEFT, Event::EXPIRED]) + when EventFilter.issue + events.where(action: [Event::CREATED, Event::UPDATED, Event::CLOSED, Event::REOPENED]) end - - events.where(action: actions) end def options(key) @@ -73,6 +65,10 @@ class EventFilter end def active?(key) - params.include? key + if params.present? + params.include? key + else + key == EventFilter.all + end end end diff --git a/spec/javascripts/fixtures/event_filter.html.haml b/spec/javascripts/fixtures/event_filter.html.haml index 95e248cadf8..5477c6075f0 100644 --- a/spec/javascripts/fixtures/event_filter.html.haml +++ b/spec/javascripts/fixtures/event_filter.html.haml @@ -11,6 +11,10 @@ %a.event-filter-link{ id: "merged_event_filter", title: "Filter by merge events", href: "/dashboard/activity"} %span Merge events + %li + %a.event-filter-link{ id: "issue_event_filter", title: "Filter by issue events", href: "/dashboard/activity"} + %span + Issue events %li %a.event-filter-link{ id: "comments_event_filter", title: "Filter by comments", href: "/dashboard/activity"} %span diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb index a6d8e6927e0..ec2f66b1136 100644 --- a/spec/lib/event_filter_spec.rb +++ b/spec/lib/event_filter_spec.rb @@ -7,6 +7,10 @@ describe EventFilter, lib: true do let!(:push_event) { create(:event, action: Event::PUSHED, project: public_project, target: public_project, author: source_user) } let!(:merged_event) { create(:event, action: Event::MERGED, project: public_project, target: public_project, author: source_user) } + let!(:created_event) { create(:event, action: Event::CREATED, project: public_project, target: public_project, author: source_user) } + let!(:updated_event) { create(:event, action: Event::UPDATED, project: public_project, target: public_project, author: source_user) } + let!(:closed_event) { create(:event, action: Event::CLOSED, project: public_project, target: public_project, author: source_user) } + let!(:reopened_event) { create(:event, action: Event::REOPENED, project: public_project, target: public_project, author: source_user) } let!(:comments_event) { create(:event, action: Event::COMMENTED, project: public_project, target: public_project, author: source_user) } let!(:joined_event) { create(:event, action: Event::JOINED, project: public_project, target: public_project, author: source_user) } let!(:left_event) { create(:event, action: Event::LEFT, project: public_project, target: public_project, author: source_user) } @@ -21,6 +25,11 @@ describe EventFilter, lib: true do expect(events).to contain_exactly(merged_event) end + it 'applies issue filter' do + events = EventFilter.new(EventFilter.issue).apply_filter(Event.all) + expect(events).to contain_exactly(created_event, updated_event, closed_event, reopened_event) + end + it 'applies comments filter' do events = EventFilter.new(EventFilter.comments).apply_filter(Event.all) expect(events).to contain_exactly(comments_event) @@ -33,17 +42,17 @@ describe EventFilter, lib: true do it 'applies all filter' do events = EventFilter.new(EventFilter.all).apply_filter(Event.all) - expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event) + expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end it 'applies no filter' do events = EventFilter.new(nil).apply_filter(Event.all) - expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event) + expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end it 'applies unknown filter' do events = EventFilter.new('').apply_filter(Event.all) - expect(events).to contain_exactly(push_event, merged_event, comments_event, joined_event, left_event) + expect(events).to contain_exactly(push_event, merged_event, created_event, updated_event, closed_event, reopened_event, comments_event, joined_event, left_event) end end end From b86d8afe23524d10956cfbc1b87337fd2ce75e8c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 5 Dec 2016 11:35:27 +0100 Subject: [PATCH 021/140] Fold core/extended status modules to reduce nesting --- lib/gitlab/ci/status/{core => }/canceled.rb | 8 ++++---- lib/gitlab/ci/status/{core/base.rb => core.rb} | 8 ++++---- lib/gitlab/ci/status/{core => }/created.rb | 8 ++++---- lib/gitlab/ci/status/{extended/base.rb => extended.rb} | 8 ++++---- lib/gitlab/ci/status/factory.rb | 6 ------ lib/gitlab/ci/status/{core => }/failed.rb | 8 ++++---- lib/gitlab/ci/status/{core => }/pending.rb | 8 ++++---- lib/gitlab/ci/status/{extended => }/pipeline/common.rb | 6 +++--- .../{extended => }/pipeline/success_with_warnings.rb | 8 ++++---- lib/gitlab/ci/status/{core => }/running.rb | 8 ++++---- lib/gitlab/ci/status/{core => }/skipped.rb | 8 ++++---- lib/gitlab/ci/status/{core => }/success.rb | 8 ++++---- spec/lib/gitlab/ci/status/{core => }/canceled_spec.rb | 2 +- spec/lib/gitlab/ci/status/{core => }/created_spec.rb | 2 +- .../ci/status/{extended/base_spec.rb => extended_spec.rb} | 2 +- spec/lib/gitlab/ci/status/factory_spec.rb | 0 spec/lib/gitlab/ci/status/{core => }/failed_spec.rb | 2 +- spec/lib/gitlab/ci/status/{core => }/pending_spec.rb | 2 +- .../ci/status/{extended => }/pipeline/common_spec.rb | 4 ++-- .../{extended => }/pipeline/success_with_warnings_spec.rb | 2 +- spec/lib/gitlab/ci/status/{core => }/running_spec.rb | 2 +- spec/lib/gitlab/ci/status/{core => }/skipped_spec.rb | 2 +- spec/lib/gitlab/ci/status/{core => }/success_spec.rb | 2 +- 23 files changed, 54 insertions(+), 60 deletions(-) rename lib/gitlab/ci/status/{core => }/canceled.rb (69%) rename lib/gitlab/ci/status/{core/base.rb => core.rb} (92%) rename lib/gitlab/ci/status/{core => }/created.rb (69%) rename lib/gitlab/ci/status/{extended/base.rb => extended.rb} (59%) delete mode 100644 lib/gitlab/ci/status/factory.rb rename lib/gitlab/ci/status/{core => }/failed.rb (69%) rename lib/gitlab/ci/status/{core => }/pending.rb (69%) rename lib/gitlab/ci/status/{extended => }/pipeline/common.rb (88%) rename lib/gitlab/ci/status/{extended => }/pipeline/success_with_warnings.rb (81%) rename lib/gitlab/ci/status/{core => }/running.rb (69%) rename lib/gitlab/ci/status/{core => }/skipped.rb (69%) rename lib/gitlab/ci/status/{core => }/success.rb (69%) rename spec/lib/gitlab/ci/status/{core => }/canceled_spec.rb (89%) rename spec/lib/gitlab/ci/status/{core => }/created_spec.rb (89%) rename spec/lib/gitlab/ci/status/{extended/base_spec.rb => extended_spec.rb} (82%) delete mode 100644 spec/lib/gitlab/ci/status/factory_spec.rb rename spec/lib/gitlab/ci/status/{core => }/failed_spec.rb (90%) rename spec/lib/gitlab/ci/status/{core => }/pending_spec.rb (89%) rename spec/lib/gitlab/ci/status/{extended => }/pipeline/common_spec.rb (80%) rename spec/lib/gitlab/ci/status/{extended => }/pipeline/success_with_warnings_spec.rb (95%) rename spec/lib/gitlab/ci/status/{core => }/running_spec.rb (89%) rename spec/lib/gitlab/ci/status/{core => }/skipped_spec.rb (89%) rename spec/lib/gitlab/ci/status/{core => }/success_spec.rb (89%) diff --git a/lib/gitlab/ci/status/core/canceled.rb b/lib/gitlab/ci/status/canceled.rb similarity index 69% rename from lib/gitlab/ci/status/core/canceled.rb rename to lib/gitlab/ci/status/canceled.rb index a05ac8ee3cc..dd6d99e9075 100644 --- a/lib/gitlab/ci/status/core/canceled.rb +++ b/lib/gitlab/ci/status/canceled.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Core - class Canceled < Core::Base +module Gitlab + module Ci + module Status + class Canceled < Status::Core def text 'canceled' end diff --git a/lib/gitlab/ci/status/core/base.rb b/lib/gitlab/ci/status/core.rb similarity index 92% rename from lib/gitlab/ci/status/core/base.rb rename to lib/gitlab/ci/status/core.rb index d1896a610b7..fbfe257eeca 100644 --- a/lib/gitlab/ci/status/core/base.rb +++ b/lib/gitlab/ci/status/core.rb @@ -1,9 +1,9 @@ -module Gitlab::Ci - module Status - module Core +module Gitlab + module Ci + module Status # Base abstract class fore core status # - class Base + class Core include Gitlab::Routing.url_helpers def initialize(subject) diff --git a/lib/gitlab/ci/status/core/created.rb b/lib/gitlab/ci/status/created.rb similarity index 69% rename from lib/gitlab/ci/status/core/created.rb rename to lib/gitlab/ci/status/created.rb index ee8bf2e8dac..6596d7e01ca 100644 --- a/lib/gitlab/ci/status/core/created.rb +++ b/lib/gitlab/ci/status/created.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Core - class Created < Core::Base +module Gitlab + module Ci + module Status + class Created < Status::Core def text 'created' end diff --git a/lib/gitlab/ci/status/extended/base.rb b/lib/gitlab/ci/status/extended.rb similarity index 59% rename from lib/gitlab/ci/status/extended/base.rb rename to lib/gitlab/ci/status/extended.rb index 1d7819c6891..6bfb5d38c1f 100644 --- a/lib/gitlab/ci/status/extended/base.rb +++ b/lib/gitlab/ci/status/extended.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Extended - module Base +module Gitlab + module Ci + module Status + module Extended def matches?(_subject) raise NotImplementedError end diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb deleted file mode 100644 index 212cd1a1687..00000000000 --- a/lib/gitlab/ci/status/factory.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Gitlab::Ci - module Status - class Factory - end - end -end diff --git a/lib/gitlab/ci/status/core/failed.rb b/lib/gitlab/ci/status/failed.rb similarity index 69% rename from lib/gitlab/ci/status/core/failed.rb rename to lib/gitlab/ci/status/failed.rb index ea1615853c0..c5b5e3203ad 100644 --- a/lib/gitlab/ci/status/core/failed.rb +++ b/lib/gitlab/ci/status/failed.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Core - class Failed < Core::Base +module Gitlab + module Ci + module Status + class Failed < Status::Core def text 'failed' end diff --git a/lib/gitlab/ci/status/core/pending.rb b/lib/gitlab/ci/status/pending.rb similarity index 69% rename from lib/gitlab/ci/status/core/pending.rb rename to lib/gitlab/ci/status/pending.rb index 95fbb710735..d30f35a59a2 100644 --- a/lib/gitlab/ci/status/core/pending.rb +++ b/lib/gitlab/ci/status/pending.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Core - class Pending < Core::Base +module Gitlab + module Ci + module Status + class Pending < Status::Core def text 'pending' end diff --git a/lib/gitlab/ci/status/extended/pipeline/common.rb b/lib/gitlab/ci/status/pipeline/common.rb similarity index 88% rename from lib/gitlab/ci/status/extended/pipeline/common.rb rename to lib/gitlab/ci/status/pipeline/common.rb index 1b70ba303dc..25e52bec3da 100644 --- a/lib/gitlab/ci/status/extended/pipeline/common.rb +++ b/lib/gitlab/ci/status/pipeline/common.rb @@ -1,6 +1,6 @@ -module Gitlab::Ci - module Status - module Extended +module Gitlab + module Ci + module Status module Pipeline module Common def has_details? diff --git a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb similarity index 81% rename from lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb rename to lib/gitlab/ci/status/pipeline/success_with_warnings.rb index 8f1d9cf87c7..97dfba81ff5 100644 --- a/lib/gitlab/ci/status/extended/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb @@ -1,9 +1,9 @@ -module Gitlab::Ci - module Status - module Extended +module Gitlab + module Ci + module Status module Pipeline class SuccessWithWarnings < SimpleDelegator - extend Status::Extended::Base + extend Status::Extended def text 'passed' diff --git a/lib/gitlab/ci/status/core/running.rb b/lib/gitlab/ci/status/running.rb similarity index 69% rename from lib/gitlab/ci/status/core/running.rb rename to lib/gitlab/ci/status/running.rb index 5580c1a5154..2aba3c373c7 100644 --- a/lib/gitlab/ci/status/core/running.rb +++ b/lib/gitlab/ci/status/running.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Core - class Running < Core::Base +module Gitlab + module Ci + module Status + class Running < Status::Core def text 'running' end diff --git a/lib/gitlab/ci/status/core/skipped.rb b/lib/gitlab/ci/status/skipped.rb similarity index 69% rename from lib/gitlab/ci/status/core/skipped.rb rename to lib/gitlab/ci/status/skipped.rb index 0e8e42f525b..16282aefd03 100644 --- a/lib/gitlab/ci/status/core/skipped.rb +++ b/lib/gitlab/ci/status/skipped.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Core - class Skipped < Core::Base +module Gitlab + module Ci + module Status + class Skipped < Status::Core def text 'skipped' end diff --git a/lib/gitlab/ci/status/core/success.rb b/lib/gitlab/ci/status/success.rb similarity index 69% rename from lib/gitlab/ci/status/core/success.rb rename to lib/gitlab/ci/status/success.rb index 7efafdb615f..c09c5f006e3 100644 --- a/lib/gitlab/ci/status/core/success.rb +++ b/lib/gitlab/ci/status/success.rb @@ -1,7 +1,7 @@ -module Gitlab::Ci - module Status - module Core - class Success < Core::Base +module Gitlab + module Ci + module Status + class Success < Status::Core def text 'passed' end diff --git a/spec/lib/gitlab/ci/status/core/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb similarity index 89% rename from spec/lib/gitlab/ci/status/core/canceled_spec.rb rename to spec/lib/gitlab/ci/status/canceled_spec.rb index fd90eb6beda..619ecbcba67 100644 --- a/spec/lib/gitlab/ci/status/core/canceled_spec.rb +++ b/spec/lib/gitlab/ci/status/canceled_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Core::Canceled do +describe Gitlab::Ci::Status::Canceled do subject { described_class.new(double('subject')) } describe '#text' do diff --git a/spec/lib/gitlab/ci/status/core/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb similarity index 89% rename from spec/lib/gitlab/ci/status/core/created_spec.rb rename to spec/lib/gitlab/ci/status/created_spec.rb index a35a3e14929..157302c65a8 100644 --- a/spec/lib/gitlab/ci/status/core/created_spec.rb +++ b/spec/lib/gitlab/ci/status/created_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Core::Created do +describe Gitlab::Ci::Status::Created do subject { described_class.new(double('subject')) } describe '#text' do diff --git a/spec/lib/gitlab/ci/status/extended/base_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb similarity index 82% rename from spec/lib/gitlab/ci/status/extended/base_spec.rb rename to spec/lib/gitlab/ci/status/extended_spec.rb index 7cdc68c927f..120e121aae5 100644 --- a/spec/lib/gitlab/ci/status/extended/base_spec.rb +++ b/spec/lib/gitlab/ci/status/extended_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Extended::Base do +describe Gitlab::Ci::Status::Extended do subject do Class.new.extend(described_class) end diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/spec/lib/gitlab/ci/status/core/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb similarity index 90% rename from spec/lib/gitlab/ci/status/core/failed_spec.rb rename to spec/lib/gitlab/ci/status/failed_spec.rb index 41ce63b3a6f..0b3cb8168e6 100644 --- a/spec/lib/gitlab/ci/status/core/failed_spec.rb +++ b/spec/lib/gitlab/ci/status/failed_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Core::Failed do +describe Gitlab::Ci::Status::Failed do subject { described_class.new(double('subject')) } describe '#text' do diff --git a/spec/lib/gitlab/ci/status/core/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb similarity index 89% rename from spec/lib/gitlab/ci/status/core/pending_spec.rb rename to spec/lib/gitlab/ci/status/pending_spec.rb index 988d3c0a9e2..57c901c1202 100644 --- a/spec/lib/gitlab/ci/status/core/pending_spec.rb +++ b/spec/lib/gitlab/ci/status/pending_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Core::Pending do +describe Gitlab::Ci::Status::Pending do subject { described_class.new(double('subject')) } describe '#text' do diff --git a/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb similarity index 80% rename from spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb rename to spec/lib/gitlab/ci/status/pipeline/common_spec.rb index 32939800c70..21adee3f8e7 100644 --- a/spec/lib/gitlab/ci/status/extended/pipeline/common_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/common_spec.rb @@ -1,10 +1,10 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Extended::Pipeline::Common do +describe Gitlab::Ci::Status::Pipeline::Common do let(:pipeline) { create(:ci_pipeline) } subject do - Gitlab::Ci::Status::Core::Success + Class.new(Gitlab::Ci::Status::Core) .new(pipeline).extend(described_class) end diff --git a/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb similarity index 95% rename from spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb rename to spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb index b1a63c5f2f9..02e526e3de2 100644 --- a/spec/lib/gitlab/ci/status/extended/pipeline/success_with_warnings_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/success_with_warnings_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Extended::Pipeline::SuccessWithWarnings do +describe Gitlab::Ci::Status::Pipeline::SuccessWithWarnings do subject do described_class.new(double('status')) end diff --git a/spec/lib/gitlab/ci/status/core/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb similarity index 89% rename from spec/lib/gitlab/ci/status/core/running_spec.rb rename to spec/lib/gitlab/ci/status/running_spec.rb index dbb0d37659c..c023f1872cc 100644 --- a/spec/lib/gitlab/ci/status/core/running_spec.rb +++ b/spec/lib/gitlab/ci/status/running_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Core::Running do +describe Gitlab::Ci::Status::Running do subject { described_class.new(double('subject')) } describe '#text' do diff --git a/spec/lib/gitlab/ci/status/core/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb similarity index 89% rename from spec/lib/gitlab/ci/status/core/skipped_spec.rb rename to spec/lib/gitlab/ci/status/skipped_spec.rb index 624348af2d1..d4f7f4b3b70 100644 --- a/spec/lib/gitlab/ci/status/core/skipped_spec.rb +++ b/spec/lib/gitlab/ci/status/skipped_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Core::Skipped do +describe Gitlab::Ci::Status::Skipped do subject { described_class.new(double('subject')) } describe '#text' do diff --git a/spec/lib/gitlab/ci/status/core/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb similarity index 89% rename from spec/lib/gitlab/ci/status/core/success_spec.rb rename to spec/lib/gitlab/ci/status/success_spec.rb index c4bc0d5e234..9e261a3aa5f 100644 --- a/spec/lib/gitlab/ci/status/core/success_spec.rb +++ b/spec/lib/gitlab/ci/status/success_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Status::Core::Success do +describe Gitlab::Ci::Status::Success do subject { described_class.new(double('subject')) } describe '#text' do From d28f5e776b90a648a83246beac94518cd8183af4 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 5 Dec 2016 12:07:14 +0100 Subject: [PATCH 022/140] Implement pipeline status factory with extended status --- lib/gitlab/ci/status/pipeline/factory.rb | 39 +++++++++++++++++++ .../gitlab/ci/status/pipeline/factory_spec.rb | 37 ++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 lib/gitlab/ci/status/pipeline/factory.rb create mode 100644 spec/lib/gitlab/ci/status/pipeline/factory_spec.rb diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb new file mode 100644 index 00000000000..71d27bf7cf5 --- /dev/null +++ b/lib/gitlab/ci/status/pipeline/factory.rb @@ -0,0 +1,39 @@ +module Gitlab + module Ci + module Status + module Pipeline + class Factory + EXTENDED_STATUSES = [Pipeline::SuccessWithWarnings] + + def initialize(pipeline) + @pipeline = pipeline + @status = pipeline.status || :created + end + + def fabricate! + if extended_status + extended_status.new(core_status) + else + core_status + end + end + + private + + def core_status + Gitlab::Ci::Status + .const_get(@status.capitalize) + .new(@pipeline) + .extend(Status::Pipeline::Common) + end + + def extended_status + @extended ||= EXTENDED_STATUSES.find do |status| + status.matches?(@pipeline) + end + end + end + end + end + end +end diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb new file mode 100644 index 00000000000..9f251735067 --- /dev/null +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Pipeline::Factory do + subject do + described_class.new(pipeline) + end + + context 'when pipeline has a core status' do + HasStatus::AVAILABLE_STATUSES.each do |core_status| + context "when core status is #{core_status}" do + let(:pipeline) do + create(:ci_pipeline, status: core_status) + end + + it "fabricates a core status #{core_status}" do + expect(subject.fabricate!) + .to be_a Gitlab::Ci::Status.const_get(core_status.capitalize) + end + end + end + end + + context 'when pipeline has warnings' do + let(:pipeline) do + create(:ci_pipeline, status: :success) + end + + before do + create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline) + end + + it 'fabricates extended "success with warnings" status' do + expect(subject.fabricate!) + .to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings + end + end +end From b2ab11a91785ffe0316e38687837e165fad3eb65 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 5 Dec 2016 13:10:20 +0100 Subject: [PATCH 023/140] Extend tests for pipeline status factory --- .../gitlab/ci/status/pipeline/factory_spec.rb | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index 9f251735067..543dae0640d 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -5,6 +5,10 @@ describe Gitlab::Ci::Status::Pipeline::Factory do described_class.new(pipeline) end + let(:status) do + subject.fabricate! + end + context 'when pipeline has a core status' do HasStatus::AVAILABLE_STATUSES.each do |core_status| context "when core status is #{core_status}" do @@ -13,8 +17,13 @@ describe Gitlab::Ci::Status::Pipeline::Factory do end it "fabricates a core status #{core_status}" do - expect(subject.fabricate!) - .to be_a Gitlab::Ci::Status.const_get(core_status.capitalize) + expect(status).to be_a( + Gitlab::Ci::Status.const_get(core_status.capitalize)) + end + + it 'extends core status with common pipeline methods' do + expect(status).to have_details + expect(status.details_path).to include "pipelines/#{pipeline.id}" end end end @@ -30,8 +39,12 @@ describe Gitlab::Ci::Status::Pipeline::Factory do end it 'fabricates extended "success with warnings" status' do - expect(subject.fabricate!) + expect(status) .to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings end + + it 'extends core status with common pipeline methods' do + expect(status).to have_details + end end end From 5e3cfe2f091a1803aa7d08238232aea846ebfb5a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 5 Dec 2016 13:23:58 +0100 Subject: [PATCH 024/140] Expose pipeline detailed status using status factory --- app/models/ci/pipeline.rb | 4 ++ spec/models/ci/pipeline_spec.rb | 70 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index fabbf97d4db..caf6908505e 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -320,6 +320,10 @@ module Ci .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id } end + def detailed_status + Gitlab::Ci::Status::Pipeline::Factory.new(self).fabricate! + end + private def pipeline_data diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 0d2b4920835..3f93d9ddf19 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -404,6 +404,76 @@ describe Ci::Pipeline, models: true do end end + describe '#detailed_status' do + context 'when pipeline is created' do + let(:pipeline) { create(:ci_pipeline, status: :created) } + + it 'returns detailed status for created pipeline' do + expect(pipeline.detailed_status.text).to eq 'created' + end + end + + context 'when pipeline is pending' do + let(:pipeline) { create(:ci_pipeline, status: :pending) } + + it 'returns detailed status for pending pipeline' do + expect(pipeline.detailed_status.text).to eq 'pending' + end + end + + context 'when pipeline is running' do + let(:pipeline) { create(:ci_pipeline, status: :running) } + + it 'returns detailed status for running pipeline' do + expect(pipeline.detailed_status.text).to eq 'running' + end + end + + context 'when pipeline is successful' do + let(:pipeline) { create(:ci_pipeline, status: :success) } + + it 'returns detailed status for successful pipeline' do + expect(pipeline.detailed_status.text).to eq 'passed' + end + end + + context 'when pipeline is failed' do + let(:pipeline) { create(:ci_pipeline, status: :failed) } + + it 'returns detailed status for failed pipeline' do + expect(pipeline.detailed_status.text).to eq 'failed' + end + end + + context 'when pipeline is canceled' do + let(:pipeline) { create(:ci_pipeline, status: :canceled) } + + it 'returns detailed status for canceled pipeline' do + expect(pipeline.detailed_status.text).to eq 'canceled' + end + end + + context 'when pipeline is skipped' do + let(:pipeline) { create(:ci_pipeline, status: :skipped) } + + it 'returns detailed status for skipped pipeline' do + expect(pipeline.detailed_status.text).to eq 'skipped' + end + end + + context 'when pipeline is successful but with warnings' do + let(:pipeline) { create(:ci_pipeline, status: :success) } + + before do + create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline) + end + + it 'retruns detailed status for successful pipeline with warnings' do + expect(pipeline.detailed_status.label).to eq 'passed with warnings' + end + end + end + describe '#cancelable?' do %i[created running pending].each do |status0| context "when there is a build #{status0}" do From 3ce2ba1afb1dad707e1e3ac2bb5ff856d06f621e Mon Sep 17 00:00:00 2001 From: basyura Date: Fri, 25 Nov 2016 23:09:50 +0900 Subject: [PATCH 025/140] fix display hook error message --- app/assets/javascripts/merge_request_widget.js.es6 | 2 +- changelogs/unreleased/issue_24020.yml | 4 ++++ spec/javascripts/merge_request_widget_spec.js | 12 ++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/issue_24020.yml diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6 index a55fe9df0b3..d9495e50388 100644 --- a/app/assets/javascripts/merge_request_widget.js.es6 +++ b/app/assets/javascripts/merge_request_widget.js.es6 @@ -101,7 +101,7 @@ urlSuffix = deleteSourceBranch ? '?deleted_source_branch=true' : ''; return window.location.href = window.location.pathname + urlSuffix; } else if (data.merge_error) { - return this.$widgetBody.html("

" + data.merge_error + "

"); + return _this.$widgetBody.html("

" + data.merge_error + "

"); } else { callback = function() { return merge_request_widget.mergeInProgress(deleteSourceBranch); diff --git a/changelogs/unreleased/issue_24020.yml b/changelogs/unreleased/issue_24020.yml new file mode 100644 index 00000000000..87310b75296 --- /dev/null +++ b/changelogs/unreleased/issue_24020.yml @@ -0,0 +1,4 @@ +--- +title: "fix display hook error message" +merge_request: 7775 +author: basyura diff --git a/spec/javascripts/merge_request_widget_spec.js b/spec/javascripts/merge_request_widget_spec.js index 62890f1ca96..6f91529db00 100644 --- a/spec/javascripts/merge_request_widget_spec.js +++ b/spec/javascripts/merge_request_widget_spec.js @@ -106,6 +106,18 @@ }); }); + describe('mergeInProgress', function() { + it('should display error with h4 tag', function() { + spyOn(this.class.$widgetBody, 'html').and.callFake(function(html) { + expect(html).toBe('

Sorry, something went wrong.

'); + }); + spyOn($, 'ajax').and.callFake(function(e) { + e.success({ merge_error: 'Sorry, something went wrong.' }); + }); + this.class.mergeInProgress(null); + }); + }); + return describe('getCIStatus', function() { beforeEach(function() { this.ciStatusData = { From ed4ec0dab7df042d53696ad118c9f0a829990802 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 5 Dec 2016 14:19:12 +0100 Subject: [PATCH 026/140] Add support for detailed status to status helpers --- app/helpers/ci_status_helper.rb | 53 +++++++++++-------- .../projects/ci/pipelines/_pipeline.html.haml | 5 +- app/views/projects/pipelines/_info.html.haml | 2 +- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index abcf84b4d15..ff507765255 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -6,7 +6,8 @@ module CiStatusHelper def ci_status_with_icon(status, target = nil) content = ci_icon_for_status(status) + ci_label_for_status(status) - klass = "ci-status ci-#{status}" + klass = "ci-status ci-#{status}" # TODO, add support for detailed status + if target link_to content, target, class: klass else @@ -15,11 +16,13 @@ module CiStatusHelper end def ci_label_for_status(status) + if detailed_status?(status) + return status.label + end + case status when 'success' 'passed' - when 'success_with_warnings' - 'passed with warnings' else status end @@ -32,25 +35,27 @@ module CiStatusHelper def ci_icon_for_status(status) icon_name = - case status - when 'success' - 'icon_status_success' - when 'success_with_warnings' - 'icon_status_warning' - when 'failed' - 'icon_status_failed' - when 'pending' - 'icon_status_pending' - when 'running' - 'icon_status_running' - when 'play' - 'icon_play' - when 'created' - 'icon_status_created' - when 'skipped' - 'icon_status_skipped' + if detailed_status?(status) + status.icon else - 'icon_status_canceled' + case status + when 'success' + 'icon_status_success' + when 'failed' + 'icon_status_failed' + when 'pending' + 'icon_status_pending' + when 'running' + 'icon_status_running' + when 'play' + 'icon_play' + when 'created' + 'icon_status_created' + when 'skipped' + 'icon_status_skipped' + else + 'icon_status_canceled' + end end custom_icon(icon_name) @@ -94,4 +99,10 @@ module CiStatusHelper class: klass, title: title, data: data end end + + def detailed_status?(status) + status.respond_to?(:text) && + status.respond_to?(:label) && + status.respond_to?(:icon) + end end diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 4c7b14a04db..00a30870dad 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -1,12 +1,13 @@ - status = pipeline.status +- detailed_status = pipeline.detailed_status - show_commit = local_assigns.fetch(:show_commit, true) - show_branch = local_assigns.fetch(:show_branch, true) %tr.commit %td.commit-link = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{status}" do - = ci_icon_for_status(status) - = ci_label_for_status(status) + = ci_icon_for_status(detailed_status) + = ci_label_for_status(detailed_status) %td = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 095bd254d6b..229bdfb0e8d 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -1,6 +1,6 @@ .page-content-header .header-main-content - = ci_status_with_icon(@pipeline.status) + = ci_status_with_icon(@pipeline.detailed_status) %strong Pipeline ##{@commit.pipelines.last.id} triggered #{time_ago_with_tooltip(@commit.authored_date)} by = author_avatar(@commit, size: 24) From c73a5d596f8b239a8f43d9825d893b96a2f7457a Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 5 Dec 2016 09:26:22 -0600 Subject: [PATCH 027/140] Truncate fix, remove unneeded admin-specific css --- app/assets/stylesheets/framework/blocks.scss | 5 --- app/assets/stylesheets/framework/common.scss | 10 ++++- app/assets/stylesheets/framework/wells.scss | 13 ++++++ app/assets/stylesheets/pages/admin.scss | 42 -------------------- app/views/admin/dashboard/index.html.haml | 12 +++--- 5 files changed, 28 insertions(+), 54 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 57db5eaa2b3..fbcb2659585 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -1,8 +1,3 @@ -.light-well { - background-color: $background-color; - padding: 15px; -} - .centered-light-block { text-align: center; color: $gl-gray; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 16646e33a4b..b662282aa1d 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -379,7 +379,9 @@ table { border-top: 1px solid $border-color; } -.hide-bottom-border { border-bottom: none !important; } +.hide-bottom-border { + border-bottom: none !important; +} .gl-accessibility { &:focus { @@ -396,3 +398,9 @@ table { z-index: 1; } } + +.str-truncated { + &-60 { + @include str-truncated(60%); + } +} diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss index 192939f4527..f2860dfe84d 100644 --- a/app/assets/stylesheets/framework/wells.scss +++ b/app/assets/stylesheets/framework/wells.scss @@ -43,3 +43,16 @@ background-color: $well-expand-item; } } + +.light-well { + background-color: $background-color; + padding: 15px; +} + +.well-centered { + h1 { + font-weight: normal; + text-align: center; + font-size: 48px; + } +} diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss index 44eac21b143..dbf9db39651 100644 --- a/app/assets/stylesheets/pages/admin.scss +++ b/app/assets/stylesheets/pages/admin.scss @@ -2,48 +2,6 @@ * Admin area * */ -.admin-dashboard { - .data { - a { - h1 { - line-height: 48px; - font-size: 48px; - padding: 20px; - text-align: center; - font-weight: normal; - } - } - } - - .str-truncated { - max-width: 60%; - } -} - -.admin-filter form { - .select2-container { - width: 100%; - } - - .controls { - margin-left: 130px; - } - - .form-actions { - padding-left: 130px; - background: $white-light; - } - - .visibility-levels { - .controls { - margin-bottom: 9px; - } - - i { - color: inherit; - } - } -} .broadcast-messages { .message { diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 1db2150f336..e51f4ac1d93 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -113,7 +113,7 @@ %hr .row .col-sm-4 - .light-well + .light-well.well-centered %h4 Projects .data = link_to admin_namespaces_projects_path do @@ -121,7 +121,7 @@ %hr = link_to('New Project', new_project_path, class: "btn btn-new") .col-sm-4 - .light-well + .light-well.well-centered %h4 Users .data = link_to admin_users_path do @@ -129,7 +129,7 @@ %hr = link_to 'New User', new_admin_user_path, class: "btn btn-new" .col-sm-4 - .light-well + .light-well.well-centered %h4 Groups .data = link_to admin_groups_path do @@ -143,7 +143,7 @@ %hr - @projects.each do |project| %p - = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated' + = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated-60' %span.light.pull-right #{time_ago_with_tooltip(project.created_at)} @@ -152,7 +152,7 @@ %hr - @users.each do |user| %p - = link_to [:admin, user], class: 'str-truncated' do + = link_to [:admin, user], class: 'str-truncated-60' do = user.name %span.light.pull-right #{time_ago_with_tooltip(user.created_at)} @@ -162,7 +162,7 @@ %hr - @groups.each do |group| %p - = link_to [:admin, group], class: 'str-truncated' do + = link_to [:admin, group], class: 'str-truncated-60' do = group.name %span.light.pull-right #{time_ago_with_tooltip(group.created_at)} From b93a7dc101d15b0a31aa10fd3335291e1348e5ca Mon Sep 17 00:00:00 2001 From: PotHix Date: Fri, 2 Dec 2016 20:13:11 -0200 Subject: [PATCH 028/140] Add excoveralls example There was no example for Elixir in gitlab-ci examples, this commit adds the missing example. excoveralls repository was pointing to an old example that doesn't exist anymore. --- app/views/projects/pipelines_settings/show.html.haml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/projects/pipelines_settings/show.html.haml b/app/views/projects/pipelines_settings/show.html.haml index 96221a20502..1f698558bce 100644 --- a/app/views/projects/pipelines_settings/show.html.haml +++ b/app/views/projects/pipelines_settings/show.html.haml @@ -86,6 +86,9 @@ %li tap --coverage-report=text-summary (NodeJS) - %code ^Statements\s*:\s*([^%]+) + %li + excoveralls (Elixir) - + %code \[TOTAL\]\s+(\d+\.\d+)% = f.submit 'Save changes', class: "btn btn-save" From cae90506fb3bdf8412d241a21a1f0fe2d96393b6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Mon, 5 Dec 2016 16:24:56 +0000 Subject: [PATCH 029/140] Fixed if statement to use global group & project variables --- app/views/shared/members/_member.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index aa5b39151e6..e67f7d5a352 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -45,7 +45,7 @@ = time_ago_with_tooltip(member.created_at) - if show_roles .controls.member-controls - - if show_controls && (member.respond_to?(:group) && @members) || (member.respond_to?(:project) && @project_members) + - if show_controls && (member.respond_to?(:group) && @group) || (member.respond_to?(:project) && @project) - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member From 4d26ab28a955885cfe8ae08917395cc7fc252ebf Mon Sep 17 00:00:00 2001 From: Jacopo Date: Sun, 27 Nov 2016 11:33:15 +0100 Subject: [PATCH 030/140] Fix Archived project merge requests add to group's Merge Requests counter This is done by: - Extending the IssuableFinder adding the non_archived option to the params - Overriding the #filter_params in the MergeRequestsAction - Passing the non_archived param in the nav/_group.html.haml navbar partial from the groups/merge_requests.html.haml --- .../concerns/merge_requests_action.rb | 7 ++++- app/finders/issuable_finder.rb | 6 ++++ app/finders/merge_requests_finder.rb | 1 + app/views/layouts/nav/_group.html.haml | 2 +- app/views/shared/issuable/_nav.html.haml | 10 +++---- ...3-archived-project-merge-request-count.yml | 4 +++ spec/features/groups/merge_requests_spec.rb | 30 ++++++++++++++++++- spec/finders/merge_requests_finder_spec.rb | 11 ++++++- 8 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/24733-archived-project-merge-request-count.yml diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index 6546a07b41c..fdb05bb3228 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -6,7 +6,12 @@ module MergeRequestsAction @label = merge_requests_finder.labels.first @merge_requests = merge_requests_collection - .non_archived .page(params[:page]) end + + private + + def filter_params + super.merge(non_archived: true) + end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 001c83ccb4b..7cf75cd6fc7 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -15,6 +15,7 @@ # search: string # label_name: string # sort: string +# non_archived: boolean # class IssuableFinder NONE = '0' @@ -38,6 +39,7 @@ class IssuableFinder items = by_author(items) items = by_label(items) items = by_due_date(items) + items = by_non_archived(items) sort(items) end @@ -353,6 +355,10 @@ class IssuableFinder end end + def by_non_archived(items) + params[:non_archived].present? ? items.non_archived : items + end + def current_user_related? params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me' end diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 3b254e7d9d5..8b82255445e 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -14,6 +14,7 @@ # search: string # label_name: string # sort: string +# non_archived: boolean # class MergeRequestsFinder < IssuableFinder def klass diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index f7edb47b666..f3539fd372d 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -31,7 +31,7 @@ = link_to merge_requests_group_path(@group), title: 'Merge Requests' do %span Merge Requests - - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute + - merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute %span.badge.count= number_with_delimiter(merge_requests.count) = nav_link(controller: [:group_members]) do = link_to group_group_members_path(@group), title: 'Members' do diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml index 5527a2f889a..0af92b59584 100644 --- a/app/views/shared/issuable/_nav.html.haml +++ b/app/views/shared/issuable/_nav.html.haml @@ -4,22 +4,22 @@ %ul.nav-links.issues-state-filters %li{class: ("active" if params[:state] == 'opened')} - = link_to page_filter_path(state: 'opened', label: true), title: "Filter by #{page_context_word} that are currently opened." do + = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do #{issuables_state_counter_text(type, :opened)} - if type == :merge_requests %li{class: ("active" if params[:state] == 'merged')} - = link_to page_filter_path(state: 'merged', label: true), title: 'Filter by merge requests that are currently merged.' do + = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do #{issuables_state_counter_text(type, :merged)} %li{class: ("active" if params[:state] == 'closed')} - = link_to page_filter_path(state: 'closed', label: true), title: 'Filter by merge requests that are currently closed and unmerged.' do + = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do #{issuables_state_counter_text(type, :closed)} - else %li{class: ("active" if params[:state] == 'closed')} - = link_to page_filter_path(state: 'closed', label: true), title: 'Filter by issues that are currently closed.' do + = link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do #{issuables_state_counter_text(type, :closed)} %li{class: ("active" if params[:state] == 'all')} - = link_to page_filter_path(state: 'all', label: true), title: "Show all #{page_context_word}." do + = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do #{issuables_state_counter_text(type, :all)} diff --git a/changelogs/unreleased/24733-archived-project-merge-request-count.yml b/changelogs/unreleased/24733-archived-project-merge-request-count.yml new file mode 100644 index 00000000000..2bc7e91825a --- /dev/null +++ b/changelogs/unreleased/24733-archived-project-merge-request-count.yml @@ -0,0 +1,4 @@ +--- +title: Fix Archived project merge requests add to group's Merge Requests +merge_request: 7790 +author: Jacopo Beschi @jacopo-beschi diff --git a/spec/features/groups/merge_requests_spec.rb b/spec/features/groups/merge_requests_spec.rb index a2791b57544..30b80aa82b0 100644 --- a/spec/features/groups/merge_requests_spec.rb +++ b/spec/features/groups/merge_requests_spec.rb @@ -2,7 +2,35 @@ require 'spec_helper' feature 'Group merge requests page', feature: true do let(:path) { merge_requests_group_path(group) } - let(:issuable) { create(:merge_request, source_project: project, target_project: project, title: "this is my created issuable")} + let(:issuable) { create(:merge_request, source_project: project, target_project: project, title: 'this is my created issuable') } include_examples 'project features apply to issuables', MergeRequest + + context 'archived issuable' do + let(:project_archived) { create(:project, group: group, merge_requests_access_level: ProjectFeature::ENABLED, archived: true) } + let(:issuable_archived) { create(:merge_request, source_project: project_archived, target_project: project_archived, title: 'issuable of an archived project') } + let(:access_level) { ProjectFeature::ENABLED } + let(:user) { user_in_group } + + before do + issuable_archived + visit path + end + + it 'hides archived merge requests' do + expect(page).to have_content(issuable.title) + expect(page).not_to have_content(issuable_archived.title) + end + + it 'ignores archived merge request count badges in navbar' do + expect( page.find('[title="Merge Requests"] span.badge.count').text).to eq("1") + end + + it 'ignores archived merge request count badges in state-filters' do + expect(page.find('#state-opened span.badge').text).to eq("1") + expect(page.find('#state-merged span.badge').text).to eq("0") + expect(page.find('#state-closed span.badge').text).to eq("0") + expect(page.find('#state-all span.badge').text).to eq("1") + end + end end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 535aabfc18d..88361e27102 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -6,14 +6,17 @@ describe MergeRequestsFinder do let(:project1) { create(:project) } let(:project2) { create(:project, forked_from_project: project1) } + let(:project3) { create(:project, forked_from_project: project1, archived: true) } let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) } let!(:merge_request2) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1, state: 'closed') } let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2) } + let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) } before do project1.team << [user, :master] project2.team << [user, :developer] + project3.team << [user, :developer] project2.team << [user2, :developer] end @@ -21,7 +24,7 @@ describe MergeRequestsFinder do it 'filters by scope' do params = { scope: 'authored', state: 'opened' } merge_requests = MergeRequestsFinder.new(user, params).execute - expect(merge_requests.size).to eq(2) + expect(merge_requests.size).to eq(3) end it 'filters by project' do @@ -29,5 +32,11 @@ describe MergeRequestsFinder do merge_requests = MergeRequestsFinder.new(user, params).execute expect(merge_requests.size).to eq(1) end + + it 'filters by non_archived' do + params = { non_archived: true } + merge_requests = MergeRequestsFinder.new(user, params).execute + expect(merge_requests.size).to eq(3) + end end end From a219158e77b70bdae43b707dedc7b2bc4f930280 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 5 Dec 2016 09:59:30 -0600 Subject: [PATCH 031/140] Remove admin.scss --- app/assets/stylesheets/framework.scss | 1 + app/assets/stylesheets/framework/blocks.scss | 4 + .../framework/broadcast-messages.scss | 21 +++ app/assets/stylesheets/framework/common.scss | 4 + app/assets/stylesheets/framework/lists.scss | 39 +++++- app/assets/stylesheets/framework/tables.scss | 17 +++ app/assets/stylesheets/pages/admin.scss | 126 ------------------ app/assets/stylesheets/pages/pipelines.scss | 6 + app/views/admin/abuse_reports/index.html.haml | 10 +- app/views/admin/users/_user.html.haml | 8 +- app/views/admin/users/index.html.haml | 2 +- 11 files changed, 99 insertions(+), 139 deletions(-) create mode 100644 app/assets/stylesheets/framework/broadcast-messages.scss delete mode 100644 app/assets/stylesheets/pages/admin.scss diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 4aaff7d04f1..694fcd4b59d 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -40,3 +40,4 @@ @import "framework/blank"; @import "framework/wells.scss"; @import "framework/page-header.scss"; +@import "framework/broadcast-messages"; diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index fbcb2659585..95c02499271 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -269,6 +269,10 @@ } } + .emoji-icon { + display: inline-block; + } + @media(max-width: $screen-xs-max) { margin-top: 50px; text-align: center; diff --git a/app/assets/stylesheets/framework/broadcast-messages.scss b/app/assets/stylesheets/framework/broadcast-messages.scss new file mode 100644 index 00000000000..9b54fb94cdc --- /dev/null +++ b/app/assets/stylesheets/framework/broadcast-messages.scss @@ -0,0 +1,21 @@ +.broadcast-message { + @extend .alert-warning; + padding: 10px; + text-align: center; + + div, + p { + display: inline; + margin: 0; + + a { + color: inherit; + text-decoration: underline; + } + } +} + +.broadcast-message-preview { + @extend .broadcast-message; + margin-bottom: 20px; +} diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index b662282aa1d..600bf17259b 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -403,4 +403,8 @@ table { &-60 { @include str-truncated(60%); } + + &-100 { + @include str-truncated(100%); + } } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index db8677433bb..ed4b60faf92 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -106,13 +106,13 @@ ul.task-list { } } +// Generic content list ul.content-list { @include basic-list; - margin: 0; padding: 0; - > li { + li { border-color: $table-border-color; font-size: $list-font-size; color: $list-text-color; @@ -193,6 +193,41 @@ ul.content-list { } } +// Content list using flexbox +.flex-list { + .flex-row { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + white-space: nowrap; + } + + .row-main-content { + flex: 1 1 auto; + overflow: hidden; + padding-right: 8px; + } + + .row-title { + font-weight: 600; + } + + .row-second-line { + display: block; + } + + .dropdown { + .btn-block { + margin-bottom: 0; + line-height: inherit; + } + } + + .label-default { + color: $btn-transparent-color; + } +} + .panel > .content-list > li { padding: $gl-padding-top $gl-padding; diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index a5f36c177fc..5d0ca63ea08 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -34,6 +34,10 @@ table { background-color: $background-color; font-weight: normal; border-bottom: none; + + &.wide { + width: 55%; + } } td { @@ -42,3 +46,16 @@ table { } } } + +.responsive-table { + @media (max-width: $screen-sm-max) { + th { + width: 100%; + } + + td { + width: 100%; + float: left; + } + } +} diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss deleted file mode 100644 index dbf9db39651..00000000000 --- a/app/assets/stylesheets/pages/admin.scss +++ /dev/null @@ -1,126 +0,0 @@ -/** - * Admin area - * - */ - -.broadcast-messages { - .message { - line-height: 2; - } -} - -.broadcast-message { - @extend .alert-warning; - padding: 10px; - text-align: center; - - > div, - p { - display: inline; - margin: 0; - - a { - color: inherit; - text-decoration: underline; - } - } -} - -.broadcast-message-preview { - @extend .broadcast-message; - margin-bottom: 20px; -} - -// Users List - -.users-list { - .user-row { - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - white-space: nowrap; - } - - .user-details { - flex: 1 1 auto; - overflow: hidden; - padding-right: 8px; - } - - .user-name { - display: inline-block; - font-weight: 600; - } - - .user-name, - .user-email { - overflow: hidden; - text-overflow: ellipsis; - } - - .dropdown { - .btn-block { - margin-bottom: 0; - line-height: inherit; - } - } - - .label-default { - color: $btn-transparent-color; - } -} - -.abuse-reports { - .table { - table-layout: fixed; - } - - .subheading { - padding-bottom: $gl-padding; - } - - .message { - word-wrap: break-word; - } - - .btn { - white-space: normal; - padding: $gl-btn-padding; - } - - th { - width: 15%; - - &.wide { - width: 55%; - } - } - - @media (max-width: $screen-sm-max) { - th { - width: 100%; - } - - td { - width: 100%; - float: left; - } - } - - .no-reports { - .emoji-icon { - margin-left: $btn-side-margin; - margin-top: 3px; - } - - span { - font-size: 18px; - } - } -} - -.admin-builds-table { - .ci-table td:last-child { - min-width: 120px; - } -} diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 0027d2caf22..08062b85504 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -280,6 +280,12 @@ } } +.admin-builds-table { + .ci-table td:last-child { + min-width: 120px; + } +} + // Pipeline visualization .toggle-pipeline-btn { diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml index 7bbc75db9ff..c4b748d0ab8 100644 --- a/app/views/admin/abuse_reports/index.html.haml +++ b/app/views/admin/abuse_reports/index.html.haml @@ -4,7 +4,7 @@ .abuse-reports - if @abuse_reports.present? .table-holder - %table.table + %table.table.responsive-table %thead.hidden-sm.hidden-xs %tr %th User @@ -13,8 +13,6 @@ %th Action = render @abuse_reports - else - .no-reports - %span.pull-left - There are no abuse reports! - .pull-left - = emoji_icon 'tada' + .empty-state + .text-center + %h4 There are no abuse reports! #{emoji_icon 'tada'} diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml index 4bf1c9cde3c..2d9588f9d27 100644 --- a/app/views/admin/users/_user.html.haml +++ b/app/views/admin/users/_user.html.haml @@ -1,8 +1,8 @@ -%li.user-row +%li.flex-row .user-avatar = image_tag avatar_icon(user), class: "avatar", alt: '' - .user-details - .user-name + .row-main-content + .user-name.row-title.str-truncated-100 = link_to user.name, [:admin, user] - if user.blocked? %span.label.label-danger blocked @@ -12,7 +12,7 @@ %span.label.label-default External - if user == current_user %span It's you! - .user-email + .row-second-line.str-truncated-100 = mail_to user.email, user.email .controls = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn' diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index d3038ae644f..4dc44225d49 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -68,7 +68,7 @@ %small.badge= number_with_delimiter(User.without_projects.count) .fade-right - %ul.users-list.content-list + %ul.flex-list.content-list - if @users.empty? %li .nothing-here-block No users found. From 1123057ab792ac73b1611f4d3a9faf79369dd6da Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Wed, 26 Oct 2016 23:21:50 +0200 Subject: [PATCH 032/140] Feature: delegate all open discussions to Issue When a merge request can only be merged when all discussions are resolved. This feature allows to easily delegate those discussions to a new issue, while marking them as resolved in the merge request. The user is presented with a new issue, prepared with mentions of all unresolved discussions, including the first unresolved note of the discussion, time and link to the note. When the issue is created, the discussions in the merge request will get a system note directing the user to the newly created issue. --- .../projects/discussions_controller.rb | 4 +- app/controllers/projects/issues_controller.rb | 15 +- app/models/discussion.rb | 4 + app/models/merge_request.rb | 8 ++ app/models/note.rb | 2 +- app/services/discussions/base_service.rb | 4 + app/services/discussions/resolve_service.rb | 24 ++++ app/services/issuable_base_service.rb | 5 +- app/services/issues/base_service.rb | 8 ++ app/services/issues/build_service.rb | 50 +++++++ app/services/issues/create_service.rb | 14 +- app/services/system_note_service.rb | 8 ++ .../open/_unresolved_discussions.html.haml | 6 +- app/views/shared/issuable/_form.html.haml | 15 ++ .../unreleased/23589-open-issue-for-mr.yml | 5 + doc/api/issues.md | 3 +- .../img/preview_issue_for_discussions.png | Bin 0 -> 178361 bytes .../merge_request_discussion_resolution.md | 21 ++- lib/api/issues.rb | 32 +++-- .../projects/issues_controller_spec.rb | 60 ++++++++ ...e_for_discussions_in_merge_request_spec.rb | 76 ++++++++++ spec/models/discussion_spec.rb | 9 ++ spec/models/merge_request_spec.rb | 40 ++++++ spec/requests/api/issues_spec.rb | 26 ++++ .../discussions/resolve_service_spec.rb | 52 +++++++ spec/services/issues/build_service_spec.rb | 130 ++++++++++++++++++ spec/services/issues/create_service_spec.rb | 43 ++++++ spec/services/system_note_service_spec.rb | 28 ++++ 28 files changed, 670 insertions(+), 22 deletions(-) create mode 100644 app/services/discussions/base_service.rb create mode 100644 app/services/discussions/resolve_service.rb create mode 100644 app/services/issues/build_service.rb create mode 100644 changelogs/unreleased/23589-open-issue-for-mr.yml create mode 100644 doc/user/project/merge_requests/img/preview_issue_for_discussions.png create mode 100644 spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb create mode 100644 spec/services/discussions/resolve_service_spec.rb create mode 100644 spec/services/issues/build_service_spec.rb diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb index d174e1145a7..148e39630e3 100644 --- a/app/controllers/projects/discussions_controller.rb +++ b/app/controllers/projects/discussions_controller.rb @@ -5,9 +5,7 @@ class Projects::DiscussionsController < Projects::ApplicationController before_action :authorize_resolve_discussion! def resolve - discussion.resolve!(current_user) - - MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request) + Discussions::ResolveService.new(project, current_user, merge_request: merge_request).execute(discussion) render json: { resolved_by: discussion.resolved_by.try(:name), diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 4aea7bb62c4..4f66e01e0f7 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -46,8 +46,9 @@ class Projects::IssuesController < Projects::ApplicationController params[:issue] ||= ActionController::Parameters.new( assignee_id: "" ) + build_params = issue_params.merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions) + @issue = @noteable = Issues::BuildService.new(project, current_user, build_params).execute - @issue = @noteable = @project.issues.new(issue_params) respond_with(@issue) end @@ -75,7 +76,9 @@ class Projects::IssuesController < Projects::ApplicationController end def create - @issue = Issues::CreateService.new(project, current_user, issue_params.merge(request: request)).execute + extra_params = { request: request, + merge_request_for_resolving_discussions: merge_request_for_resolving_discussions } + @issue = Issues::CreateService.new(project, current_user, issue_params.merge(extra_params)).execute respond_to do |format| format.html do @@ -169,6 +172,14 @@ class Projects::IssuesController < Projects::ApplicationController alias_method :awardable, :issue alias_method :spammable, :issue + def merge_request_for_resolving_discussions + return unless merge_request_iid = params[:merge_request_for_resolving_discussions] + + @merge_request_for_resolving_discussions ||= MergeRequestsFinder.new(current_user, project_id: project.id). + execute. + find_by(iid: merge_request_iid) + end + def authorize_read_issue! return render_404 unless can?(current_user, :read_issue, @issue) end diff --git a/app/models/discussion.rb b/app/models/discussion.rb index 75a85563235..bbe813db823 100644 --- a/app/models/discussion.rb +++ b/app/models/discussion.rb @@ -88,6 +88,10 @@ class Discussion @first_note ||= @notes.first end + def first_note_to_resolve + @first_note_to_resolve ||= notes.detect(&:to_be_resolved?) + end + def last_note @last_note ||= @notes.last end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index bfb016df46d..a6fc9bb120d 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -480,6 +480,14 @@ class MergeRequest < ActiveRecord::Base @diff_discussions ||= self.notes.diff_notes.discussions end + def resolvable_discussions + @resolvable_discussions ||= diff_discussions.select(&:to_be_resolved?) + end + + def discussions_can_be_resolved_by?(user) + resolvable_discussions.all? { |discussion| discussion.can_resolve?(user) } + end + def find_diff_discussion(discussion_id) notes = self.notes.diff_notes.where(discussion_id: discussion_id).fresh.to_a return if notes.empty? diff --git a/app/models/note.rb b/app/models/note.rb index 5b50ca285c3..08bd08743ef 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -99,7 +99,7 @@ class Note < ActiveRecord::Base end def discussions - Discussion.for_notes(all) + Discussion.for_notes(fresh) end def grouped_diff_discussions diff --git a/app/services/discussions/base_service.rb b/app/services/discussions/base_service.rb new file mode 100644 index 00000000000..e4dfe6e71bb --- /dev/null +++ b/app/services/discussions/base_service.rb @@ -0,0 +1,4 @@ +module Discussions + class BaseService < ::BaseService + end +end diff --git a/app/services/discussions/resolve_service.rb b/app/services/discussions/resolve_service.rb new file mode 100644 index 00000000000..0437195f588 --- /dev/null +++ b/app/services/discussions/resolve_service.rb @@ -0,0 +1,24 @@ +module Discussions + class ResolveService < Discussions::BaseService + def execute(one_or_more_discussions) + Array(one_or_more_discussions).each { |discussion| resolve_discussion(discussion) } + end + + def resolve_discussion(discussion) + return unless discussion.can_resolve?(current_user) + + discussion.resolve!(current_user) + + MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(merge_request) + SystemNoteService.discussion_continued_in_issue(discussion, project, current_user, follow_up_issue) if follow_up_issue + end + + def merge_request + params[:merge_request] + end + + def follow_up_issue + params[:follow_up_issue] + end + end +end diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index ce68e433ab8..b5f63cc5a1a 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -120,9 +120,10 @@ class IssuableBaseService < BaseService def merge_slash_commands_into_params!(issuable) description, command_params = SlashCommands::InterpretService.new(project, current_user). - execute(params[:description], issuable) + execute(params[:description], issuable) - params[:description] = description + # Avoid a description already set on an issuable to be overwritten by a nil + params[:description] = description if params.has_key?(:description) params.merge!(command_params) end diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 9ea3ce084ba..742e834df97 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -1,5 +1,13 @@ module Issues class BaseService < ::IssuableBaseService + attr_reader :merge_request_for_resolving_discussions + + def initialize(*args) + super + + @merge_request_for_resolving_discussions ||= params.delete(:merge_request_for_resolving_discussions) + end + def hook_data(issue, action) issue_data = issue.to_hook_data(current_user) issue_url = Gitlab::UrlBuilder.build(issue) diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb new file mode 100644 index 00000000000..a63982f60c8 --- /dev/null +++ b/app/services/issues/build_service.rb @@ -0,0 +1,50 @@ +module Issues + class BuildService < Issues::BaseService + def execute + @issue = project.issues.new(issue_params) + end + + def issue_params_with_info_from_merge_request + return {} unless merge_request_for_resolving_discussions + + { title: title_from_merge_request, description: description_from_merge_request } + end + + def title_from_merge_request + "Follow-up from \"#{merge_request_for_resolving_discussions.title}\"" + end + + def description_from_merge_request + if merge_request_for_resolving_discussions.resolvable_discussions.empty? + return "There are no unresolved discussions. "\ + "Review the conversation in #{merge_request_for_resolving_discussions.to_reference}" + end + + description = "The following discussions from #{merge_request_for_resolving_discussions.to_reference} should be addressed:" + [description, *items_for_discussions].join("\n\n") + end + + def items_for_discussions + merge_request_for_resolving_discussions.resolvable_discussions.map { |discussion| item_for_discussion(discussion) } + end + + def item_for_discussion(discussion) + first_note = discussion.first_note_to_resolve + other_note_count = discussion.notes.size - 1 + creation_time = first_note.created_at.to_s(:medium) + note_url = Gitlab::UrlBuilder.build(first_note) + + discussion_info = "- [ ] #{first_note.author.to_reference} commented in a discussion on [#{creation_time}](#{note_url}): " + discussion_info << " (+#{other_note_count} #{'comment'.pluralize(other_note_count)})" if other_note_count > 0 + + note_without_block_quotes = Banzai::Filter::BlockquoteFenceFilter.new(first_note.note).call + quote = ">>>\n#{note_without_block_quotes}\n>>>" + + [discussion_info, quote].join("\n\n") + end + + def issue_params + @issue_params ||= issue_params_with_info_from_merge_request.merge(params.slice(:title, :description)) + end + end +end diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index ea1690f3e38..d2eb46ac41b 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -4,7 +4,8 @@ module Issues @request = params.delete(:request) @api = params.delete(:api) - @issue = project.issues.new + issue_attributes = params.merge(merge_request_for_resolving_discussions: merge_request_for_resolving_discussions) + @issue = BuildService.new(project, current_user, issue_attributes).execute create(@issue) end @@ -18,6 +19,17 @@ module Issues notification_service.new_issue(issuable, current_user) todo_service.new_issue(issuable, current_user) user_agent_detail_service.create + + if merge_request_for_resolving_discussions.try(:discussions_can_be_resolved_by?, current_user) + resolve_discussions_in_merge_request(issuable) + end + end + + def resolve_discussions_in_merge_request(issue) + Discussions::ResolveService.new(project, current_user, + merge_request: merge_request_for_resolving_discussions, + follow_up_issue: issue). + execute(merge_request_for_resolving_discussions.resolvable_discussions) end private diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 3cf6467804f..8b48d90f60b 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -163,6 +163,14 @@ module SystemNoteService create_note(noteable: merge_request, project: project, author: author, note: body) end + def discussion_continued_in_issue(discussion, project, author, issue) + body = "Added #{issue.to_reference} to continue this discussion" + note_attributes = discussion.reply_attributes.merge(project: project, author: author, note: body) + note_attributes[:type] = note_attributes.delete(:note_type) + + create_note(note_attributes) + end + # Called when the title of a Noteable is changed # # noteable - Noteable object that responds to `title` diff --git a/app/views/projects/merge_requests/widget/open/_unresolved_discussions.html.haml b/app/views/projects/merge_requests/widget/open/_unresolved_discussions.html.haml index 35d5677ee37..e094f97f3b6 100644 --- a/app/views/projects/merge_requests/widget/open/_unresolved_discussions.html.haml +++ b/app/views/projects/merge_requests/widget/open/_unresolved_discussions.html.haml @@ -3,4 +3,8 @@ This merge request has unresolved discussions %p - Please resolve these discussions to allow this merge request to be merged. \ No newline at end of file + Please resolve these discussions + - if @project.issues_enabled? && can?(current_user, :create_issue, @project) + or + = link_to "open an issue to resolve them later", new_namespace_project_issue_path(@project.namespace, @project, merge_request_for_resolving_discussions: @merge_request.iid) + to allow this merge request to be merged. diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 2f05093f435..bdb00bfa33c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -42,6 +42,21 @@ = render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form +- if @merge_request_for_resolving_discussions + .form-group + .col-sm-10.col-sm-offset-2 + - if @merge_request_for_resolving_discussions.discussions_can_be_resolved_by?(current_user) + = icon('exclamation-triangle') + Creating this issue will mark all discussions in + = link_to @merge_request_for_resolving_discussions.to_reference, merge_request_path(@merge_request_for_resolving_discussions) + as resolved. + = hidden_field_tag 'merge_request_for_resolving_discussions', @merge_request_for_resolving_discussions.iid + - else + = icon('exclamation-triangle') + You can't automatically mark all discussions in + = link_to @merge_request_for_resolving_discussions.to_reference, merge_request_path(@merge_request_for_resolving_discussions) + as resolved. Ask someone with sufficient rights to resolve the them. + - is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) .row-content-block{class: (is_footer ? "footer-block" : "middle-block")} - if issuable.new_record? diff --git a/changelogs/unreleased/23589-open-issue-for-mr.yml b/changelogs/unreleased/23589-open-issue-for-mr.yml new file mode 100644 index 00000000000..cea48b85254 --- /dev/null +++ b/changelogs/unreleased/23589-open-issue-for-mr.yml @@ -0,0 +1,5 @@ +--- +title: Resolve all discussions in a merge request by creating an issue collecting + them +merge_request: 7180 +author: Bob Van Landuyt diff --git a/doc/api/issues.md b/doc/api/issues.md index 16f8e32c82a..119125bcd3d 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -330,6 +330,7 @@ POST /projects/:id/issues | `labels` | string | no | Comma-separated label names for an issue | | `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) | | `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` | +| `merge_request_for_resolving_discussions` | integer | no | The IID of a merge request in which to resolve all issues. This will fill the issue with a default description and mark all discussions as resolved. When passing a description or title, these values will take precedence over the default values. | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues?title=Issues%20with%20auth&labels=bug @@ -506,7 +507,7 @@ Example response: ## Subscribe to an issue -Subscribes the authenticated user to an issue to receive notifications. +Subscribes the authenticated user to an issue to receive notifications. If the user is already subscribed to the issue, the status code `304` is returned. diff --git a/doc/user/project/merge_requests/img/preview_issue_for_discussions.png b/doc/user/project/merge_requests/img/preview_issue_for_discussions.png new file mode 100644 index 0000000000000000000000000000000000000000..9fdd387676c095fdb3383378a74ec92ee0b337b8 GIT binary patch literal 178361 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,`C86ew}hcCAw{L<-R2rt5;eVW4e|Bz9_+G;-*L* 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 aH-r|Gy7;|7*zAly{?T(9aS7Tacr|Vw(T$D0%XJ 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%0D8OBiu&7u>XrD>wlP2krdrF zZ@=CY8V{s7yBzjNKNfsEv?{%py2Bq)@-~>WW&inTlf^qtR)~~(tPRnTPHu~>GYPKt zToHkJ3EwoAR-jMdo>O4eJd4zENBjIz-#4uTx^}B#n)4cQW_vyFhhU1z?mxyVEz^C! za=)0eq=PqPIr2QvUhhsv#PvvtU{X>{K2@;W*~w=SG~e53}AF)Az^*LME7j@<^f z*5WG-V@ZUQuYc{|ea4P06SZmX>VX(~M+Ruf2v*rl_|(H(u@YhH)9vl57^iLZo>&c+ zI&#o*K1jPiGe=u_^=&-wTPIkj6^gBk%lLFWW_=F!IG5{u@79UzONjFHyX<)4mrbM& zZhXePLJ{1cof6^vS^A>eDa*Xp_7bLw2Vkrwjk9MsZ_rbMxXTt4QyqL1krt!X+2BuR zUA^X6@OV4TaE*u7zK!O)B7H4n146GPLo4UAV}dzvg+2JUK~Z1v|CIebMJB0~$t&4g zDd9+Tn{NZpp9P>njr{}7g@Wa-32j=t;KN?`19Vd{qwzCt!EoqnPNVlJiH5|i8TWJ%0I}XVA;AH9}s5lHv#JRY~wbG(gTya z_9mqOZmbzDy; zQk#t({qkCnda0(*!;mBX$D%uhq=mIG~s%3QrtB)8vruE{m71_ zVj)vkrjAU>HXEP~q-^_ExKY$4y%)nizQCIwBj*(SDlmnD49)uijx4*|LvfbA?5=_v zsqU^N2!v+$$Nt#U}mqFl!&T&9@WB^yTV_ zh1Kf4fj~VGZ$mT&EqOS~bFoA3FV`uy8wvg)G<;x@Qd>@v{M|#Chyq_lY+pQ4Jv?M$ zmVu*}4ZNG((CParj@H*Nzu!PRA)f-wOIMB4+@ei&EQDJ;YLnEd%wTHN9K8d}o}J4N z$&@|u;?!7Y#VulYMJ-3GD{Kqog(H-E(cmah0n zbUz@Q9(#Xz6JpG^83R1-0=j^)yH=}rc3i!Qk53*>Z}*6%bl|42l&Y`w?Y zQckN4nM4l(A^X@c=9H6DswK8lIHFG@b_uH(g~wECvUH0yZiL>Zo=p#Go9Ds~@=F@ zFPZl}J>7Yg~0;59Jn?sSyr*^ za9c>xu-%7wf5x-@)UE!$1F~8G7$JWbK1BIu@l448I#d>$4rwg?_I+zv#^hxsMO(S@nM8&ZTJbFYR6MQp;P{hsnu}U= zOP#9)(x-68Yhwl-HS-f9-I^UG8S%`q0bc(eG8g_CGHWF2cJrBDh0O1h5CJiV2g^%q z!SobD%4oXx``r60cl9`kuv4~V@)>XivEnp4z;+vWE6We1_;d}l!t)Rx6L`<*k6hE+ zBc0`U_!h$s^6$(s)I49KH9lHfKgNvC_naPQmFRj!*11$wZrq$*5R3-Ho>CbHIStd= zSG_)l)Yvq;msh<$TY?K|(!Q+kYo?VI@%NL*B{kY~Tsav`W-NAiMcf5^kx3+@L4(v1 z+Jjz;FNCA(NeMLJBLk?1V`=YM)F?Ix7@ED%G%;97{@HB&@@s2csS9u5rrS?NvfH^v z$FW5PM7PljS=^jYUd(Z@Ghp-D%B2cHQ(DL6gr_3DIAlv`Hp)6wmU(Ya(xuTC@cVQpo`hWu__c?T1>dO znK%+O&_bw$^U_u*ablKJL-VTDi8-c7r@t$$0y7di26i)@R`r>s6~DnJWrmBRaHqTu z-sP)enA{JI1{$JIyqbDcjWbztxem?U9?#(iC`?Q&qTAE7f)^{8U;1vp;Ae$Vt;BL# zMvj{!Y?GKIVbSV8iVin5BoSxD!DrV~!7kwNhA zVmhsMH*|fyVKD%XPu(^p-DT)P7~Xu&c4FkD#}?=1t^TdKAbd7>Q8n@WswM9H^0|Vb zN}8-kc&Z&L*`w$s&U>9|<|k~DO9xKaDg2)XW^Ci>!RDW>4p;Do)_Rz%S61pNifz_z zip)K_8Dem=v8A)zTYbLi1}Tf5XP$P_@q&iZECoS@{0o*w{c!}ksP6WJJ}}Bu{sHI- z8ppR>`RnCpobkbPMwOad3{zJIm^VlCn9~I-2r$wNR6(WXp{%2_&3v2{r&Zv zr^4sOF^^ zyE<@8h9EjW7Qv9dk|Di^mt!Wx2vKzu>U0}5Tx zbd>4OCbJ&Fs0-h_7wO=Ip8{TQmqSoZDCfI8A&{YZFZTg81&y`{q4^o9mu9 zZcWGBh;8MOi^od~3MLmWc($?t(Po)Rh!5j(lwL42v!%-7#lZPYQQ$8K~u%1re|w7MXEoJav}7TMF3zoo{*o< zwfu~BI=5t^=kfs*jIhP_I?W~`R`#z;xDCbBZ9*kkCt_XPc^-Yo6svpF}JdHN4-D=4YV5Lcdjl&67O#UuSG z5^sRe(CVlSYxgDH;mlS5el(ers&AuIB!^uoZ4kP4+dEO_A}S&OffMInyaJ0q=lF2h zy?AW5PigAvsyhtba;JfcgHjYUl37rD#vrZc-x86va3eZ8+cf`E_AADK`suQn* z6wj6SYzw}Uf(#h=TQS8qvkAu+9ukixVD`j= zM<{>Ye@aw#8LSgsSvEC)Degrz|5#5DOH0K60kbqNCDu(aJux)bKQ+pBN=)B@?T~G& zLYjLsBDvmxYd+34&qm}(6%HyX0%4Mp9t|oZIjMb6U&F%zw2HCnZ4LCi+`L)7NB8$D zH)~bK;tin*)yOc&^h2O-tCiXNt)-RA%P zt%Jj}?gQKupquy8k0uu_G zqiDAM0BtxHzON7;oIjgHu&9}xhQrswXv{wU_8UNcw=h2KZX?P_dTlOyB7Ko>=}1=_ zg2B3&7>x+w$=7OTfG5v2Qv_KSa;ZEhg-Dt9ru)fo=k&(fdMNzl7SvSUQe2&XqiRM* zz}}{fZk6j7qM@AODW;skeQN25qgWd->!umeg$^?p4Gm(~hXh`QsTL=Jm?3hP*v(PD zp#X{N#5sDFyga|e6$&s;QVxj0%ua?r_t@|`0yqqWhi?iSVT=xB_r{ z!A+#OxN)>23Ju zU^vsf@9Rsaof)ChWlz-IC`F$~?LeQbGM?1@u9ec5K1B13+4dw2J2>;Os=q2ySl5ya zTgKt_+h^g%lkoOaVy%Qa9hMm@n`6G2`L$i=vu7*JOhQ;?$EC+n7Z~}Htpc@@;-s@7 zMMXcGpC@{CL^@|7!%ck;=uCKQ=Ou{_IU)>pB@D!cV*8QA+3ktYOrxaUtzjc--t-8C zg2IQqqZ(r&u~x+pyl+lQaxMPt@BCBwab;$>JmcBF_kiqVpWGFSYQS}RyN9xbAx(;l zhC3oeD$Lgh^FDldqAo$LNg}Cc(iwlZZ;#aM^!+QScCwNg^z>= zpF{7Obr5yDzV(3VE~ZCpt1(8Tw(T=&iF;4f7w>AA-+Lj|y|uv(u}p{9raPZ(0P<(^ zEnPv$O-`QuQ(FU;J7d5@LCc+7P%=q}^syt*T}~lbW(5l%Oh9pp%nOxQbI%DsN8O(x ziE8~0GW((oFPdqfoLj=vKL&MnBWaFdy+EbBUYj_Ce)jMSh~}!+0mKUdF%1^$tK5jk ziQ+v1VI4irXLZL%Dlt#vo#O~)`y*$^fTd7itgic}2lbngmUz5|wpi4>y|~}93Fafw zKW?CZUMJn1=rEAWQLG|;x1cC;V_NEI?AI@fIg%d<0zIRB5MdkaT=QgX`#PB~iJnVi z+F|N&@#w{rYQER^sr2cLM^BvEA_0PL4j2aGYT*k72mzU`$7&V{xnI7RXs&+N#z!K8555#txqHg)*A;zB%Xt+BT z91*{ld;Gdj@g8-oV)Hw4&KCR5jZtchPXiA7?ywN@A4rQ;(UX92DFg)15Q%XS@b`1j z|5D6+XWttu*^RIKM2Y&5^jA04z{091P8u~P)nsXONv&%?dwxB;gd_*`3A}PNw#_x} z5FIukM4Q?-VpW2T^}g#7`pE%QuA@osfIsR^N}X50W=ot)21Od6PP3n<^p(ZDAfodq z&Ej0kjh7K+Mb#C<6n`!0uXRlrzy8z_gxl-4BVy?1%Wzvvh%k55?xBb3dEAW@gTOP|YZ z_d+0VV36o8M&zAx3o7H>;iSj14YLhTO0 zPsM(FnvMGPck!%60w_P@l^c%M-$#%14UUn!mCOI{tA4H0zphQdy)m7@92~=#NJk>| ztm)lO)*>&R2}eJW8za z;jgXoKVOz7`R6j^1xr7^!;W6?Z%{k!D9Mmi!raIIM0**KGL5Mc13 zw_9rC+bE;YvWaG|ihf_?zaIFW@$uD*B}Q3isbaR>pJ3bE%UUh`e)%0AP+alf8_trE8xG+Pmi|f*(4cT!FQnN6k|Nci7u=li;A(Di-ip>b3HU9?3nxV7#{)pm@O;RcU>`uCgv z^tj2o&SCuv@^CZ;Msjc2CvH*7dt|56!nIwB`Kv_gz4Ip1TE2 zj_Z}9TcXKNgjte#U0w<;FAu)M#txTP@4>LHUs~hlh(r2O{MbFrE^Kir(`P3ooCO^F zCLYez*nJeNsw~~~$q@Tbvzn)O4U&s2Lr)$P$^42H)D7Dy8m}x7feIO60Aby+!(`3U zmrxJw(OFu3qcf#mT*h=Os+IE??U4`4lLx1M3p+L?W>Iq*2Rs2i#w>y$} zJw>Nh)-$(rds)$@z`Z-J&o;M<$n9jsh|QVjTZxxUeGbnxiBJm zyZ#jQ<-AsI;J!*%l(e>tIOKOD4ZrppGs&i&WLKX`5b-Z>2Bv%-hR1l@jqLNBKL281J;DhEri)#obNQmvE@r`+}uagUSCZ>C3P-SZ~u!Z2!+LuJi@fk4{;g$ zd!Ii2&Go>qqnnv@XX)pCSihVU+26h(;E&)KYiH>Yj$-a(PKuZ54V__1wf?$FO6BI3 z2k@K0_zL_XzpoeGlk3RmIUm8ZN2OOTpZEHHUBKr3)T4BY&+ju;FYA;o29lX~?!)3I z!(5SzUCEpWOMRnWN_{IWi6V5_-=MUKOlQv4Hf;M0Ipy|wn?%u%OWBXr-5Xz+BV1XB z=r0M&epD}`{e44B5VeqIm<%Mg_D)F%VXvwZ5x6!_TX}err4$cO~9; zV_sLeED^}bobv)75i?Rx&9}%SY|Uw-$i^LWJvH)AtRt2OYn@ix+DG~B$?fdUjGyP& z?l+@PT|lB)RDmj8$*`23luJ$-fcKk^FT!#=-;l&TET4~Qb>=q;Hs*%fTJUs7m9bef zHg(FoSh!e;&f;!xJ=+A)v9dOBnW-kfz>jO@&T(F7PNZ-*jvR%d$4)zn#Ii7VG;41P zcU3dmr0U|;x(KBzf%9|1tF8NIhtEFURuH~bTCU_!a!JV6^K%ciDYH>MQBfd~WPwmv zy}5QH7=Njy_qfOC;~WDev?|>3=L|IJ)K~*nte75WlYT3_+xDC--{vWdC1ucAXQDG> zJ{oGiXCjUp5ANnR1f?Br1L5*(mFJ&epECI6o6_~NZz6cx^+%2$4A+z^xl_)T(vI1@ zTz5#{hnMwU#^}eJcY7EfjN7D#+rMQ1;e27=9)xt#@^7=QR=~{Wr_;MPMw>+>#^EJ# zw0(Lic^+M7+K}%b!RF5#sgG;33hQ>wUAO8hmer>q#YS*|guJ*2< zo7@%ds^CDnqE<9UhBR*lgK%04}lTtKWbGe8lM)=XR}J1E(t(<*{4fSg`BRBvZgR=b0`M z2bKp2uX(c%VSNV&s}DG9GnT)$!p)&)>BBG{_Rr9?Ej9*9r-NDdl|7t|+V~P(cA-z) zkF4p`Ec)VERp#k$&k3oDftbsqwklyNx0k=f&le=KgIf;DQ)bTRooFJ71NlDax_9*q zTuQHccBONQSeVe%>G-Dg9aK?x=l~2nHQjbE=~CY@7QF82Hc}X<^tw0Kcj0L&deRI} zOd&t%_hq=KS8C8IjX74}g3>|aQXPER{1Q!UL8vb*I6sabkWbSKsN}-V5aaC!HYz9N z>~<0$-reR!FI^fCPdhenb%j1>;k($R>!uA`5c=3JbkE6If=fSpq{h)bLt)xRcxo(C zFTesD8U?M?L9-Y=-@!I!G|PaO5m~%K(l3W&DfYMD_uZW}6wvU1ag;gWnuSQ0+C) zZGsU>!`@v1UD<16K~{+f;>=MZcr*%Ip8n$a9XX-m$x+DhAV5%XuqKtI95VOY-WG~drH zhs|%5D8||Od!;WTO->r+0$(iBYJTutt?sc^L1_=>7s8|0gd?FiK1Ma6iDu977?%uU z5OouPd2(d!w)eRU8wak&z*T0mFn*9UIvO-f9SF`-K)?hJZ9wAXwzHnPkb>UYy}o)D zN4C(!L8~O!DFEZtG5>CeP;K;oMthBa1F>KkVFtTjryIBAQc# zuW>v_qv>Xjnv%EK0@T)QQqIoG1%sFXNseO@wfPTHMI|!_~}U1fNL9<}{-{ zwZVq)AQ#pKnN0`7sOa;Eo`so*$reCWcf4w=`W!kM0MuU+VHs{WccJ6>T<7Na^o!T2 zY0!)6#7`k8j{!+`+CmoLPU1Av+5YiyHs2V&Ot<6hfK1Iwqcg^5*Tjt0-| z8s{vbm4sV9&1REoy3Hk;)5LLHohm)=4{bE_TX(b%TJ5W5v-tajZ5Jyvq9ncHa*m0S zpr>*6GN|aS*ir5t1a~T8y-Au_&YK$b(oq?4p#IX@Il-a)d$E4QmX(YJRMpUr#b_=Q zE?L}LMjHKI-BaZlNgp42PdSHR{5*C0a^x|eDGjMUg;o4<4-D#c9&nV7kcR=CWt1yo z`3T$LP7OlNO8D`_!@^_-$HrZi%DS_;@UNxDd_LRGeBK=C7gkn=H2Pi*Y6M1&dk(}z zNacrQJ#>prkWX+0{Z)?FU1q6Y*!AhExNitLX{{YCU8K~k&-mpwYIG&dt_7ueApoj5_dUXclH1oGCgYH^WON<(63=YxR#QT zWz?MpDCW0*UL|%!Jbc)OSD7=KhyhsW9jInM={dpMuzGvhbiO>64@$f&U|3)Gc}0}c z?Boq;noMqKG2g!jbX3wuAc9ZaskB=DsE4K!S9*R!#qNIWDEsuG^ECn1;z%|F5)7y{ zq)3mwhZq?BKBSQNT~$nu%WB(abg)vC$6@n?_Sr3Aq^MhJA7&u8ScMcG{^a*~AS`&@ z08-+-GkurF$rE;7$sVm}ZM1I-TqT@)n4?fB6Yb%Xh-8`12nK(YUR0RpT`PH+ky<}^ z(V5coC^+WCJmLozbCp1OQ^;WBept>>VlybZIe44xiO~FF*SuW3gJsOF1<`u6 z?IHh>V4`;9d{*}`ZZ}*W_A&87GGtyJ zMZV?J*zmJOX?7deEMZLSAUD$Gar{taLT_ z%Z5r0<_^`VvfsK4r`XZrYfE2)j^YLMF=#xO^LvL-uJ>Po@;WwA=24H(c zh>k3`YAKQD;P63bZ^1URb9`l?YYNh57?UTLsh>5)sAy}G8D{%tKq|1a>7-m&Vol zXSsMm^+dDQH&#usL$=|Egm(#{iNw1A#P zF3wM83k;`u>ou$qy0<9;5FOmDd@OGmmj&czrA{m?2z6T@rVVqSQuq;ifNeOpzlP)F z@;WDYEj*-fX1jQJrq##2z`g{N@JpMls$NbMTFsug=kA#38iSl(^Ab#VXkIyAZ{8e( z+`p{`B5b40zSKS>{VflOuG?MJnmp#{L#HUO1TlCm2pH)>Ckl~C*5!S-(bg~Ram6b* z27cO%0Ss&RXkQ+%SFTJEeI&oiA@@;CZ@xA?Z28lzx4gn|e-|Dv_hFhEVj@g3kpvxY zm2-;1D!l=@4dtC%gE^tXyZI^<@R6KADA=Ki!Aq@?zV}%!P*}e8j4?0L6+~~fa^EER z@;G7+8~Frl@lxs#lDKp)QPx_oAYx}~?{!P~c5z0-(RPxdr(2(BQ40CbGPri5a6=a= zJ0kI%0~N$()V3VZf{NeHZX|Kx?SUOM>Uu}m-uF0)hft#46yTcjJQX-@iVdUf*T0LM zMIYX?<;89h2dGMGt;+m~oz~XB4C0+;O%fTY`5@U%Fk5(R;g&z1f_1P-(Qx>H6pYcy zJ;e87iUD)R79H(l*CJ`^jQZ)QJ6)`t?=S(8+TYn{S%n|JS-qntQmS_oanVBPc zKL^)X-wIH`tU}u`ifwEXYvH#HQPX0k+ivSR)c_uDUpDZL3PnxYeLbV`XQy5p&pa;4 zd{`zZ4kEA)cQPt=VUla~DSpHkePN7u7|(qRfOj0rYqX7C8Mc_o)GRn6$7XNqA65$@j6K@6$ z?qG()`Uda}EEJFIXdo`JHLB1XD6JD1c}KinoT0?^+%7eNVLpxhW#N1aND1PTj#`D= zHx#cjZj|)EoPG#Vn5QkymCaNoHmF-Mf|i!7&rfW@7BVuz5QfP5d7?2Q}w0f?%vAiQoIFXfI$utU)lrdX=_m8 z`nhrcnZ*_y`BWsqDz3kI+?mO3laIi!kazet3;o>Kp@i??p@b6}MePchZ>?54t)c@_ z(N*u5Y6wU#F4<@%?G@U35Dd3smyByrwZk9y2x)kI1LtU}R4{rU7`#CqNnNIBSvP<4 zriliwdt301DWC)YcX);j2kP0jBAbQ`Fpd`qTu-27yD5^nyMRQ24ro<4&6h1yTOdk z^nk%c(XRV4E62T;YgM?1CA6@VI3-VIg+k$=gvOte(ScwjC2IZqO@W89VD~i5uXTO4 zRgIe=0bQiO$>I+Tg6pKK#`j*SI~pp#QZi#)*D0A~=nbWN+w!znM})}^YoxqVPNFyc z&WGP`pt(Jj)&Oxg^M_n)1PSS{k}cl34C65Z-f@B+IiKHj@A`QLGneeN3IB%eU&Efp zuH6q%_Bos-Js(eFlaMx2E{EV%7LAAF%faRnwbMp4Tg$%*YM1jKJN1sj|k*VF3n7J@R49fo%K4;%=0B z&Fu0!x@={$DAmNy5pKd!-cjc1c|L6?xb(pc{Q)Agcajyb`21O-x$xpZI$1&v-;wKN zdhZ_0qX%EHD1Vp#UNd!jt(GLo3&#fT=$FUbI|_^VFxh=tM!h3gYu*RQZN16Fh`IRY z!t;T~d;xB;^VfO7$xa@ffli#Um_xIghf^!)gA#k@y*GKCJJZJ(rWwtqX6S6wy~X1O z;Pj@w5Tin~w_j7@tP*PfgX+<7O(Y?{9TV|5!R8lG(F9#5zDJu7Efs8>UaeG<7aop# zDsK#;-WNX@j`MK|hvn_vKG{63I$DY+Spi;J_!Pqf&bT6ZI`3NKCv*4(x3?-+F-w8N z{FV;W2}U#L0rQ3P02L7o7Ner&@U-h_;OE;`qJ!BoJM=N_Mzz-OE(@a?r7-YrOP{Yq zDYeJ|Hn7Um1U3ur-&o^kCaha{+_1wC?fR?NG1qM5@* z-mS%Xye|I!ZgiVhGqA-D_$Ky6Vsk@Da00wCIH7I%-L4kjd2m{Gj8caNf1rimYJxoN zx0YyX7YY(xeqZ@`^Np?Jk#aG zcwhP*uBx!muEfi8GuX$WZBndYd{jR<_*G}OQBR&Ze7W0;y;o9^>G46z?r1k{ry^;y zZ|v?P1DiPHM5kyziuk*s%(XLri&THiTrsfdG^YeG09vmz*`nlICV z`MS<#BT0cd#xE)J@wm(`O$`^@&HKJqqNd@xyqC2lk-iG30R-zM#oG69V~-c@J~lmCWZI3{RMWzPVDL>+D46(`5J4 zM+&g!$zJy^x|ywap>%;A|)SpHaTcL zol7Gf=k%egDvraHD27HwJ|TMRooslIfe-%N&yW6O zLy!lnD*J*e{bZ}td-V4uNz8YWZs)p=W7o%>N#Ulp40Ne>=k zaMNDtMboujq(hU38{HvfvSyIhR8-EMW|tu$ATx7d`PjW>AIrRv=R)h|+tt>23wweZ z>s|jr883U_3NO;3Ge7D1&C(lpA67AAfmL8bH&<_mz0MWpw>-SHq<^93`)VM3B9Tvk z%Jr5EcEbZv*)eR{R(%(}%MCvByE zqG7*Cegy$bSCkOK{;i*d*_CT`X>e7lciJLVs&QZs9Y`1X(F0$fl=1ZA&r1g7+@~|x zRfbzU(KpYyC@|cgt%pZKHZ%7NiHsMPTzkhAAZ*yfaTn4j=1OGE@hW6}Y>{?yW5U6g z)iuLUZ+4C?$aahQwXh3OKCSP>Ug>;8&H!*KIm^NVm-TctqgVqKXnw|R)lNV?=S_GY zsj*{WD6hiTA_TlOl8#iV_z2t+2 zI{IH>OTFkQReG)AxY98)9P*Q%@2%q8M7(kgrYH7s*W4v4ic4F8hG&Z;=X}Vhd$WCY zDUGcwO~l6rbI$Td6+hIz!yqC>fX;zjo=AXp`01e=T=4veNp2O_FE{EYK-Z^a42g>K zAsoIvko+xac9?14M3ANv<;39n12{;zHAlIRmnu;;`<*b*92$(mr%i3*u=jL|{V{~I zTso@Un8A@)y<$X<*Ic=k?Ht8`5Ln;Y^+0!4rp3)^%rOb$-@{VsC8g(UCTe1b?yHS%m~I-Cil38*SsAf5LTR z&F6WM>8#hD?JQ%o3fUTa0;K|ZgONKuTXbGlr|*iw{=NTD2=*(5h^k#8z~E-Qq9XZU z7jIvHvnA%cf8t~h55AO;e({Fi9XZR{$Y*6K_lngGrXrQ>CRCC-Tx}6c)a~Y zQ1hj8*58c`_<;d7MV#h2tc2_;TNjd^Ds3c^>^3 zS;@UWT}nx;cp)Rr4Wz1dL7y}h^<*j15GlL~e6r?3FJ`0V8cunR_9=fDr z_Qm$wn!FMyHjn0Q^4d{uhKkpyheW`CbE)qWt}6lGkED$!cJKV!hUiM$znEE~eRpZj z&DN5QEFNm6X9m6#F($(RI5+1?a5HBJz>XY&#<+~!0+{^2lhL0&O1#e6nyHJm-%$)l z*N)Osv_9Eh$Ht#5{oNlWgkzX`-Wt4J&-ISvs5h`QBFk#OkF$CmPPuZw1?S^Bro%b%|hF#hL`S1;T zEZLlweD*HC^Lh)Su3s{iF2LeZfd(3)YftriKC``qM6W0qkhE0Qy^*n7#e?7p?yKXKgg}P4DM2&Dly{ z>tKnAFtsMhjHa)ylW=sb;@QjWntar>7sE1CHFNemEBuk@|0>&X#fW>oiS?$tx<^~e zVqETE96M0}6OBflyb4tw z|4q~QE#L7M75X36;4iZMKdixj$ejOhY;Qyb1o&jKmEI7XOcw$)EpywRC+6w{WHl&aa?7mMA!;!0f*>i*H~SF;Gpv z|6`H<|2L|1g@dkqduum`&$G3(p)3QCFJgLI^o2HCHlZ!Des-P8YC&Cmoe;L@P^{+ss3_t z4`_J2IP_GP|Db~PSO~|#mLrbRc5Plaj;C4KXm=Sq(?fP z$}Kw5tFtuIpDNclHat?e1=Ex3p(+)u)s)I~e6S?t7H%D=_J~j~8Pq);BMo+Py9 z+_g&kpP34feH)(T1KAuD<5AtH8a(NJFlt%Kuv0mB=q8EBX}w;eii`y|+&gaLwCe`e%c3 z38Biy100vWYm0S2!9a7b4a{D>wJpQ;iXV5ghcCB=hhen!+^Oc(S|;m%{6OI zR)-FEseRhB^SxGIc#lr)#_#*PdYKRrO)T5?Y&;^M0EXF?>fH0nUlW9?i)r2!rLPLIgqHR51=A+tDJtNXWA&$id#2hR!i z$Oi}xRYF*nE~kF*NL(EHgn!*!{!L2s{&x6i&*+|b_I$CaOoCVQ@b?G8+5h0+|Ax3H z4B3b=?orVMwwpkEG*J6OWt+1%_!pQAg?p_oy!BYc4WXbU2s;)`oPvqXvukUsbcp*C znF}&r5_CmXj8B0Oo;(fO9;YU z3{ODUh_|+;NHq;FI3z)iQ`ZUCbofP-ATnmiRtG&s&;#EgdUZQR7xypieUFxy&vGb9 zI1ey!Pp!65!s7)(UpI{_XA4dGCV3^)?hd=zx+?Z6tTgbH?tH^0eS7ZnhQ(jrq3#-f zJuld)xV27!IkG1i8z~boH7Gzsz-!g`@5&A&%jY?zu&^jZ`@QR>oNPP0)(!FnJNw|| zrI(k&qSqve#XghVlN+#k#7=jP``E;*=#0vL81ZE(?#kX;%$>tbE&}Vr>0r{-A&>1( zwVR_2ZC-@tlvd7Vs!rP3dhJ12S}kWNmL(x3glp-{B6=a-*eBb;&b6qOTi<`S`uy62 zfnjlFpFrI4Q~VsF?n{ZaUc6DV9N!DxdW}+P>_(#9Ml4G=;!x@#rfeI7_S7$ggjT+L zNqa1aN&C+!1YvDvTfoiV?Jm*Aa^0I1{KGJxAU26mR7}y40uvApD$8heoO)G}tUR^t zL;qoJrVKw#nG|@897Kja0MrEvX+vYfCDtNB5N>6i)jHljk3!>)@5SogYr0wN>^Ey+ zwcHCff3B?Qt~lzu_JmWEp(wyT=5mJH()}wjmq2e+efFncA?CCm`3uxk;0?(2z3$de zWuY{7v@r?wP6lMbc(Gzj5HOn~TplC46?DKJG1p(W-h7qh-7=HV-?FfPA#;&JN2=itA51p~EExxoG}kVtu+Cq}zx>Fh!)_%&f)Bg-O5SH)7(BDo5KKK4Smcs$b5@ZRx(a)VI2of7=ipzzLHTlR)P>eh;=U9*9O*-056 zFJgPw(;th56-x5T{{UV8f~CZf3=O@L62~NX3D|V~M@=XdwuQ+pLksgVQ5sxP_mO{N z(L*e9n8;?Ga0^bcEA2n!u@wDEfc2)hI?ItGY^55?N`|rhGa}O400DM*i+dkX2TupJ znKM?olyxz%h z7bpEs*QgXPn)c;J2;!0~WU)yLGM*xpTAwlUD-~;U9q{Uh<0#Q9yJ;7%K9nw-pOr)} zRD%aaBW`JO&qD){qDhNf$!*MhRs6N9T3LQ*e+}-s=n+<))e138w4m_b_eudM<{j3q zvtf7XWk*q{-J-Az^vzQBdd9U&HGX1rIUB`GNY0VMWD))!VI>F5wFDogESxA5i+D2( zGfY)%^}4!N4|KN`a|gPmY~-XkTYgaNFS5AoA;Fh$)z54*?NfoVnC&D6!jt9=jR~0K z(yy4Oe(RJw2OPO;gL9=aqX#TU=o8TRaXDs5WzEashaFmO+f6$CO0+*0EgyZmb+yM) zJglhZF7?#IZqLhgdeyAe;F-MUrq8BGzn@HuZ6LIH1X61I&u(UJc7k0MJ>ycOl6jKA zrxVi2QEnM43BbAF*E!`z<7M|L1k)LZ5=NpX@yBr?Vv?(=-Z%^fs2t-q$ z)fYdnB=7@(u|#WGr%Mx;Wi6ta(igSRIV?5UxF9W>n|v~OYb5!znG5%ak6q;k$Gs|* zap}t)^FCe%zG|IlIY>-E2%1^1jA7otu({JD&sA(Y>kD2GcQr4&_qY;XMD7J9KWubp z=4vgA=TDAiNqYHc?aqEQtE+ArN$jFsZ&)m`q>W6o0nyG<=uFs}>)~bHm)<)o#A+1K zbL(?o3Eek78CWwc;O7U4?wb!x(j81j(6cT^E(6Kj5l2o$}RC0>#kcj2pcV#Uw!YTn58+2}LEbJBc*Nf8|j0br8mYK}Yh?>_d8L>|SQF{o3H zTDt3F;{DOKLv7{w&R#&rVj}O)$BgSzbbQSo8BOz{b$SW?BNxoQDpgmIbnLKX@vD)V z7ZSsshm~7)H3Wm(t6#*Ntg5eL=4-!GJ=5}B{+w%#{5heGXKE(0$lg#{rUt!RXQ@5u$L9sE?{<8WU=Bx$xLEA?fTQ3jKIRsC#{ zKflyHLQ(jAvfMq!M1CwyV8(A8(q-M|BTXRqfgJEEfUUt zk)$r-jTc7?B?^uTB|=v$#N4O={;M}!XEmZE`Sy@}`de^)-{Me|{zRaL36tS~j`aQj z&9u5biT=Q|jncq3ON<)lJhMk-Cns;p+y&)O#F&&)(BV^Ytc{YfaMjNB<`nwg#T|+F z8R+XX7tK&ZzPg=4aybrcJ)oQQ?E`6T!e8c9D7R=$XXKpv4ec#%BwP9OWLWv{PoE-l zAhDYh8Cv1e=LX0$3axM!W}Go?m=+@A6TeB(XyiAVcS+ygKd;Je8K}?sJ^E%ltxD|Q zd&zHiI`KLoSTo7dVsD^Y@H4i2v61@3*~+~0+oy+1rRxrpyLFaKj;3NiE}k8xqN3XA z;P1}7-!%#K4=p%O7V*MCCgbJlX0D)@itdeh8>P7`9zHG*-%#};e8Psvbxam3havc5 zOs00^;Lq!kTBtxd)~V~j>&}zWpA)o<(N&^aFb83sg2m6|`m9g#hZvf$WIx&mL*_(j zl%!e{{9Klr&WNy9^=BUc=+=I*OXzzo&YK}FUlx#01}G)fksi<_S@7L;N z$56!sN>{zPP%}QaNwykrbkkXG6t3izgh`ohg$b#DNQy{}-gUhsb}3OSIWAvvBMF%? zvD1>8pZOW)6S2+WVRYYZrz4Cp2OQ+(J3!y-l&0RT)(VRX_-HY z0d#y6{?1fEdh6qz{Rd#%!%wZ(U>KLWoGrwMh?lFwPZ#|(%~ zTazEvp^2yo8VaLFo8O)Uw?CED2X&EjFll*xqZ&PTyjIIH%2zLBN!HsxNqSu_;I7|< zrQCb8GktJpxUQgYJ#ss~R3Yl51*b0P*?A;N#~1T~ zBMTvY`9it;o%=&Wv#T{X?Il|WjesKmPf+)(2RN~8k6IqF#%qK5K*IQMkoHStiJ62# zYzyg^s*6GI3B2sRB?HH&0u5jsMLCRTzl#51Mm}%6ZdNTHwa=Y zq~o&zT4Uwhuf{;GQt((O(3crwpN*=xq%ptl zI5M|bR*&-R8Qf&mukn z;D-$otuIof)L)nsEH`TZ*qZT)p81d|*HazX;g4JmHA-&;nXkX-~D|S%V*nP z!vo^4ZLpn=Iw)$tXPO9PZ@89f&iV7oVr+)+qj^Ye0^^ZWzQelrt$wgxbm^M<-mmTc zRl%(~W*lhh^_(j>2eJAAF4~e>i_%-98RiO^h3f^(k1RO-8gS6tu;{DW7FU*FptSwW zSVw5%EO$bi{BbN-Q-gD>EB46%4A9B~n>w5RfRuuORSHvic*o_)K<4vSQZJIdwqKEp zJC+N?eMGEQSt`wxTZ2+UJEU3$i^!#icQSgPE(#d8iousLRun}dpE5=zlsxYCGx)ou zG*iK@(_Jfe4CZOmyPxJ;F02&ebIsC2sqhA@bT7>V{Y_tqUBy2_wdaZQ(r#KX@bXyY z;?=a#OA}hq=h__?aYJH-vIBd@l0vvD_~R|P_6_r!k>EeVj)R)lL9O^?WEE-9Jwn)l zoAGD2`zhwLm*ytwKrP->CXCk|M09rXDLw3Y+2P9!=XH1Y zNJi+zlAX$a5i#TYi9}3qk)16bh?TszbrWK za4QDLF)jO1KH<6##%AFQ#aa{fA3o06z*ZILTx7upD*||%N*UwAff4r6s>@^wblT7@ zZ^P$pz)NS63ZTRSgrpmh9@~F!lybW+F|=bA)#p?G=t=*hu-q|#NOpT^lx2Is8u?H> zZEU6JuHVlyKAYV;YYcwH&j3p3{!#Rtsep@E4{Kb0&-_{)bF!iKqGWgZ*5|LQ6E@7- zGNHQ79o`)mu#%>=fPSh3acYzCdG!YhGEN-NP(P$kdoWcFKnuMP8_Jp!Hab?RIy_hA zfTwhe<@Z`k4QDuVYd_D$n*_d_eX&K7sr042G-y27t;sL#uHTqE8gx}n6|$@AtH*82 zMklW8{o%@$)Ig~%c!DiWkynP!Ih@*yQR%^Y4ee>P2Gen&#=}gb7xCbd_{`Gw-65fg9RCa(wa{ zb?%rIu3XPmQx4aB?V8+`DECdeQ&f_c4Z6)Ltf6KCI+zN8?}jPNo#lXPn}C8i}4lSVD(T_yjj}7dR}N+^cQ`xZt!3F zqN&jS$cea0=C0VYJdakne$6p>Vn%eVNuST!Ha58E1~{4gO3e{Zc4Q4@^d z0$Lkj-Yszw^KEb;RA=+2%4yY%1zc-_Xr$Z6^?cK{={|)b&5T9;5B;^@>a!ElqhC8; z*YGxl0hkkF;}ND`Lep624|=5c9% zO_~&i6a{B_tP+|b3wVn@y7qTFe&m=nQL^>#^~r=9*}h@cg@u(N_7n0?$$|%hl|Q`1w$G&N!0pI?`*J z{BOdNDn-MRq8AF^kS=VYPR|7JL6v2C!W3q>B;M~QF{<8ZfL{i}dsb1@Z z+WG@m+-?4~m(-{B-TKDh3ZIwya%BB`e?H1f9@}wR?!ATdu;_fpm+Ud}MubQl^)^e4 zkX*wWt=IVqdf&%AipUZ8BnhyoJDaO3RqA45SOd7#ghrdLX?8BOx*%4%^wB=+Up#5K zHo6nJljWDWlB)rVus8Q~=P5>MZ|%`*v61zL>OHmFs|!8(oJm?#i;)4xW0a9(aWoybo}4HVLJfpN8>qaFvjO@(jfSUZ25*grea zD*Hzga8s9%BW0y>XI#2TLIhr7h_sCp9QO^V@!L0~lcZ0>oYK;{%9`Ylp3dfK`CoAO zzd!X@5~nj&qt3kS^f{1c-(wA^GM5(Y#7p7(DxHZwVt(JXN772Cs8+h1vK z(67;<{I_NM!;Ssj6SU+Y-V12!6CJfl_>~M15ind^6Y27vB0qAs5xP?$9Ja=*H*x3E0 zo@YMTmW8A$FweBgKVxFO1Wn6yeb1q+!I;8gyV=*Bc2_*?T5kwUHK+}_Z^XLTq&j)T zE&tXKMBqNKZ32P(0ABbA@C%PWN59VX&r1diBVcDEqkZfOrZay0Zi?p7^27@l_z-N# zw6fIX?A=N%S0cxLT+#mCcKRJ${VfH9#eLv484&QUOc>{Ri6M5U}qBF*o)*x1%GvnOcSg`4tWf&f5prFK#H{ zJ9p{!FFjUlIGL&dk+-a@!PNO_&TsYTDbU041*T=;`yfY!Y7hifHv84v4pW84f$dCp z$W6O%$L)a@zBT}Ejq_evn2no#-f%dd2$_dS+wc4u;U zKeEUO-rzUh@5&9~=3J@&6s^>8KVsp>##4*tUaRl*p>}I6+eln2tnvJSJ>@vbACy7B zDKC(H^e8&`w(XJko`i3+{I72>2j<5xNEXMDQ=5%GKox~Ue~}py~G%G!O`@--sWs?XvxwgyP{L{-x&UkV2=ddro20A zL7{(V?^D+!2GWlqgT^TqH0&0|a8&V^D_`)1UK)lo$Y-E!Ym$-GW> z@GY_b_!jQ9z0Y<8d<9Ov4~h;bW{!+`7nIS~_*SX$b5>hkoOxEoQvWj9e&u++=+#vI zg5^{z*!AfMXa_zb_1XvUrrqXEN7L$FQa&%y*j$qh7cs6|izBw2<}_=ArPa&w0$iA= zTyT|B!$P;{)rGV~L2qCTl3X88KtkKe#O_Wr1NC3RozC{4*@KZlyJyoZr;b_nE zIk3i!uNNIbU7TU=3=gROV7LO@V0Kc%`O$Z~7#<5k92-=!tcHd$Kac}P+3yEprt6=W zLo4RsqKVWTw5x5|Ia$&W5-eroR3AJ|thkw0Lpy`}#@7a^5LW4cZw2S7BC8rMBTIdZ ztP`)etAbG5RqHvO{MUF)imh&X7|A^J()8y3ygU?%m#TCKm&QwY1Xk-*s_(2pB2^@+ z%3h$pDuDI}(`is~u-Sbl-E{8G+D{-t9=$aL zhx)M@8Slf*p^Gcir88e@)@YW0o9aifFqy%>w%O}rNEUSGV4DRmnO0Kfm7dIRV0W%C zaC6!=Oqfr9pdiu5Cnpp~I-EmIXTM{>sYcox!B2-By)R!S6HFOLe#BE zR3SCN`{AyouUMnxuoRnL4Y@w*BxsTTq<)@u_EK%jhuaI^jmfU&_n694K6PXDD_eVcE;n?aYYr3Z z)rv{@UVTeC@F&>40A*ehBV&AfpR3GlM$x`pd+y~0({h}(qxt=aWoK-KJ}iT7NqoXsqJLsR zrsYe>4TRZjnzVa=ZIKfkTU|6W9nmbhmRu}{ItwXJXlH9E_C~2;>@cWmoOh|KQi+L! z4k!{)yJhWTzwHnAoxylXZ+$=RzUzR=`8I5f#P#kWCrxzyIb~n_=0mjaaWR1{1azu; z`2#@MIxLifE?ZnUKs$X~qIj$2*y_ClCZq=@Imt z6{^n5X}Yn%YS$c5WyHTf7c=Zd%2yZWHis8vgj(#FRlt#H)att)jl1R&S{1b)HSaZE zi=sR1W8CJ1kBK7XzU`g9dGhY@^3tuQoEqY{-nj><3#p$e!vm9HWfMkgVciRRCC+0{ z7Ifg>^HtOR9;AsU!>+jxoPoX%i5)MtYbi~y$LGy6Wj&ZF%%WbCJ;XzQK6{ejCbK`3eUdnK3-FUN znn{z=jT>zB8@Qc-cuM79rEqmT5A!O{WMH=k0ZAy>Nh`Rw%Dv_>Ku@8s8*q7udT-1! zWyFEYFjm9f*S4`kxYmeK#n?PnK*30V`O&;)nObvwy_}|Ua7uOPPy$%h`n+hA6RaZM zzkJ|1GSuF|p7lZ*Fmr8f-tN{b?^F2ym}>vavESDtoW%r))nGD_0yql6f5Ql&$R^4Q zScNw+|Kd)vC1A^|FWPDjwGicUX?MD;Bh_(uUSODwZm;A)-j6?I1t zF7evhe9hil5(p3NGwms};*(q-nY~KG`qY!CBGMaRXk~N;exC1LpS$Kl)cDwcF z5v1$p5yrOEk%~?4QhU%+xuHqj?vE$DN%R7_9^a|-`_=tM!(#wX7XzCO)IeS8{&5c^ z{SY$a`b8o63MZZ=*~ugY*G@Hhb+5`Zl3nzyMY&A}=;Q7!N3g%1H6sIVO0!)Kanr=; z{>#JF=mt@Z823~KPxJBtYQ#mEJa&|L$-Kd6HWl0DZ)(7-I6IFy!O|H$NMwbgab!(V zQ~23!&?fBRZC(4#+ZvYeyANJ`lz7?FOo!4mfipbb^5Q)QB@Kz5!%3GuSiE5$5ji|x zBXmHWt`X&n;CpZPAip3HD}dfB)yreG;C6YHJC^N#UVooux>yhuSZp^Pd{#uM?~9at zYm~*{^1wUUAY!;iif~CPM#K7zLCaXkHW0;}UEq3)4 zy|x{{6_mY7Qx2%B#CGw5sf2K)**Ar9Y|P$jhsl>K-((S1rmd)3b+Y58MdAN2f8Pe3|-Z1JC7(30zIrDIEJG)VRMIbWv9@ zPouok0!8}?1RW-9j?U52MY0Bb^T&Qv-Mi944nB^VC9x$xfT)3_EwGzeGeXO+>q$Sl z$M%6$XX)c0&J=ikmZtUX_oOP;?ZX=P{%LgKo?de~toj0@sDSW=3n@^^ zv-Yt9lX;wa;M2JpO-)V3(A?Y~&sGPf?|=~tjnR?!7~{Tamm#u>uJrN-B=BN+12$gC zit?L8qncH049z!QP#DpQ`gM$a&$}wmtP`H#{MpCCBsmRC1(s7Nm#ID*2w zPXsq$zNcArSk`>vn+`$4Xe)W1Y|8Ke3RZF9C&6>NYzRyrQmO)qt5U=$U^xBsAn=yc z!<$3*G6~^lWnXdZpCxo6;7N$hANXCr3%oHksN|9#M6^K0aDAaauN06BKA(&F^+ukTuXNLMa2gkA^8u~h1 z;so_ZF!XW__{9-?dacK1f~VlQLJPq~N(bEsuRh8BX^kI$Zs*TT^WajiGR+_z^y)cr zpqbBz*;p~SJ^U;k)YC3F_7t~zcd_~<^%4nH-ak900Fuiof}0Iz+S3%YIe-c(-V=ek zfnrj=igdS@LpK>g+@yRbxuBP!qU?JYi5^0JsbmJIv#1|bn}Ja~Ju1BpGu^WMo->;V`X*N+nF+&>UyVd3Un3EvnJV{mU?KRo&XNkE_cfl0 zG8myG4QTtm&~Cl_C-V89c(S56F+dSF>9}syLcmj1{@KqLHm*za{T1pMo)P!eS$3E_ z3?#Xa)^Ny18_xRcRlx|hGkq^yGy_dX) z*j3DT-dfz7aJXbc#uow(ygU@K8wsoP0zz7orG2E>_5qyzKbJ)KtKw~9sI@uCbc~)D zYK;*>Em+_wa=u%>)w)17UMP-pd|Jh+8Hp&v15e)cgLiJ> zHz~jJ!3{AQ`gKFhE~{6moRhl(ju)&mxfgnTYAYwBwPZ>d+4wJhzjIruWh~)%drL${ z>8!~>d-P@_gH})K6ANjedzLytU=R7OvZ>ku&sOM#WK{-0McL7XcmZUF_TD)KUV(E` zn%Bvvrb=AVyY`=Jr=8-R10Cbqno>b!|R<-opaI_?0MUwEr*a*na|1 ziUI`X>OScqe=LTe7kRV)NbxvdU*!m2=iWAjW7Ciu)B*2juM)&xh*Vu z0q5uDqXfJ^s^1tZw`mB8&3C`=opVvDN(+bbq^omoc`BR)-VQnJ$RBGGj{NP!v@Iz& z_(0lyUB9G#fl5ES`B|I&8d-omf}$C$$O;?mD>`k7T(VH&epMBG`D-?W+399251EUkJCb zIKMgX@jux02k;FqJ3)$k!A*K645JT%_`Dh-hK!RCRu@rH(~8QxF;#tWbT8;)Yr)iW zKsOZ7k(G7T@pVSqCU?^kyxUshM%%ttd~@Ed<1b(FTTQI3##q?9#n$hcw#cP?VaxJb z#Mt(gzbh5o2`@3X?ZXVTM%Ts(PPa^+KSVc<9Lpj)sVvTI=Ae^j4p53dVh#y{tDe$V z_EqrJ8{Pg6WQA*p#;GK*pTLclLUoE~jOA-)cCCZMqx|Eu{I_YMRP1}U9+o-!-_2@S z2jLMw{E=ud6dw>R*8{acmCQQ?%hm9Y<+>FTdyCFBjf>#Re<#ol(3 z#<>{+#k)SHz=tjNOTFMR^g5L@i&6R<0I1BP)5jIwv{PlJqB+xN6_TwhyC)Nd^tM|w zb8M@LFFtf3cZtV;!Lmce>Lg+3^#AO9oNYRZVL;0xD)Y{AYe=bF%`LyH_xTv0FH_~% zi=q4%!hKSs@#RPMW0-^>GeoXskQ0`Gw{40E6~<^xrL06eSFmpciHDb4}Ve` zY|#HxFPH47e43zw!jG#}7&&CZx@47}WrZ`~KtwwI3KPL?V@R7)%#)^Pn|4SO2w6z1 zioif(`#j0OI&++fO!WSAwB^y5y{9yX%2JtF<5RAj)+Ws++IYmTOPtK$3595@4^7nA zRUq}CzGH7M?$6~cq_gLKb!y9tuK1i6{l?(9-O!ti+6ky?f1#hUS;|t2KSMJ(j-7k% zcIi6Ev8cwVh!ZX`4W^D|z5Qw;;r^l%a^Jp^3WsV(eXn{|y6~mSesa4Nv4;^u^kK*Z z+g&1>#afqiGZ?A$jZ@gTZUWDHV(YxKIJUvXhGkKE_P}o{4AY@#s-hwn!A{*;aTh%n zBYY4A-6O&ius|3;bB#=v-&Hf=%F7UK$;3#mbqvCxba8Q1@x1#B=O63G9W`Ld0&B(i zA8Pi+XYqZO`^DQ3wwg9q$853M~MId+D4Z_nJ6CJV|vdvGtjya>=22o6fHfov;M=ZL-@ znrZS7n4ek>>Omy178v$5Mna}i2YtVgyC%5?R9v9uPtg{czSAj*r~2`)`-g%>BbsGPw8<<4}YtydoL_# zov#oG3aV@A`Ep4Z@Ox1UyS+4z?oCOTB?Z|0gxMoymk%lH0G-IuL<}!F_M&2@C_(!Q z3EBs#3S_SC;@RWY&SlwC$#YBLI^_{g#yd_cl)f1C1ubD8vUSTbafmaFHt1W^XCDpF zC+<3K~NcrlaMco-Fp#>8vcENgo!e2n{m`(bt5BS$F- zYdK4qrWkc%SRDw|>!PsK@zVq!9}@7z;ZXHFeI8DTeC>AoEy6xS2{E^3)nDlAFNXJf zDE@z1HvAV={Uee33lD2X{srv+RvrjE8DqGvM%C8@D8A7NZ9XviAfi_{Biih~!69xE zm|IUIn&C3LoV^KdzLgm47Y3$}d`&oqq<<M&`0<3kHVblN#TKG>3>G5iWWo$!Gi$(yOm;u zvXJ#ZWg%UaDh_US9|vN0ae)W`vvnr*x@vR3onSF-0XQ=Ev($PQgOjxyP={PnYn)_YBoj_j0e!#=FP`fzljR0=U0w&Iu`!0p0krBuI-~hl$ z%j@d&|9y%+k$eVJ`v4*V&`sEQ`u&ju2-V1r0HJ2g53H5C4WOo{&Idh#1n#qNe%aIb zMJ|h8*4xB`2y_;~kX(#P=QcOE43^Sl-ih7T4=-> zOr`G1h_|H)IK>OcB6}cFb#y$o-~l~!-xZL=oZZ?CkKMCvQFrvqRBcdnjGAxOfGqE; zwV9Ua@5;E{Gi!rINXkKIZU?FGfNtWI*;s~|L=RwaPwc6D*60~&F&$|+LzypwQ_mR0qw#8%$#D-~%D1Y;huBlqQ!0%p0@qlmeaaB@)QS zbd@O!+hs*rAzpx)z_OcwBUZ}Du8C9Gh30jGp96yBB@K{lU zXc{y$so8Ea5_GzspwkL{#W3#2k9g=5pZ7LXrVMD_h@z$;rbd|xO zHTuVj#sugsI%|^TSlj_~q{3{RkV6>k4VGgq(F<5(M{)TNy#=I>{DULY!Qovjd@7)! zIH3n9PS9=a9>zNzaF4g z_vY5SYYF>aOFO^$cEnQXed6s%qV9UzzuW1L%ciD$F`yJ|{ql)P9I&7~r5ic&@qqE> z9jwq-;GO|!R(gU$UWuqf!M`S=8z}Yk2eLf^<|9CK15HL~@k^>E+~0Vy{Q2%m4ay<0 z_n85TPcEAsRBv-(*AR!GnG#1@fjax5edJ84a9O%EeIfySKkyoDE)q&Ddo~$OjDH&X zBgaaJh{U)3sH2$(!WaDrUxe_hQxuZtKB4Ecn~OAyDh)94?heoectzI*^ylnsc1YO2 z-mqg`WPG(|rwVXF?DVGC4gnAUD|cJi-I5x!ulU->FJJ2y$1wb%cU30)`d|Uto^DG= zPn{he8^wYwS);6q#>st*Xn7Ab-GijJ-gzrc-YL^-UFF7T?L%T3+vIs>YXfwlM8cf0 z9}n5Q$@+%X7pGR|yBQFzI*#Adj03lMT7?MstHgO6OsG1rmqtW;IX7T+wUbh+U}`Iv01XA?%H(`pP2z~hu60IR4Hr@CfrRT3WC1=gI zbJv2p05ETRt$CT9#rCQV-CI&gUz$R7ksQk%vxl|F#X@5duoDwQQ~YMd!bUF58@BKM zoix{jmGs_rFSsB;pDHyv1L?@qHb_z&OJ}f7SuVOo>HDXI(-&jD4RhzV7d%{n%nBcJ z$LXTlskM+44)vvYYFq22&v7;}fyx%#{|O7mQbkng4x5Z-g<(hr5awOvy<%<1-mmCjlvy3Wt8VZtxVc%Oq|r?a_a zau(`#+xAyuLXg;@w7tiQjznAPsP&rd<{5%5MG$T2P%#O(b-t$N#?c#_x1(sD0#^i1 zAqt`)W!z5Snwz)V{A{O@T^|o{$Ivmwtt+sdX{*4W(cqg#Y1goDrz{Z&-4^?$Fo+|- z2t%$K+CSA?6+_k)OO;u}AgU$sGTYV}BW|m4)>ymrmV)#y1`xhH_qZZd`@`PCxUmuVLP}wz`Z&BGU96G zt^NWF?o$#*LE!&~qJ-auQ(NM6qxVQe*^Tt&VBepd#H!aX(9;gL!3-m#g)oCYXgP0F zS}zGvTToPA*WJwv&0snGh3{`rs&>ou-UxgdQaEdlfie1mLB|N%_ld3mZo1lHK`WV! z`%<4Me^`<|m-fh_N@r|TJ|biHORQT5GmKjl2D)%zDf-@~D&JDWp?EkKF*N4llYXpd zM?ka8h!tcVxGJ^2_*3`8X;V^R1bv?7Mn6h^uy#NI6Qm$moMN;|@Yz@AW)EX@{Z|T@ z1W+`7+dXk;&G-`qv1Yvaqzg1lNF_*!czrgQLSNi0^)MpXf8{9+tUGmvVp>K$@H$|` z>efBv@|bq}^r8R9rV=Vg|p$IzM8$t4_hLN!P6yoX&1G5g>?orYSZo*f?hUl*QrO_SWyr(<~IBx|m z&izm?`#gnk7F@1+ClkV?U$+gt0Vu1>qzE)MLN~tw)C?gA$STm-_vrj z;XO$Io}XZMZdMf-@WS6MoVt7nJa&|Hfmr=tz*2ZCT3rVsF1a@;aaBJy!u;pJ*&fn| zals!ez9d!vMSQQ`%}XIfX>p`yDs11y%~wM{8oQr6G>Y0J`uEM9C$el;E&--Ky{o;& zVgaWtzaiXHb@BRd9*gi-HdUf~I4^fmyibSV9yEhbn3s09c#SBS2}o1^d71!&W`|f# z+X<*=KAji|;7fWx?d^mf2C!$~63`aZhtbH{PIC|=&zCIJ$#dBRfR9V*i6HXsboCNz zVO;9dLqNgDj{}Gj-;*DwKB0Y(((-tgi89p4N9pagdJ2Uent(iyB05P`n&UcN1_w^E zJesXO-ua`uB{rg8v*S=fe6Az>qlUnY`^QPSIf28at2Rw~VVTjCvu`Jp@W(?PgaX1h zU`0Sj7VW#xknCzA9-wjBT1(Qbs%S^eiOb=p%`Cx&}>*6(*2N)aIgiofdge_x&E zb?7?(nFT;-0sa4vYE6IZtVVdN3Q@mkb4?Z&%Z|vY%|~pj=6LRH$h zs}#ScBdlETQ!A@oZA6`Q+dM>rsxS);+$CNi3-+ z^q6_i1zRM%Xnt`=^a3CAOkx}bw>r9ol#fX}vOYXk{9_+}>r0|xpZeF-Bx8(G?eZh+ zPVWu_hCc9jLh$E!qM0<-yD4-`>cAkob-7T6KR7V`44!D{{uOtejnBqsno+rY`{wur zV-1(UQ>s9BY+8n<=$fC`9&B|rgSA;f%XaLEGb%un+(7M^-A15oeX^UV^(!C2f$utc zS_b!3TFdE57uyY@&C2vgKHUVlf-P=WeDk{wJJH0s>uI#P_%k*JEt}+E;}PZX)mLz0 z7!x<<1`DY=`nyR3+~7fT^z#Ve6Nc#or*rflrz6b$?T732m!4bJ#xKrVqPx8fLFi?h z*jTN!+7CX3;!|f%CEc_)=Z?*Fy&In(wUuwKnuT9y^oY2%X7=Hn$ka&y+xhRj6{^RNH2& zQ3x;AKOg(Dr3AhZ_cZ|~7GEG1q;0?*$yg;6q5#HH8;%H-O9URVK`@nxv@{r~+0LH9 zY}^U^ZtwHJ`gf3cXd@jUQBt^d$GTwYob;@F6Gl9?Nwf9Ke6eD_m2PBleWU?pVc=vAX3>o%p zMyg)`+e6(FDW3R?1GHxWR61xOx;W+tp^*7mkjp)5>KiH)?(l-^30C$=%HQv& z0nx7~y?s4VaY7krq@8;|eXrBwK%JcYcE4w!AfE8r!1^H#`R-4O6&01!uA;wYM*vTO zi?>B9`{;o`~4%eN)s z9e71uqe-k&ft!KmVtj}O1QYk!2w*u2%kMmC6S{DXw1d+BqGozzhh9fhbE`^=*sRgO z0S6jQ;dnDPi9c|8{g%(XB7&0R7*;F5IdO>h6^RB~GgNQeFOqaffQYtOjoV|IZ(v%@ zbM)V+pgo@9dt#iKS%Gh(l6NW(|YRbIkn6PqHZ# zXb*ofnPG37)7#`BToj|KaQo9{InpUhwyb+M&Atc6Q<~C$oM$G)FY~2>dRUjAw|J`n zWF32#4-!~nl0Xl;gS%L!*EHE+a{p}ASW8$)>EwF`HhSrc`?*#$FNwY&6xKuR_Mi-j z&9pA@AlC!h$Z34?1L~!yAo*!ET?;ysWXv4)#VICYej`shPacqb6QFCaNDxMxCn!K? zYSlKeT9&0F1->hrKb!Z>CPG|QPVQTOf4e_!u=P6$_`i)ZIPh_-5ESZz0Eu5LbRUfW z(#KgvPXkOG$yQKbd;=8mOx^mvi7#{k4(JBkviu=@5=oW?TP>53KEhk=(oiu1V+Hrf-+h?8Sx z57mq?irG}XWW*Y9XBSsLvemDb8SAZ|Zt_Mpp&ORl9a(i5x`$M98gleJ=;dL5%q0ig zfh@k{b=BNOD?UGbfFrLi#7z!P)N!&yc(dWFqbN(d1Hq$}b_!2UykS>)s z88kC-uIPt&0rx^`()@vVqDKIuYP8+;^$(zJQU zG!+uh(;B|ImyBQ<4UZl-tGjR3C?o0oiU7$8;!FeQfSNqHE}HK`LbSc?N%*2$e?rgeZGm(_p~Uc5L4|X zx0Gc~A=EK-Wjg*nlf$kf7`|DkAla}ol6*V+I_9{-6vYtA{4`tWXN%8kdngNE+=x)OGm1NK zT-DNr^|A#D@fA_5k}bwrB;;*TO??q>Se2CWv?s=)Yrm&AV1oz#m4lSS400#kHQS$9i~{^n^R^jg%4(KRdQfp#xMJ2$vU)(w*sc?frw0L4B=k;98S%CZ_h6E=9L5_NYLr90W&0Y1W&q(M*0CejS?dZhuPA$$ zUty1Z*?#T*+6-0CU4EcQ@a;5IqJE{KdL;*oSWt|ww&dBEEIh+ioHdehv*W1 z?M_Mjl$~uSe*4pScNNiiu++>4dAEAC;O)M~?m!)()r6o?T++x`PkJu)Zlwb%1bpTDZL3r{slfiI4TRlwioIf_nQby)w0I9|85QJRY(-NhIy%ZR?d@>hZ?^Xl8xD&v=16ps z6o){58>~~(;&092nRMeb?=V>Nlp`Hyv9`=jFjy~SMHUW-U_;9Rrx4WV<^`^;OxqS; znM)B7flZCcwOxo#X2s}GY&_KoYmo8{oS7r0eQq+nuu)eE+225_|94o%RUPlPP`nUI z#wEn)m=TwG`-uSJbz_2YW=52|Wc1SQg$uir1)f*l@r3w@6uLi4QlHg?r}R zX0BFL&5vM~6F&LrS-xdpPogmFjWSYZB|~Zn?xQ1c`rYnJ93z2lql0xC6Kq^TEN8vSW(tCsx3(L##^i;4KzrMtu)$#3DN zufpFuR$<@;9)7!*tH~wC%$kX3h6&mo4q6?$h*!y)dCWjh64S8B`gDA#2vw zo+GyLKB}`L&FL(|7D-Uf(ypHsA0fB)xF52!+@Z{XvFVS^Y#EasliNudQi2n;?zHrc zB@`Rc7dk$53*guAY~>j%#O)BcEF@8Hpvc3xlIKP2HH{UGQ& z@PKB>;?y<*VZq)Pu#02iAZGMezYo2YqQPikD&0dsgRO=f|D8BIiDx^)fj%U4FG;lZzaLY)6ItX zBnWy&SM@Y>?)xjc5+*slWh{AAKU!PBK?{v+;jc(H2yQRl+Vlz(%hZeTtGoRX?!BpAx3Jaf^Kx|4ftf^5t8k z4SWw~k5p7$NU8K02ed7Jh#%whVCV9=F6M>ps~dxawE zkrQd>Qr^Vf7o}+ljXig)tb2K{WyCdF8PiRBp1#lcYG{UE;Hxt6wZD0yV zp2q>lfnvQibeFf6xBnM|A_>he%(LLVO7>msSX6#=)zM6OA@N!)Ky*-0HbjhjYyG%> zA2Yb56h*srxkvA7%u9`a20q5JSr#)j>KUB_^<7f3%SGW_@j^bI3ht4y@jXI3(V0l- zk7KtOkaIlsLOFILDCWT1tSvg8H}q^vigu&i(W0aDSWR8Iv__u67*y~8O!0eZ;%@{! z2iA+CFTIm1tNIe+MSK`_P6v7G&CXdgK~6*tq@y z2c3jm)K9+M)6#()iMQ%Kt>#uKp7r-|y|_EXl>@@7l15o<&PQ30HjR7(-z1y{zS$j= zzg|R-ekiBlI)uTeTAlN(q7fBKrEAnmO6S753KuzthSV%Jope2#0Q6T+L|&Z=(GJ+6 zYRN9b`uDWM!S+cL8RqZ?k4L8`o=8A?Y~xSK#q3fP&Ce`#i-cUy|LS+S?%KVk7Nez< z6~JdvquLkez%joJGDD|$#O~J>#fc!L87!^;X{45 z_zCNwHDsjQ-`~NDrUSi*{k(nw4RbFT)dWU6+`Ibe;A0Ve@ADR|hdHY;oFaR)po)|8 z`OjMf{;?whV$@JnbK@P1d!PMw8;LoOMBpZVp^Xh1PR0&V@$JU)szdd%WryJmSBxTu z-sJb~9B-RS2yh#*4RAPg`y5FSe_*)uRm*>eIxKD@q2Cl`Zuww~TiLvBtDkHoi0`GA z3$g6dt_Bltsh{KA*}K3QnEhXLnYlMreY{l{-=_&4UMCDXINqi*f4xpJbijAwxfFed zUK`aobLx8_m7A5-zFeUKgRuH(Q18Y3jSFK=UedaO{X=N@4w{YP&j^`0fwBD4E(BQOKM>yM$115Jq$GG!~&XUNDDu&X}0zgkF(VO7aw( zap;~#7Q20U97`_m(v^;4+{g@%ESY)o`@D7Y zy%zGY?&o{S6(LNTh04!)Q3yxa{JTeu3|$MV?ZD(vgR|$>%lzHzb^887Y$xt0te1h? zKyfm(6JqM}4HX6kaCtLdNZijEpyW@QS{HWh3gy}2yQWW4XmXF42%YRLsx4gW^Lf3x`}lYt=)HK(+aJ*1{sGO zuR*R!4_=m0$cGM1+ebJmLI!S#CCqhLvA6|rZH(E>7lFRDr8{Tn9&k_gGrB%{V9 z%dQ2Azcw+(h-Cxc-%Rzd%jQ0oUv{bXQ3l$Cv&OFi$9AIsq{#l0ac2Ba#`!-P=U+PF zp9(nsTyggo1{2EbK>1(1NziXl{D*-kJrptjAc~7tE~u?d{`XMnrgu^8=Zz)3yQNlm z{lFw4y<8ci;-4A$XdWls+}*WKx%sjA$#sUFqUMwHou>*6Oe()?*qa#K_wG$nU(+p6 zUF=%t$3J`;%&Gh5(wJMmUBthzC?)f)x7--*v?v#H{_AtFBDOjm&pX23(;hdUr1|y6 zq_v8~u3kS--G;4h+w~9Zeb`Ef;G{E@iX;pQiuq&xgJfpw zO-T&N%x6u!Ym><+6Zu9l{?E0QlCMyG3)gh*F>sM79N%}S;QZi;Jpq!?FIG+|^^bKr zx6nwqZeo<}`#B!hWuu%te#H9af2J(Fg%)$uRg}~_^V0}B^t!F6<6Vec0zO#!%^bsT z2J`RBYtH)X4CY5ymeF5*NI?YP@#udjD8)}yy0K@bhxd=9UQwWoqgc4UblnFUMnWRy zKUSl>m;Cs5_}TERfa=4XPf z=>F*Sdw_C7SC;91XS}+W5JlVFkuXs$^=nYl|KAVlp>M1~SM;A-rLY$?nydTbBo)LX z?wis6xP=;1-Cxh8Cq77`JPN{pFg3n7zdnO?8y#f{zK9h#G0M;|>Xa}1sj&mq{B}b8 z#mz#n{ug1y{}K84&rzt28~a#vV)-SzPwU!vm7d8%|7_rI`Fdgh zGL2mwFFxPHD806Ijec3<#Q)1Dya{wIcW%s_-npzFrR^qKH8ao&)yf+2C%h8%w*~c^ zv2aCTy3_x_zP_EFSpLE9g7fzWNQRL7vb#fXB<`-iR3tzdMg4A{3hIBloeSf}hR(4> zGNXmDyz*2RdNXMH$9@jfSSp{~_)DCd^v(CH_^5%yVq}g1^%;V{r4-Ke+}I83+Nwar8(|qL2C)^s-M%~T~9exp)|oi+$~x0_mtb> z36MB&Uf*r=?zO}}2}{7uyRAO)PSQlmUb}-4TG=dr1`s4O;eR`iE`f=6YXq;yU3K#1 z&(xhX@85_0nr~uq!y&pK7}oTR~036aEO7?o-!LcJNf;r4qn=D%xPlbu6AU^ip@$`|Fjgvne=yucD#daI=A##0-eTC0(wC z)iWG?6W9FrMEv)q9^~fku`}J*11UkMDVbJs39NpkY;G zUv!97ggy1o>A;0C7%x3Sb0^A=^FKROyx4eVDk&uH?{1PugMn{J^0z(s-r<*K+kY*k zPBS{I*OmUnp_0=Cx&UAq1t%f$2dJ(%6&N914=jl*q#t!`M4iudxI*2> zS+CJq@x8Cr@`X=7muP2dSBqUH+6w30Vw^YK*yU-J{W9wFaQqzxey8uv;!;pxluKvw zTuV+nM}5_jmW~dyMSp4(=zN~q@$%gLWVHxV%ra4FMNbKEj$DH@83BN+3^0RvhnJPz zp>5bSKlssrSZ4hrX6hTUMxuIS;v(X$VK!;&1^_K1@dfSL0aN$am)j30m4wUP8#!it z=&HZbFO|dvdB@Zo0pmo>R{atT??v4)N~%w*g#X9xLdaf#m|U34COR1jk39p}7x zsce-}ka+5(?!DWP%Mu;EX~>sj`+CbTUh&|d?808(8Ax^5$3DPXE*BAGmUJNCC?65E zlA}9$xUs8dT!9VQ)Z1!!nPjFf3TeuhS=AW9&oD2KctD{OU#SqUn#`{9@VQ=#>uH>B z8L$*XB_0yPtg{tnCh?})$cCV=heQAsuS&Atgug>~B^cHn_XttCBn37@Vr18JzN>h0 zhPdKN;5LqT*_in5N26m1jPsf&M|{$RgD&?nb`N02Aw;3IZaY~9-Up+*B@W(-0GIWL zR%c2$b}y443zdR&UZ1Z3HMI&Jt~Tuc4jXL#Tbqnx4h>uyf_`~ju5tD-!KaVa(OLHdA_;K_q;bI`*4v9b zLdSFKuXeC;o0UTG-**+i=-e{D3#NdJ%KCGZscG7h`uWC7%|N_hxJ4A7jE~irI4@s{ zyHs3W{&-rx5Ga(#xiW<1X}Z$RS9a8N1^uhsw6|tBm$N~Lt9$3-iaw<$P4b5sYwg~B z_v6d`GqNI3pYdXqjzlzqvXQ~qO#}=hGTCB@3heOFQJC=L7VMwT8Nllq+vr$etPt(1 z)5#0xy=bXlpn?W;9Xl<35v|#zhr6}cGHv^mMkBM&y1pIB2y5d$b_^--Iihs8T4se8 z!i<8bH~9H3O9@X-{LvS?OY69Le0319l&6FBG7*M`^`*SaKYf$@eS#Dos`j62x!9h) zG!piDDlS+EFEY`81l%g&J-nQGv{x*=lipCd@xk`IZTzJKUbctpsCPr&pah;wAI_=O zXrmUdYoG5+O6CN+*q_r*FQx$n90*Pb^qm?XB*EUFxx4MmQC*FTO*K7?M$NNaq5nI# zM~-KWX<)4Bn1$K!ylN+xOb&y*+jS2BfAq~}lksCF$8co4cAN^SN@$Ilk9WG5Gd>YL zsoktwd-D{Z?^85q;vB|{RBCS2e-AVr4=HWu!9+`)xN5(vXxoy zXKR37nsbS=oDn^$iAqSPVuS)8KN}y&iMXVD2FnPC0+-)6?zRqi1TwN`!mJmL>#2Ku zT?abH@S2n`LR0S^4xKvms$D5|%tV!~U8dj51g~q6>mG#sbW}YG2(^^!Qo4VI#C1O| z;nqMrHhLtQpHS?xeSbb~VOWv5ve9M;cGNH8(T#&TYjuidr}(GL4u|!^0nnkl)x><8 zDXR7i99I0I$QQS=(A_mcblY@eo^4b>BpBadem3kIFy@hYshb)ASHg)ad0de zyK`*7Xx8K=86fzU+W$@8@=-;(5jn(l8N5@c&Bo3K&hlznpYWz}fzHa5}#FKaVDfB@^-~DT&$a6V5jaJM zeMN#9-iNvz+k-iZq-l0pEDz+~$;3X9fcvq`qQV{#m z&%GKRuH`&|P9bQ1(TU6PSv%94a#(jmMzUz&hoGVKI=|vXq-JX=Jr!D!<_6wjCr4^0~vjvIz@`y?O zeM@2sX1*}FcQcR@0Nvn zt<^$_n9;zPY-TB00)Y`0ZU3HfhiR82U~j%keKjw#Kituh(t=4K;&*braG_6GYc?erutcXW12HQB`cr2_nFl_ThPgJ`Dm!|v|~(z zz%lM;%*Qx}&|9 zM>j2>PIu+gY2Hns;fB2M;yrd2HWj|Y=5Yg&P|++(nbhjNS=AWBBMkP|EqB7lfS<7| zn&myiN{luJ2@hn^F8S!E2_c2h7q>zgc}T7#GReh`*ud?tf?S-piho-rtl z+t5Hi+{68q+nCX2DLl$I?Ytm)*a1U5d3eyet!`CnYY+I@)k3d?$wuvmRo&7WzoE8< zrvTkkr!F>qnw;8otOHwLv=&JP@@ZK+Vy?6AmK(YmbzHI)IABZq^I=7a*m=N3bcJar zR(qvJ5*q8)RXwI4+tk=jINPpWAep>XquA@e*_cP0Jq;r;6});Z}^d zCt~WcD67EvH)!8;PVq&DQ|=m+cv_!+WGL*mojK(^tUT1{?YZ#-#r4XlW9^qZ zAmuWMY`WNR7^-m?d3_3qYjbd6CZhstkit*FDx)O4l+Rwm^Lmat^L3`J=m1vPZ3duh zXxBNc${9H$?{UzmT#7?!mSr}Q5v|jG*t7c5Kb#NP4xO87 z>L9)UfO3&&rHU-zb)$9>{q6Ad$if(A6R}$&Q$LhF_L20{e8o*KT&orCr{%}SY@)RZ zIj?cmOuN)iMSeE%Hng+L3jA3GYbOGT_2E$##8#-te!oAxaM-A~iF+xsTcL%uI2*+# zytO~Wy~%}+dGANn(_TtBamXm|oCGXb|AQuFK_q?LW}}tZ*&&kslFmA3b(U6G9jM)J z^B(fm)0i_`7EC}?wfc5{g+TZb;xFVFG;qqr^N4?POx~`=ofPpAP?8b9F{0Y@ zKww40GIF9L=Ju6@ZkcYnL7CX0YVx}y%NR|gn!a?`eG)YVJ z)}44H*R)w_0izE8#Rag(%=rZmJTe+wbJlfn)LrjL&Yh*AiKT?)5TOjG)C7R=7&WBzz9E%Q#&bqu1 zmp2nTN;(f@_uNI8u7nwhO?M<8nGOw^Y-+x?TZ}V~h)wrpaA~ssvQX z6+x^J7owxXOwA>&)>lALwMX#`Y@P*6sb}tCszGZFuZ`G^vNL{0>pMixOb=5MS=^l( zf1Z9{bSz4IBkk6*S;6HBy|7m?laKxMxcn*Z3McHORNe-B>TlwB-1hurw+NEsbmY+0 z{4@zb##m^(16I&R((RRvzBpBi58Dg0m=+u?qZ!WkWFd4mtCU{^ zlJD17sV+r*23q9o{s15?r)TxdF$7#oE|pu%i$oZZq#(DBc}-3jdC5CNspU z8N5pEMqWD0%i}<>D8YKIAf{;SO9F z22F{g&iVDXsfsK2*+GM+t%t5Wc*#JtR1zEIjUHABP@j88$x6WfW}nX8#Tm+xQLm&P z!V>*%FFlIK8!}3F*javipu=IPcbavTdoMkSKAGWM)*-;;eqbv_u-Vl5b6)fJsw&oa z_R$^j8^Mc0da$;K`jy5H25?#RH9`}i>P%#lDY$ojk__oj%qFwq=4U6kZbS*0!sl=c zxRNukP@Sw>@6VEg>c$)F6IP-M2Lk(y*flU2-a*>hi=0k7^a{o+v;^(O8Anxcg>qYHiR*^rzAuP1~my z^&=ulPK6lurwHC%2K4@Dq7C9Kf62J2+o&5~GP9YYittnoKI`0v^DU-Rbq%B*8QDvC z>M77)^m+Y+zb*$seP&z&&!JqDoa_nVM^`0>E|1p-vBCGKGI2{X&JD-D;?56)Jeou) z5~bMmXmVs^eKz@Id`@vTwT9%=(_J$5mH1`O$QL>!@^LTxhxpkt#+m(LUQ`2Ll$qVV zySyK)nSPSKOZbo}`g4m9qV6gsaft0G8{c}Nim~3zy;y9TTCNt%^h_)!a**x5c3(YN zy@<&0@;o686(ueMmFnNZ%4pSVZtH1n)*%|~y)3yDyZi>?gPYF`JohP=~ymbKR z>7v{J>cs?U+Lzev4(d@?KUGdT+jiv?Cnp_sO;}@}Yho&n(ist-dHtL}a@T>?m-yuT z{ki2La~bQxxy{x}wJNf@zv}atdk44TzTTEMd?nqazm&lT;1ZkQBW&P-g;injgd^qw zb*S*s5OPp3yn#dYYRmsKEX9dwni{8Vl;X@m_v@xZR{^p?YN9eBVd;{lDzpO2?7}H! zA=9XRpOWW!?mfISl^tFlD%H&>*>o@QL!qXtY%^Zw*14@9$>8q-{yJ~o=zITEKxf5r zEsY`J-om|VWP3!nx1=23*qAO@rQUcVtP8ymg8&*k=EUhu4|{_McSqkC?8SCwk;W@t zlrEOrDq4#__h#zk+ml@dRv2l3PskAjp6qoI^IJ-^-d$#Jen7 z{`h9wO3vuHERT1&Jt{|hZ+0;XrnVge%7e`gCoH~OhQKft(;Te}MHXWZb=ilQM)#lZ zM|H(`c4FEYsGemDSq-eT#$R6i81ZoWs);TLr;zFOk@2VPD(NHtMn_qtX`Dt#%Uh_< zFs#$aa`d(A;hLlZ`r3--tnGKEjnRab@A)}>WUS^Ua*w~fWfU|As%PkQJ5q|pY&dP& zTEP5wz_!VI;Pq~2?67W(BiNBH?%|J7lY~S-Unl)BKcniJlOFl&CCj(N(}A()Q)qny zMvueXr~D2SFO4|%_6@?Y82g&gyNFvXp4VLtgv^{uEV)xwItqz?buZwUOy3A~MvJ|4 z8iG$vVNLGwzTApMkncE{%fEwa27b4LN@4ZKhE-s?H<|hH`(sXEHyu1hp%l}SA6Hr@ zo5nuYgi-WvwQmzp0?M9^G znI8^Bmq^MDzZne)pfCXa zu_w5@))p#aK(8W~yY9^P31e*(Pl?Dr$c?c@M=?n>0t4g8sapq+&p^5}L#)sxA3}(Z z*D1fD(2@?x&a7mocE}XWw)_jnkq_6MpVTq-CtQ|n~nBi8f z!wG|K%(3H$O}NE=GQE4(n+LO=Mnz&*)J~q45yL99CLSLm^7(}nysm_FX>zn@US58% z{iJ+hI>vEqkViGS!fENFUEK$gn)1zQ`Ov)YA=INl!UO3AQfx8|f|aOt-imm z^t1wc=j2jK2vzK;{X95kfMLT*ZA6iAbv2W}&Mnd;7z|5b{R@y^j{gqtSQ zs_3!@gn2a3rKfIGAFP)$WG+!L1r;tHC6Dcx6JjY-4zAQWw|yj*uNk~LApiaFpxj)i zoR$w*)-l4HW*ZQi!kQg8#dN@Y*d0uwFJ)hzI&(B^+V)<(fD1v#F1Q}1^YV&2?Td7L z_K$qzW7CBjSA5y)(6&Q<(YL0*^d-pw908Z!58}#UJrs>FyWO+RHzpv$I+nD!j zO4w2NOA3-$y|cg?4UNCrcd--JXmPq>xu^T6?o9Lw&dJbo^7Z#6rNjBZ&~v$O8I zP4r)7Lw9ythoYkF8`uT=qDW+jbG}|Zp$rm`R^UghW-kKtto$AL9hn?_&Sy$vb;aCH5c6dC zxQAnqz_x;U1-tsC7Q5M2Jg{ug-kXUd`h*Wuo z1Lw2O%1f>$FXw9=>G)Kz`c(+JpsoxYt#VR+;H~4~E^<#ubt>$IGJ#PU)X@+n7 zuicZ~Op zDkV@vIq}R`EwZP1R4PZQFdy{&povY8t&_cqHL{l+a5YdTO_3;Gn-Eytz|M1_$+?-tD!Fe-uWEITPMrtT#q!(53X7ePe1 zuQwZMs|VG)Z;9UU$QI9M;An7dfwgRH!9(~(nW1(uJZ`;MgI`HxMrjAwa3>xatIuni zszfd(U4KducYvd<_|ky+a9T`C)LPYzRRww57|MT-kwN4Or(bp zCIawtJr#u>HW`*ebG5_QC+R|9>)~;0=SP-s8Tor^yiN5)tiiyuN4!ehU)NMuE;%P7 zrzDJJg8)#bCk%NijkHBV6rUVQV3QI36i6~yk$2F&-7tpI;WcQ%&Kfj}wtV-y)u5~u z(`pCNueyjE;48SG!wKWA+I2R(+0ar@3*dG9rQa1n&+Hr9-K~I}u6KP9V$FIs3i|%w zUfe5)mI=?GbCPr8;)9d#LMKY>_Rzlv?%NdyawPZsaSKYO?*vsou?o@0WJLrn}KqyrMsF%B%<2 zlFk}Dzh%!q?Ij>lc~$_oEP9Qf1XcIke)K&VN?|g}LPe+8oqLk2v^|};ihH65rB$6e z#B!LB%B`2@iX=k}&e29FB&i6+bkeI0%LrflsED#fZ9HMyXQZ6O9Q5rWmABN0oa4m8UCQLKpoav`3 zd%=QltAPCYsHa}rh09yY$VGqK|15faSNb%2(gCH{=UAS98N2&Zs=e{uJ%x zF9MMf&huY3RiK<+AEvGWOm`I2MX0pWQPLh}fj%TQ>J{1K5(9^6vQrYap5P^MlIK~O zL~Z41tBkFBurBo{Ed#rkH^9Z91$p=erb6(kYJ8QGA5;w}ekJQH4&n|ApK=>~So^am zW-O;n-1E|8=pl$tabb`#WI-H3!5deuDQockzVDdWG|xVPNK;~1r4jcP(bmeoF32_J zR91qaiSOCi!yV-V243rxTe`>JnmHe0u(6nG2avu-};8ou4~E%%6b zE$%h=lm5;Rtco28@5tuFaCxUYcq*2!=2&r+zJ_ff_wJSi9E5B&RX!t)aV2j>hlwwV zE5i!XsP5~VJnzNyzT&~vZtvwD_F?olPex36KA$;vNZM0*yI)#?!Xc9`zs(Gyvn}PD zluV^seRC$$!oB~|TRW^9%b$`DICvD;`oLRaPX~UqX>~H!>n{BEICcHWyita*J5peR zJ;V?8Zb$0AWu|!@K|VoRu+8YW{z6ZU#1PGM(TErHSY54W4uUpjN?p0!uB#0Cpm!Ou z47pVy%mRHtU$HBGt0fA0WD~Es9UDZpf}Rk-w*(_k!7pW)X?ZUi4MxcKb=C z1chhRZC|tj7+>#ob*Qf@f2mEEFJI2RY8Mm-KJOecp1Ad!yFTl_-Bc(&VJwaGXsW3o5r{T!ZdSK^ed7+T$ z&=l?tr(am%lCpkOIHo&~Fj*UWE6lPm?3PUx0gc<1(LuXoQa!CW;`ym}=MPJ(srRI7 z#LoxnG>DVTNO3ifbZIx+tLN6zND=6Kc%W%Q&lQ}PId(4vy5&d8Hb1F^RX-^!mHqOf znf+<6bBjr&T-xzFpRqSN^)edsCt{sV;>q%g(DVtU%`wdC*kqOh&(DhMPb9u_4SN z_|YR^jS#gpo^`V={P0+jk=}H1Hwf+-n!F60-CJ@nRREoPEdrqVz{b-?duL{$46!So zE;vFduK4BA*W&TDUNgd7;SeaxCvu|ElotigTX5ZBW7~-)@AxCB3R@tv=PUijK0g zt_o^Q#3|zDCHSnG9y;k|Qh?tUi1HVe&n|$uR zsZ(!ivPxd2(@XHJi{a9y5`mN}@>!}h4Ei8z)&iMPRoY2zP#M8k^2tMvn4~8RsT|E0 zEOgpHKCMb1C;S|~%a($D4s8}+ozMzH6k)C`k0M=K9JnFJ+3(UwQTD%HeA^*$r{>E+ zO#^Q4R_68^6}Q=OcCB~rmaUTwqVva_w9PeLhdOS)I3kWD`Vi;|4?05CycRt#0a>Zl z0vta_5dFl|0*E0ETs?+nT~lkl?MsXLyyYA~>SI=c0nq-O_g1fbT=Qy~V2?vx0^C|R z?hz37^6fp|JERV1a|oiz*b}1$elUMylm4sV4dbS=3*$EGTzItWB0Zeq)4oE0GNqjS z4ECz%P~8_RRn@Ifu6^AH#w&bgdI4-3hG&y*TN5McIkLe1jU!;c9SFbGRfB9OlZbr` zd-VzQq3g=0I4&b^vL?N#n5B~~&;9ar`U-4<>$Rs8= z4YQfIAE#$+N>-hvJ2YLTWnX5t7PzH_e@!f;=Sro%3d^=4U4QgYk?Km-bGvV_$~5)8 zTG?S8Xlw)MAUGZ$nx2;}?}rn(in%0?aioekT>Shrd2t3nxD$PF#h#x{V9+%_9+?v! zREUwDD74Tb9?ep2AfR!|Tkq2f&Jrk982A(@B9+eCg5Rt7lT ztu+i1;EbkMRu9?dR-e(9bqo)USm0la=7lB*mn4)zctNrE?xnP_fuHQpSKU5IXIl7#-NdAK?+gk;zt<_z*o%8%Nd>UD+?ak_soqV35+q1Ga zfQAtsjFA!weVJf+tVM1u{)urH%dugjLAFAC&PMW+N6hA;spzS_<3YP(-kog^bMk_7 zRsYEkx1UaN_R7c`5AYjj+tra%?BzaIPF51qN}Aa_WRW6sqV^Kn5Xa*F#t|W-+Fp5* ztZQ0eJSJM284fTG41X|A0#TkwG4d8sr0kpK$w&#i+k)v|e70yh((Gsg{BdB@?P{0M zr|+s*#WVj&S7O=yi&4u_TFPZ#o%WBz<6G}TaYigA5WQ#7_AjOg)6Tq#L#bRMKzE4| zJP~dx9WK41h@madrHlO7MGexgP&3?;IdNY5R#ho!*qL zI;PigQw5(SnHf9n!f~i#QZM>!7bJuA7{~CPrkrU{YaXn03Y$#yw7Kf`j!H<;Zd59$ zOh`5})V{3)nHD&Ba97Mrp(-0J{OUDi=-UdT7Mp%-%{fo5Ogu8Hb7|LhJ>}xlUDmgc zUG(Sb8Mt~2I4noBD!DTbjLv_T=q~;?6RX^EAT;Nmaz-o=8O^U+l96P5da+CGFqmCt zzKLEJFg4nEudktDKuzEH?mQ`}* z%Q%~IrqHILfoP!T2re{0$~T^r4-pTFY;Oox0vYrvRfq{S3eoY~de$v^8xiny!mE#>e1uz_s_Q&DEoW%z8AHtOufynR{BDpy z?0kl&nXK1EuerrGPTcif=&YM>vRYWFRj)SV>NNW-LO#c;B(#%9Wq|VjaJ+n6NR(LT}e- zPG$b6z1k?c*gbL5libYHxo5C+V|RRRV`qfcjJcerr!$tT?CH6D?v0EOHaptjttNBz zObHkg0@Uf#3>UTAEFl~=%V9Nfc z5l2?`tya#VMYE&RpLQaPKWn%d1^*xRzB3x`t?m1yNl_9a5k!d^Jvt*wqW9<#jD*q4 zL>X-`K|=K2TN1rTk6}pByV1)aI-^7#3^VVDbDsB{`@PTS_sjFeVy(UY_TE?7SN&Z( znpP2-`=OA)jKVYVQ}aYxSy6Bw$=qs|~#3h1H9#v^t2rZ$Mg*Sdd2 zJUwGScA>^SVdd6`l!YxO)2^$gUQbvz;*jOx8gj!LcX7^N_B1eBf}(wUQ-9?{+Ofhdo?~nx$qKBZyDG*~K`4NrS1TI&_nN#Mv2D zPA|Df80sGgS(=<2S<&^7b3Gh9a8&Q!;+o+Xt`)XRU#%M%NCgNwG%g)jQhz%gbdBCo zT1qrF=Vd7a31s|k36?uAX{I1g09_M`KTF5!?%Q8q6mei&Ol#N~O09SS4Vo6uFN>g9 z-H2f`b@!6rJlt0@4=D+S_b-=@?cM%18wz%s{7UK@N2@W@19^#^EhlqGx)z}{P*(}X z=Dt;0G8@VJppXr^*J383QnOCZ0caVIVTJd5`#iRedu-omCEvn|JPka2tCp)i(fWp4 zG~96a*Z$kuqO4?hxJ{p#L}E^a)F1PSYNSM#1LKmxh`0U;^d;eJZXxq4MuLY4i*z{5dPH)`&ekgT`S_&Kt>`gJ~Sdj;qOmFy6 zG?@0Z(!ly3vS&($nwuqn@uGrMev}SvzQ4;(Ypabf@fg9ps>4Xr>Dvif-6x=t{;I>C zdMWJ^1L@jj!;BHeu86Up$iU?>%qOA?cN=(U&BLdr?WVV@pd_cfHC;^66Wv@JG&aFQ zZkmMNNAg?~IhfhwIUR#gt%)$ zzgtgO;_V;2BQU3YGp#4`XvD}h zBK@fa_T^NPN0F2+o0Lh^j$&h@ezu81k}(taj@NnyCCw9dMEyJFqU9XLltI!N^?SMtj_&i(7{GFvCk#)~7p?yN>0RE4kn5ZVVg+XGWHN zWjbZ=B6bO^M*~20h9P(EJy6e0EmYg`l%RYNKK^;N$wrj!M6?@tkW4?gY-b;zO_}Lof5Cy^d7E$>+4u$7_*Q!_740YbSbW-mAeb-sBub2eE2^Wv;sM2 z%W_8_CGU!UhI$?{oDvt`96B1Y%1H)Gddf1yfG-{7Y=YhuXVgEPDwwX8w@E8Dky60q zJy@=)G{7muxROf7PcC)&^K2{Wz=eF7qhs$(f;K}I9)3VNg@RU-T%VAkUgQBMYrIPB zmDkhCiL{7{7Q9zd6!D4{^J!RrJtvrG_{r|}@=~Q$Mo`^JYI3Sfh#ZiT^4EA5kZ4y*u$mQ50c3e85cY^;WtfMS1Eghr3;^h!9! zQ_*i`9!efIHvq}aDI=468>XYb4&_?{mOXWu1=GQo-|_vhHOv_mPfhY1g^Gc3A;LHZ*jUjES%$QvulM>*KhX}}rt=V!sldNi)t5S%iHPInOy7|e4jEXuu zo2VT3SU;P2`iQ0m7M zxLY8R^TA#$P#WOSl{NQ{K}T;Ed-8D)HcPOvl~6>TWvFNDW;X|pIbG-R<9o5iQJ7+l znx14%qXznUAGWU6qaO&-!N(&V8mJ`;_tZ~*f{QJN0Z)+`@%xdFAJn6IZE^H-hK>nl zb@EE))4Er{?ShEqVUedPd!$9a^)cj?kaElgV6P#=nINT{-AIe9<=WGOZ}YFOK#C=p zcJp}ey1xz#>rY(@ekI!TqD}5*_bmS-H`YTT&j5m;zuZie_OMupX=znT_OMML6sQpc zM7=)O^=`$NmNQ3xl;Yo|G$*|;7`}Tln=4*nZ*Y>W+);Z0OVKrR|5fZ~%vOLLAaA}J zRe2KK%+&D1QaUS~#ZeP^IC+dWr1RUAGrO$O>C1x5kt!(Z7k1Zg9-UH#s`;c2ce0tl zEDh{Wsk6vg0`#1vT-llF{Pw#;oi$%m?WI@VK`#d1sYfBh7{eQ5Qu~07ILl+{$mmGT zIE5Fcvp;eMgJ#|5ozhcQ9=L}Gsf8~AgWf;a*qq>Uh#gmP-Vlfe(#Uzht6tDM=e4Re z5xtLo5||fkcTN3vF)Xl0d%EyE(1q!eP<_c{F;Bvm#yi8D1%xR6VFT~2onkSv@)q6o zdemqa{fk^8*KYkh*IpdKH$qd(`)6D)|+sld_7t?Sv#nnnwOm zRU-9fZn|i9AW!W;!K$8(o``~5c&)xC8l`IUN4L(bs?n}MgSUP(WAqb9!)klBrmSaJ zTU(EZ)=55)yPJOgktuML3Qj#wDIs}|+~y2h;B6KyK=*+vx#hsaNlV1wsY7oD+aSa# z7t_G0pcMO4Vq`h$r&k+S4<_ls3<%D>FSy(O`u^MzwWp0qrl;Gdx5i{Fp@eR}vqAy= z^O7G!W#uPC}Ggt-7$wF~jtcHs?+#lmw87o3%;KGDIKw#J(%{`Kp-#iqLp?J~f;g?LtQmu)SgMVcoOwh*dwqkzpk>-i+Kij(`=U3&N`St z_tQX$^=bO5MGL#>l3 zDhY04%I3!idkg}h&qm1&YfxS40i+f*_P>r9*nkNWv3ky>vhFjVW|Mc+?UM-))Xw14 z&@s}wbpX8*R+Gd@OXgX<%C|ex8{ODQ_Fk{T6L)dk_j`E4l}Nu@n|5=8AJY2yItP+! z@1DReb+Sl1%*f~UGN<7|g@nI~pwj5rt0fbIY8}PRhBS|Y$cUOGhIyfK=Fn%ezx0f!ai&tN9<5r}H1oABrxdxvg15j76X4=@t53)3FNRm7T6; ziFI7Uew6ev;E6(@*gMy^FNJNGDMMT*oaxfyqESEQDdC@)(H6|frjO%?;^k|?3X z4W(>K>t7?bG0Xw+uot&@YC&&{jim-=W+63Cm!!*?$Qen9WL);g1$QBG&by+5L4K4a zyBE@@t>F@g=*pYDnktOnYoC8AzMfDZC`GTARrNP|Q~2$q7H;E8$KQ)_9y9&ifq~ zirt5QFtNShXP}nadkjn@6cUFIHqt8HIVge+6~UTIb45p=56VvsUcEHusVFKCaG7bQ zsXmjN8k(y01n;Vc@S5SIWmp9_fRBW3?WtbxG#2A5!eOu#hGn$?_Xgm|i8P*55~cM( z!Z!9`t%U`ZHyR&Dq{H{29js>2LUL=8x zk~xd*0*kk4SPQnO!Q4#i8D{wSh&TiM3ClO^u$D`C}`WCv>ft9 zCGc^foeqlXUO9Dxlj!e<{x{h%fYm-6yHXzlXs+I*&~!c|BJA@W<~5 zcIus_PEM9$q>8d~aFxTPerY3Mtv#r_-u-CDqRw?SM%Tcg%b_+|^yh=g^!^qTLa`{+ zydmO`WQ(vr3a0T?$;Uy1!8QzOa^{4#PM0>ffdbZpw!DN#mG+}RUs0bgd&Gq1KbB-i zu^w(Ys)r_@KR?CUH;Ao5B^Ujs>2fy1h_q_(Cb;z$86ovGvvL4uj+$Z)i^PXc%+?nz zdN@|0ZmEdR0-vebo+8(;nD@CqFl5*-FvJJ#>NjEzyfV=~7`qwcyMcdjwX?6|E5i&k ztIdc>F!*OCvdy#P3;tTNfaUu*%27h?hoh%&*Yud~m5u@Si635#5h~jRZct}H{Jbx? z;KorX&AwPxl4ugtFsX|eS4r+<|K4LDcP}G0I_V3K9N&07kNW;i6iAnh-}AK zCjpC-mjeY^izfxPhp( zv72}f+FI8{P)UKz*&kq_-R1zFJ-J&e&?_4pmMW!n?8X)2qIaj_=xC!{AyLlX|7Lb~ zbj!3qn>Ke=USHG$$&ehJaZW>xZ@5@c$BFWFk0dZ#Mr!8bVQ9-Sb&^PK8D+XO`yH@- z1Or~~_~jo@XOuAFd+N8fvmF3}pym&g*&h*2#Ky{r4r!OB8Rp7mzOhq=R?Ys|MTPvnSD`kZtjYm3%L`i7?` zo6XBYQWJ{}#YmZ8sv2jU?+K`xk%MPMe6)`)egkcQp1B2HR%m~*p-v;=jx?-`FDx4R zI^qv{sp|d|Eui2y>fNWe8NW|UX!+^i%Z`(Wx5kP;D$wdxz1faQ@}d{P&2&!@X?v4a zZ#L#!*52Enhszwxwq9IKS1nCs#munKP5R`_u(N|+wd7>01xDYuG;gf+69ib82Xd4) zxGtIR*Kf7kF&Kvkr)8A-OHX+Z3? zFmc1v<{=8cHcEvaoff8Q1V{RF!n- z9(P|;i;FP&G$=v@m^-AH#B1wq2d)?9kMIW`r)N(s8o9BSN`$zkSCV_B-!{sVZQ?zS zxW^u#?Y~oCk%G2qndaH#i_Hs*ZuEMrsb~AB=cv)8(bsiIHaSAdsWdx=II(S)Yb`51 zp_OE0%N(IG-J~wEuCfPk;`Q41>Pxm2Jc^Z6qFCIj^+ZJD3lM3D>wx!_BU5pi&Fj6H z(yWRHg)Ik{j(uhnn@pG4n@lUc-P>{mHZo&Bsf?^beoT9qLFn0bEyJS7@;EJEi`4>d zVFSVQ#XtsW@+PSWz`N8$p2*)Y4nNQH>)+`-@A&zj<=2UG=XyRfW%?c$xDKT%Ezjr~ znB2j-v(|5)waD*ob@4TYoz@>MrB;G4$-T0One?CuX-?Lfk0J{O2BRy3MLAhS-le-V z)C-^P!!XQ8UsUR8YxrVfkg3@-CAeq?%Wq5GbFaeX8>=QHAn*f?)34tA3r8O5g1-7I zY1^5dE)%z%vnFo~fCk4@2@fVq6O;uKaucShgdZ7%_#OpqbqhR|S4=DRGApm&42D5x zz>UB)y$alRU4;ZS-!|*MFULqmI9GLfn{;r@c)#7A2u37Cl3-%4wsx=AC6xM^*7k|; z=p6*=L6Wk_{1iN9N}GMRF&TnTr(7cIsglY~S=)L0PsTG+qQwy!z^k=9@fJp5ME-IY z9%y=71U7&hKq8JCiw05;sXPw04eoR9yU*Ro?hu2RR>zmibd z2W+qu$EB_^=z-`nANe)g0YX4)a{=31W!!}FO2xOWwPbk5p=w^Bx2z={na!ytWufN8 zDyCM0HCdz|rjYlR(tjq#Sx6rO7DY9#@9mR(_Ji^NeSL%`2ClQ_G!8ecwP_cXz@ zG{CpR?++Aj-XAgmdOVVf*6+EB1)a3^-ToG##u`3sS6%04S51@dym7(2v$|^1U3dOU zu9FfY>qhI(tQgi#@j?+DDod%z!K>Q-%+F*m;%v~xgI}ad2fqMD{mDm4lv34KXqPA> zKVJ9dBTLOgyB!Ag98YWFw7J0Cu%{v{BR^Fd=V2p?3D%FkQU`Cis*Kk<`W26-E}E4+ z58&exKAdY>i9fStlE(vha56vEtGmLWENtX7ufL71nwFfu5{EY+Bh)#GUT?*(Xuq2qxXH>V4kLGz1v2%4tpgn zP%K$o)RS++q>jJ8Ucj5H%OG6*r;eiE+Xcu^9Z!e(`ihh1wyH|Yghub5eVYkTM~D|+ zE>@iVY7MTI+he(Fh}LHVA?C6`x6_Z9(Woug#V<-*tRF1QRRO>65r6gKXlSdf0G}lv zMxntO^V1Rl1^Gbrj0d*^G3wXf$AOL5`y8CSGOzfCsDSpe1VJZ~uMoF;u1Kp|WE|Ys zqW!Y!>X=U`IqyzadDz30yF_ZrVA2hwU5puL3EB_ZCkq=Md44rpi7L;RALL{!7=^U@ zPGOQHWDm2=FcboG>Z-iNI zYb&J_Dc^m2#rY!Q!tfwG!tsILom7kQkPQy3zAiw{vM-feev%5UbdxRvH1J-#+A3aS zpOPUmN`Owue4HgJM$@*a(_-I$I5_(Lw1h_A2(_S9JL7oCphb(KP}qn<@^&}BV|$Kd z8rRG~`&vWdLQ)-#F6rG|sYKd> zly04D=}%kkeNC9UvcdY;XPTt0?J>AYajj6%l_hldDtq>5S#7yX+GvTdh9Y&&iA4;B zaB6ffcTgt617*AKv9t!xJs9_Oqo0xzVbvaY zH1uutVV{4xSYXs?N6H|r;^FRh+rq&XHn zx0Ko~zHHe;GRZP$XF1D=W#bs7FccB5u!=37{J?Ci-Nfb^w+~7ddG*Cr6{y?lT*NuU zR+KqOQK25a$54{x-!?Okjo2-W+D>Uxca@VU<&Jj=SCl-?sPyE}VQggQDp-!X?qp-T?mavKZ}b-0uSQ zIGQUpVD&2o-~H4TQp@mkx`EE}@c!NLX~6g!<7x56=W)t2P`!+yqkJ7zo*&dt@kh&< zZT|G5ChjKO@8>S-(Co|;9G{me>gwv;VlMOHqAFTiX4jMhx%*b(%wV<=Iwu9&;(=%y z%f4n3+T{ZtE4glVQ)}=fQ=P63-za3XfVs4C^$Aox|!~s!vbmM3E!Gt zL5kxGJ8c~ZMOCkaQQyoYTSbLt%)M(0wKZ0^S7*I}v`(hBeQQqrmj=Eie^RO3oRE!# zG1Iv%-^&E#WpY|gh)GJP_vu&3zJzIH7C-(Z)m!c+rOQe!tyxfSmEaCs+ut3lhSr9W zK>&I1dHrmL+1+LIew1sxvdL9~Hh#O}aRKG@9|-z-@69E8?!0?;%**9kovr3E&%b z<%-S_U7Ekq0cLgBJI;YNqZ1V_*16`sXw0uGX82O7jpRU8kIJ7KXT+=tdWGlyNU+<} zF3Qd>0`ldkGUQKPv0B*Ws2fti-!m2CbC9^Bx=dT2dt{vISGX|2QNBADhVvQDL<4M0 zvVhB13Xw@ofKeiiaDdnhkH{!bk2ekLZoRM@YD5x?aob7hsvAK^?MsyMN9sISTr}QI zLxxTty4M1CS3F&1>3O|+w%iaa@n*X;^Y(R*am{Pbmt&39L=BX1LuZpZ{{?14cJ}*~ zjUG%3RXL*y_F!W%#jdm*$k#&sPMd&&r2*7NHGua{KH`M$@t_4btBK? zZ@$hmCZa*p-6Og_4~6H57d#bk)b>f|ph02yy5?+e>p zKoDYpQ(obS>e^-PyWug@cN5`9Wnftqpb0`s>TpR(+)9sa^}(UU>veyAgi?j#OGeTE zJ7&IAX%@M>)v+@}A4^xx@vSsQ*rn}|>SoiYsqrtK*Q)|$LMpw7**xvXOUFKLmxK>A zkln3zIJihC$Ifepu-)TXlQzj)N92ufkxQK6K~jdC*IqmocOJUrCv*mNl49>sXL!Rn zVIGTD)!!QO>_&M`4sI&c*Hpajc6aqX*s}}IMkR>;1s&+8b`c)Y#$vLEH5cva&h*u! zhZ73Rc|6MGiK`-HLfQ?-vqO8goJYDJ(tOI&&gJLKk%yHMyDXDAX7F{T31j!HoW~VR zta2Zrl}pYFfR`SgW6+7yTdYkLp7n+4zZ(p3J3uCDF3v~dc;koRz3m1D1mK?7)5CW~ z+3q&wdut|zE^n9JOE>l9`bz(V!h5sweH#r-tYZ%W4KvRSzU$g87rDOvj_X;L-SLLo z=-wdt0FSe6*%#l9=$^AW4jLuuhpHpJ0(YPCO=ZdUyoixWUpK0i8CfqGiJVpQbQ<}P zQ020IHlMtI;oOHxIf>G`a9MzuphA;f=1qQgQT+8|R^>8pXGDd`&K@;;WYL0qXsc4w z-C6||oh24@xLGn_x~^Ur&GpZLrCyiWM~ zyV|Qm*Zz)t_aaWlCw$2Gb6w zb8aZ%|NN$)^EuKw>vGWBi?_ZA5SAZzs+imTb&kK5{YmW)7U#ukHshIB4@$3`y&gO- z6UVRo%W_7m^P@PsJ#4?{*Lvn|(&p(p{|@8*52E2;jCA4hi$9@E6OP1Rmc58NtA6lz zOE>wqp5dt(7_IJubkx(c%t5z{P)o= z7cSGN|M~HOSn`W?XFGS%A$FGkkNf!R&liE`VwB)CH6D7Gn}8)&`|^ex>ECJgx6OPp zJ13EWng-Fe1${!vU$28h`@aEIT2KGUZkX-dgOcAM?^jsXnf@gAf8uQRzdZjoX|AR0 zK(3EMU}+cqIr z`)`M|2L8G2(e0d?lCzE?$9Hn;0{^n@TZZS?4SGSrL;x=&sG-5380~+RYbpIVR8_h6gs*6Kkw3ik}>K} zr*JsR;MT*3XF~jeQ`6#aJoNW}b5^2#HU~ia<<+P)##@vGW(}C!mH3ydek1cI*fBD~ z>KbJ>LCQ>`B%b8_>$l0zzs-0x!j>^ggFr`(#G7URcGuV5|3+#4sS_YWm}QVLbfr;( zFeBo>s|RcC^Q%_(`ttC;-})KR5AG}fD}E^Oe02$?@_UBp?XxRMUcWp1Z_u$Ra_83_ zd!OxwJR_WNN;*j^?SF$wEzvw*jmCz3ZtGDyV^+(0MaaMLU&D=aENb-6kB=@BP>(fj zu@qw$|LvHP=YsH}&niIDA@}TtAig6-B*#Udl2Bio#BSo`K}(&%dfqC^MJrI zw5cL%Iu|*@JC!YeNAv!lAN_KFO5KW#7WYN1GttODn#g62GUHP4`tM|TDEBAFdr%hK zz8FZjDF^$Be9i0GLY(Z$|1ziI_MeE!pDxLlUz58-V7RO$yHvt+Uvh8tzhByiTjx?p zIj5DaagBg0&0bw@lO@F(h0IR_{fmgHvz&{0Fd1bF)2%bG>WGvw2f#&+8nf*G{+{_g zJ+GhBeyW4eoLKD^ttY413aS)mC+?I7QnW# zX7w6jm~b@xf2ZmH66SviLzrmu?tdlb|D(jr-nhc+!$wG4+&o%aW3m~oLAY)lsM`!K z-Pi@W{6DJh*6$fa-qz=^F#v;l*|Whqc|AAn7+=#-35LdtZcB7@CriiB;_wHXk3JSs#^Ro(t^fN|L z)So;O>MD$vzjS?5q*Gy@W*(dsocilKRm1E%KJTBzz$otPmaIggc6Zg?S^XYL--V3F zwVRRl_^XpYV%4yYcJ#}QcT3;IWkRhsyX)LH)wYs*U2`{T`CW5ew(6F>dnHE~JeShU zaRp(fk-(E8Htj};-E#yXQ0lXDp?)I51Sx|vT-D6bt96TQ*s~5Wp}wEq^mQz;0wa@1 zBEhQ59gS-8;f_*D{s3$9T2#V%*5%~!y{OZ#InDmY5zO!p2Km5!+1|XAMZ5FUWd7xg z<5%pjLJnfsFdb^j*?Zo<*u}l~rFj(w=O;mJh4n69rxFf)+7|qwa`BJxJA65>93y^& z?rHJgz5)EC#JQx3Lhh*aQ&ytn*EmBUS2o*a>}BST;T4wAY>dRx0k+?|$p z)kx>`WhukC73tRRb75emT|Z~iJ$sY=uvBQmGKa!jo@;CME3w@Eg06;k2!va;YfpA;o0iU*#o+TQce+Ga~v z1&H!o^0eIlequt-f!2jJnnEvO{?^0jH-sV6`odN!Of9@(Frx@MF}8H`h_(ARgRGE| zxR=Uw^?=E#3znbqPyCHr>}tcw+g=cdrNxR)uidrchj$(iSL8V$AmnwL@118-%neir zErJhLD&z*uJo!w28D>iGp?sT73W z4=ZKIIfDJ?SSiC^x=<6%dsPRxl`$@+cT=7H5ToPtHhxeoAIe+&qFBPBIDj)uqk8G zIjQ&9$e0W4uIZw#yrz;ui2Q6?{(_U-PNTQ;^0tJD++~TLGl`nf;d?n9>bVhZM&QXq zV20Q8(tmggi;Ta?r%i@Xpu5%n;H5;q3#D*Dq*C_V%fxL&7Gov{s@ZV1@+Kwk724Ih zd$G8WFf?2N{?W$-f#q3yXc~A^!adyglElGJtn;s;YpFev#);EKGfH?uRx2V>)w|*8 z+Gf3J%4R)#lA22K)Ya~iwJHB`!JM%QdJ8vizZuwpHR#^(9( zLkKm|ya2#;5`~xL+)P;G-iWwK3Rg9+nsb z60msQBn5gi8hC1n`+T6aF=v6S!r_!QXzAADGr0Jv@9o$pbcX1W{#SSVzMW?m3=cOKr_b@(A-vIyXK6*85(iB>?Ag$G>Z)A1>8GZ4V;>DYKD{NvuXxM>SmwP~8TyfM{Tb!*aNVOxav65{wi^3*SFH*NWuj}JnM zAyM8)+i|@HhnMRU++>GI_E<$u6Yt7q^tB$6(%EVFiS+$?%BC$uzFX+1FD-e%vD39n z_IDH5k`STkj+g7~Zn$YZOhWOQZm7i;5FFPnr{Wd)2dZth?rt6AldQ%8I|V(?H;I`i zvQ`CMXrEelQ>E=^?9XM}ez==GOSPiRSXkV^n7H`CHf!B$!SxB6z9K^=J}FGn5NoBl zqi=c6>uc{lpRGYoJBN4XZr4CxsgZK4yhJe$ju72YFv_5$#e=*kv5o4Hn$fL_^oi-{r>!$dG<5n|sjn2^EMq&+#Uplx8_EDw0&|lD zn~CF;5ytdrBfvTL6u$uC29fj{F_ks^dcH>AS(x<%jewV9UXNV5XkKv(LS<5FGYSyL zVPlc|=}tfECUO&H58{$PM`%#5O6qi5sc&>%UCe=qF7_Oh$!;!oV>z^Fe~dpA)f+4a zFSOUw8E$UN;WwsmQ9}wHph$0KI`+n>rd2_WUB3s?dKL4l?AbOpmKT}n(QyGAR5{WM z+->GP$NR_{!`mT-u+k0&j1OmGY$n)elY0E#7n6B|y*g*1ZDVqirS%*WiEQ3y(=u&Z z<1^w5xN2L^*md8X3-j+EXZ*aPmaV($t6WdqXc{x{82C@$JmF#uzX1hD!q@u|Uv-Jc z$kxn;Bjr>qnOIe3$JQeg=$fKN0$<+ROsQfx)a7Nds!3`KLXfd(n=vp%2Q2@HrET05 zegrqVl+{ZTe%R8~iDqE6Bsq-OM(_vJ1URo_7?ks)eXm>AyDVS#D=lc z7i?Jtrde*IA6#x#G^!MN-y2OF{I06yB$oO_6#ICxLeKJ-*!(@gM+Fx~RE{(gK9uML zaM~D3m;!p)DQGRzk!6sZ;U*8D)+9b0;6_wD>*rJ{Xb*DyD=FW}n1AOf%}OSi9?jOS?AF14dn*^coh$}O9dZU$vagzEQui|0-l*woUCtEiM^38g zGF1hhxPmX>o^9&;qD%qEQU$88g|?J^b>xIGpPsoa60%kdUe_HiLxKlI^OU}CGA#c% z#%{)!^c1-UHxxOqTh|(ETsacVWKeh&oVfj@?)9dfiF27|Chc?&I=l-;YhED+DJwB5 zocBs@#u8;a?-SXmP49m5nuhP>?2(xyn&%zI^wrd77aaN8BxTwwG(6Q`AtAB4+n&A2 zUMuF3c1(I|fi7<1;X%S*#NLDp8EhT#ExaGuN?>Eo^f^jQ;l5GB^vicWronU8e$LQH ziaU#TNRWc9bz1*bTw>=S03mUz>YUsG+!{%ZKT8_w6nb%f-xkB;6BoFe~ z-OhBxnhE<(j!EM6;*gPxh;k0w^uZwo7$CU(ruU5sO7j>`vyqiplZ}3`6MlaJ7EuWHzW*QLeaxc(~AKw&9?3Q7)tuWs|0)!H6-A#*`f@XP6w9p@4T>Qb)S3A4&P$ofm7R6)4Vv?^HDl)(lUjPe_;EhFvmtZA$Hu6|VRsh03$cwDMr< z5+OjRwI%r}FCRu(WWpz^9r(+p0em@*b?X!3bG>XT#@|VJ(L3gF5VDjV*+rCm`jOn& z6L+WjI=92nFs>2E935u((E%nh?pC?kTP;w=gM`lYIp<3xe;}SDIDd>@Jaw0~s(DQh z3M&;=S~a7$j1U>)2H%|KMUh|(TEMadb7U3TqCpoQu-x!A5O$z$$z%~x<%qAV%C`K& z`iiSJqF}yD^(_%P>dD;cD}N`$LwIPh^)yRgjgW`%`(M}y-m3>x;`6=QgI4ohn#$%% z6=pk_SkE$S=C{TQw0@VJ#HGrgLj1zNP&X~KTBQ_!_Rt2$>Bq?{E*lVRG_V)u zDdWeN5kYq%?PIM*b26zm;{!`OeoaBM0nKd&vETX@_NA?vdQP*Xzi|MJY|47|RoEl; zKX}4VCct5fqsMzEn3(0!p{@)n5%vSi>yN=D89$eHIr|;+QY;xjkI_?Z*;u|47Yrlr zpUE>7rgO(3#&Pb`H90DR{WhRlBN6kPyH<`aRgWh)@L!6+fq$i99mFnxT`b>>OCaEU zZ66<|_N212nx=MnSQKkXKZ)w*_-=20Ad^(y2z&lg@>!X3Ewoq>6LZLPN7pzDYg+Yg z6ZUh)g$Yrm0r;}4TjKl;QL#LF^NKAsP9#LJclMJ_MowSPZjPB>ubJ0((a?^9 zXiD%w{LD9S96zr%WVi3)ui6$sF=YeE@9JGGKh_QzZ3Q!@ zJXh;xDjG>vEjza49Uq#7-=B8Z_KL}?TC4En>9E)h#CsYa_|9=}iz@1Kk3c8WqeT?0 zCLhPAIGFcZ6DIR11HG!g@pRn3ii3)IKEN0xSb6k9qvb+4+W(PjP4Yc|69>y83%0p$ zpBdP5529nJxrHw2*7;}ipAzA}Er=fW$mr+w7K$aL>1mbul~Kj|mAzz8Ktw>N#~fC- z(6M8~c1WnXT;di@j4xO`>1B+$KV$Oy-Zc-!(GAUBYh`ZIT;kX(?q;Q~7klAxaV~VE zCtJv`O;jL~nify#RX4ORQCZG`U_QA^uJSLhrD|=}pUu$4e%xRO>+4T^ic4`02k*uc zmmoc}PObd8o=OzoIoga5IJvcc(z|GhX5jMs5XDU78=Pzl~CHdVM@t=a#GuN7pvW`&2PLD;AOx+(@>~7S|5Kx@JVK@w9 zjXS&5YynqwdyAYBf7$`7x%rt{Fz>uGf&Iy58y(4J`)C*^pCfR|*Y}B4LA6BXkZXfv zQ_fQ|);8bR&H9QTDEF2oX_Zjk^077EaVD8olTUhh+-Q<{;{_H|ZtZ1! z$*kz2>UWl|VCQtdP<&KuUYUv}=NMJ~+g*`+Uosh$mbgvh9AQeHA7P!YI}9R6ZrG;a z3~}b~4H-6xHx^cx`k@e)m1Y6)6vnO!?r6gZ44~@q){lj)v~Mlfd#a#4D-{(H=RKiu$~R*_}ivLAyDh ztTY#e`be~PRB!ifmt147TopZ^*!5|xkCo;Wf;-6ECEUZM^;Tu6A1()^!IZUGhgbjZ z#^#osyRlUpwwzK%XOC>Tu_cI!bu#7|{U>TU=Y&^C1lw8|7@$ZR(;7a0sW1_tHO_q&n&5X(34< z;*8(lkPE&JOFpWaBtYdU=0ML>-q@(gQ~gZ*H=1n;P|C#h}!N=qR{MJ8`9NqupBhl9hZZRH-LSTHX$p z4gF@X^BK{&4EnNL5=w`}b?60q3T}nH&>ux{m92ZX*Y2flmv3401t~84GWeF=ZK7T4 z;U_c{)|t#TZS`vZUSd}sj#IJsl&gB-gQ3U#F=>TMWgUORQNi{^)vpBHV9p+J)Z#D= z`dJH~S?yvX5BRph`UDGbg~b(Z^5a;Cx_t#oV|R0E`czAk_d8Z-DLBNbk8b^)RGq-D zB1i0W4AApg48^SqxFS&P?QgRsZV+S*%2 z>hk6%9={6?A7N)W?9~#T|I~s2-=^vWNckuz^pE9dVvu_eyQn#UUnh$`Z$m2}q9Hdt zZHQKWP!I>}bOS&>(@WlD^7Roe)jyv{x~8p03-a^*jLw%GL9aYvDMyDi)W(iNM<30)8lqK_J8^0p;L=T z2N*}EFHKYEG0{RiKbQC|e(RR8?3Ghd`tbSb|Q#v8a>HuF&|;tJ`yO$^y-u4-u+>@sdx|*Zx=n&l20sob2{Ur zRiN~De#@rgQu0w&-vhj49X=FgPrE!}*d_dmEkCm?>`~UIncCn^OFB_A-hgcA+w6nb zN3_WvDA-zlueA!9wz}BHQ8lLF{oUCZIu|x=WL^ulx8oxQ zt^?g}pUSDgE-E5}*Y`d)8k9#uG)s1~HfLx=@wzl+uI=RRusDWQq_Yt!XR+Hb!mj2q zcQ`7K0-E1$Qn68Is_e4Hzk$a`?h8)YyFVf?6Al|{+IAYiXFEGqE%tcdYxt)*81nGk z9Bl6WV14<c{Jg?}gvsDY%=|UvUsEG6>M#+V{^Sj&)_iO|wGms)T1{h5qWxm|(ixG_ebUpa9 z`Xp4^+my^F{=^az0q}x^??87pL@aNtUOAolDq{O+@22PcMi+pAc{?^^-$gNk?l9EV zOZ9@y(+y$ue0c)qA$o~zW2tV*p4c?zhq$%>*K8w=x8@aBADuc7*MocDM5b1Jqhikf z$H^AD!zKOU>-aV?L(`OHs?|maul2N5qZ9LS32Xx|*nulEZg_g|nBeOe&F@W9_D-%1 z*$XV2RQ&-v0Ek(Dj`)29{6}>RFx=IuxN&>DNyYwZ9%vrfEmP;o2MW8gby0lM4#M$kJ)*#rb3ecbxts3NdU3oOeZ2qzWP`Eo zlZay3SjOoKV2F@kw13x+w;l?fn-BtMnZf{>X9RJ)+Q&D9CFn@boaT=S(w&nmKMSisuk2InGND?B_&`uoil6MjR)REk zhtER3u&-_L`v5c2w?!3o**}vkVBG2(?pG4iee;nYR4<1Gyj3$-vBJkC2Q~X$4M?3P zMXcB2$cY7<#2D~LM$yzz@a>^KcOUh}jS=ubUP7$P4gNm95uc(&{w?yEF9UtuZU8;o zsguv^##=l=3n%i;YVYg5d6?+ig!2}~9d2KpsobcHDIxX^+hlkRVKTZ`>}NvYGNRU*XG%;_pH0T*n1c9xO43~FYW&Tl?>v6WBQG_voD zf9TSz>kF9;Bg;z|U0kTo%bpLc)lHQCIL~5m)1)9~Sug|dRTxd#5znn@_=WGJGNPd` zrN%y%u1L}x_Gv~Ux6rqG-SPjl_vHUj?e7;tyREVn*=4;nO+z%Y4=O_;YYUS~A|m^w zx{=*5wn3QD$Wr!w=}WTZMiN40yOxlB9p7^t#oU+q4?cc+&&&Hc^Sqz^{Va2)MOQ>; z_ceFG+2`#`vkVTDyL{AJFqASrA+x%~Y8h7h3SL3gor6U~l{8!uzEFhjFkQZHXVT4A zs+jWv>g_ED89p9;_H;nUT2D2wn(6QUw?>|nz~@IK@$FvSVc)G8waX3~rKjT$!7ia; zMsQK(qxb2x$jweamoNsm+ZDZGzK{Kq)12%mc1>U=%5E6peW@5-RZ=}v?(>?lsF7Hf zc33j>sMNfUbIm+h%;~cf#tWh1VxqM^3I|aNCng*_mOXmtI*P0_4ENWhNRaCML}R+S zq{~Dpl7APs*6oZo6*PszQP<4_0}gcK8ir)*dNcP8EAv)WKQg*EIWN@plLP)hyCT%{ z^H5iz+mn+r6{e$3<_dIkO~qp!r+Tw7F&7ml^%XuQsb7{_2+gJRE0Yu8(q-NF{*)zz z`||}G$$icvlKAIj6KR2I;%uk}nTIb#KEX1gft(U;P}d@au8wy282=FgWa}TA?iiKe)tMGoC6!5qbnoZ-F3B3lWma`G5PqacDV`fmUt?R^JPp{(gSE;;I7PkBT06{1 zT^0S2yM}2BL|3?$SKE6?iJl$)OwqaQ{x;f)k`f|MD!BU)nLvNP|MJ*vL{A4kMn6jI zufC3o>*vMk7v~n!vd7hxU{{^v%sIvrG#!;L|J#Zr~9uRa^7u zc++>>R&MTn@jsN*{;=zQY3UztTr@qwg-bj3pUha;hODN<7K+h*1Z(s&VLyZ7wEZ|z z2aW_$I^g&DUp>7WPx)c*bTH^sy6;S=O~}dB_DQ3**;oB-`61D~s+RS!_AZNTbl-)A zDBSy)mn&ciqq?h-{art~cIF$h+I$-ia?0n6xc=7!PY2mo&O*(?*E{Uz18Lr1Pm$%$ ziUSM?`7|Mk&-P&$IQ)i}Y>VLEJxk}@2>BAeuW zCps`^qMD!mqp%N9MH*3J-feLEO|>;g}-N}{F2;r$2=B|TB26gtZNk~>a~-( zI@px%nygUQ%XQyC*Dnqi7QAZZWx!>1Me@xqL`P~lbas1>vm?HK6fMYRK#$^kB@`1p z-gke6H`eV7%QUV)9Ud;gH#69ToW>GiO-(H%dG%~cfM;^7pv%95?+4)_?3!7|%ZX!O zPfIWNjySdt*>_GAN>5z#>RZTn^16o(GKbj&2+4eU?HF-pxtiXwC6>UF{jN-2Vo-N^ zt?pNd5b$1+=!RF0;~pJ_!z#As_@c*#4xI!^=Mq3L~m z3oEJ}-U@{MWb+~w4CY~Ttb<}#HIYeRbXOR&9858hS}xC#dy@DhAOVO7dP{kVPzUEpDI zYxkjvsPqcWOr5L`k6y-{)zs*7yIWRMbmb=S(`}zkvoeNi2iEG6iruphhI zSItdqLa(%aW|$Xyf2B3xph(-@tlT#$el<>h(M4C*LMMoBwq?>;*Y#5uj&l7QTsQfJ z_ooX%{6zyE;fWu`(iiXSd{V(NxFSi(O7k-yU&>`D)w;<8C_e^xf**PtpC9P(iis<7$kT5SuKKL1i z*(tp8`jfLuZBIdG#nQ^BJBB~6l@$ygS?iIjDH}2(POr57vMh1+lf1$yg}oLj_^XSp zq%+mdA-<|Jve+8UQzX?V_tiy@@u}0};edeezYt5jd5B)4@TBrLo=4MlFH4HGb*!Y+{A~Y;38X+h=m94aB5im_vT=^3>C-OsC)?HUqSs469fqT#MOYCgo z(b+<1IlTpN#^+CW@SoLyG;~)QS3<4Ylz~l0hdKRlx+Vaa$O<^lwzVUF{Ug@#$m_tO zFfH-BP`aRefdgZZuFnOv6{{cy4oigIV1G|#>a z8Q~5SyLxVA+vyFgBRhn6!Lbx&W(E-c+svaf;ZU?JYR~oqGg!7g5aO?M3QyAMgCID$ zX~a`Cl=7`OYU}?8=K&FjqIZ?FJXx5Tff|41@Gf^90iqR-58vzAxr!$EI>vb+q!oyQVmrlCQda}wF!dSxE82P3M;b6wauDdi;(JcS03O_%oN?zwk}O~P(nUAzlv$G~UbPfS=JK-3S!S-dU~LhsSg zR@u67Q%NqKgAikTa2T(_?+@m&(KgDpAzF2sf%i8FyLFZLb))ApgNb~QZYNm%KV4>J zubsjX5cX*6#@{nmEXc&i8w~?vlG1@l4iJRc=n(|eM}oPB{)iP7hRC9gdaCI`G#>*U zU?ddoxmEQ5O35j_i^g!nZb*AjUrfxnv0@+yDteJuRX~^z_JKNo@*B`x3Y6rwu?x@^*&AX;;HHjo8n+Af4Vrm8; z+wBr-$0)&FJi zS4#{5h*BKVPLiat+lmC~S7WZC&s^hakePiuZql%L{1=gsnV|b&z(H37+Txm*^w!jk zUz_xlS6?@f)})y;Kcn6Nh`7Bw_m3B#CQ>G6Pa1}3CMVV1&94K5V0zT#9OyLp45}GPHk>Z zZtB+lO4yN$YN^8oG6(Wd0q9ZdM zkptLFOqXbQ>$QY<`I2s0TXADqy=mle39o42s|9i^eOJ9Xoa_X{)IiZfft= zl{M5Wk0u?}&SRMbAoE;~QK!*<6A?U=9OOLY_AlAka6pdEm&=ryB$)Z zaatebm32Z2+to@i)T2S#SPQ=7pt1X+2t*s}76(Im>>bdynIcwbO}!$K)nJwFNeLrX z4m3X2J-lhuRi90f#Ti2UOzEzvVU02d@VHf>Q#;rq{MSQ61g5RV8mwIBAMp5 zmB3q+4W|Y@Td2EQo%NYC7!%apJjXZAvO08b3b{;J!CC<7Ymqv%({Vu@V5611&6>Jz< zn8;f~G#Tt=&%GA|Hn-aLn56~Npb5t%h$b()RlYOpg4c)(6o1@i>_xq5G=ofRw7bkd z*<4^$iP6fjWuzq~KoAA?A0#=^KM}YHns4($2?I77yJe?0JYbI!^0`eEXb7)1rP7EZ zeUK`UD(tDhaFv;3{rXeRh@+Zg*uvOG;Kkk`Cef|)xi_Bf0cgTikIdPkD_boF_7TJ# zj>m*^EMoDX|Fh5&FKxso3FSmmjvc|1c7gBji7 z+q(N{(PQcmboc8mPqASx1gOV-ipKeNfWzcq_jrfYA_F!Xj5DnDZBNjeEXR?dXe#=F zP}{jm_DROs7NX7o)7b^=JjRQb3uHFl6A&ESFgx1boikoE6t9H|*Yc~b21)d-%f2GQ z^^El1Bht9Ew6vB>k0oe5rPa*J$;ptBiHVq=F)I!vw3_m)^1yI6EJYB*fc6d@nZ%B6sGD?m42+k{QSLT1tB56OJTj zW(JkU;XBT=iIW(!Xc$`vUe!m [Introduced][ce-7125] in GitLab 8.14. -You can prevent merge requests from being merged until all discussions are resolved. +You can prevent merge requests from being merged until all discussions are +resolved. Navigate to your project's settings page, select the **Only allow merge requests to be merged if all discussions are resolved** check @@ -50,8 +51,26 @@ are resolved. ![Only allow merge if all the discussions are resolved message](img/only_allow_merge_if_all_discussions_are_resolved_msg.png) +### Move all unresolved discussions in a merge request to an issue + +> [Introduced][ce-7180] (Currently on Backlog) + +To delegate unresolved discussions to a new issue you can click the link **open +an issue to resolve them later**. + +This will prepare an issue with content referring to the merge request and +discussions. + +![Issue mentioning discussions in a merge request](img/preview_issue_for_discussions.png) + +Hitting **Submit issue** will cause all discussions to be marked as resolved and +add a note referring to the newly created issue. + +You can now proceed to merge the merge request from the UI. + [ce-5022]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5022 [ce-7125]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7125 +[ce-7180]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7180 [resolve-discussion-button]: img/resolve_discussion_button.png [resolve-comment-button]: img/resolve_comment_button.png [discussion-view]: img/discussion_view.png diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 049b4fb214c..cfb7c45de8e 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -28,6 +28,14 @@ module API new_params end + + def merge_request_for_resolving_discussions + return unless merge_request_iid = params[:merge_request_for_resolving_discussions] + + @merge_request_for_resolving_discussions ||= MergeRequestsFinder.new(current_user, project_id: user_project.id). + execute. + find_by(iid: merge_request_iid) + end end resource :issues do @@ -151,24 +159,28 @@ module API # Create a new project issue # # Parameters: - # id (required) - The ID of a project - # title (required) - The title of an issue - # description (optional) - The description of an issue - # assignee_id (optional) - The ID of a user to assign issue - # milestone_id (optional) - The ID of a milestone to assign issue - # labels (optional) - The labels of an issue - # created_at (optional) - Date time string, ISO 8601 formatted - # due_date (optional) - Date time string in the format YEAR-MONTH-DAY - # confidential (optional) - Boolean parameter if the issue should be confidential + # id (required) - The ID of a project + # title (required) - The title of an issue + # description (optional) - The description of an issue + # assignee_id (optional) - The ID of a user to assign issue + # milestone_id (optional) - The ID of a milestone to assign issue + # labels (optional) - The labels of an issue + # created_at (optional) - Date time string, ISO 8601 formatted + # due_date (optional) - Date time string in the format YEAR-MONTH-DAY + # confidential (optional) - Boolean parameter if the issue should be confidential + # merge_request_for_resolving_discussions (optional) - The IID of a merge request for which to resolve discussions # Example Request: # POST /projects/:id/issues post ':id/issues' do required_attributes! [:title] - keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels] + keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels, :merge_request_for_resolving_discussions] keys << :created_at if current_user.admin? || user_project.owner == current_user attrs = attributes_for_keys(keys) + attrs[:labels] = params[:labels] if params[:labels] + attrs[:merge_request_for_resolving_discussions] = merge_request_for_resolving_discussions if params[:merge_request_for_resolving_discussions] + # Convert and filter out invalid confidential flags attrs['confidential'] = to_boolean(attrs['confidential']) attrs.delete('confidential') if attrs['confidential'].nil? diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index 90419368f22..dbe5ddccbcf 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -55,6 +55,30 @@ describe Projects::IssuesController do end describe 'GET #new' do + context 'internal issue tracker' do + before do + sign_in(user) + project.team << [user, :developer] + end + + it 'builds a new issue' do + get :new, namespace_id: project.namespace.path, project_id: project + + expect(assigns(:issue)).to be_a_new(Issue) + end + + it 'fills in an issue for a merge request' do + project_with_repository = create(:project) + project_with_repository.team << [user, :developer] + mr = create(:merge_request_with_diff_notes, source_project: project_with_repository) + + get :new, namespace_id: project_with_repository.namespace.path, project_id: project_with_repository, merge_request_for_resolving_discussions: mr.iid + + expect(assigns(:issue).title).not_to be_empty + expect(assigns(:issue).description).not_to be_empty + end + end + context 'external issue tracker' do it 'redirects to the external issue tracker' do external = double(new_issue_path: 'https://example.com/issues/new') @@ -272,6 +296,42 @@ describe Projects::IssuesController do end describe 'POST #create' do + context 'resolving discussions in MergeRequest' do + let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first } + let(:merge_request) { discussion.noteable } + let(:project) { merge_request.source_project } + + before do + project.team << [user, :master] + sign_in user + end + + let(:merge_request_params) do + { merge_request_for_resolving_discussions: merge_request.iid } + end + + def post_issue(issue_params) + post :create, namespace_id: project.namespace.to_param, project_id: project.to_param, issue: issue_params, merge_request_for_resolving_discussions: merge_request.iid + end + + it 'creates an issue for the project' do + expect { post_issue({ title: 'Hello' }) }.to change { project.issues.reload.size }.by(1) + end + + it "doesn't overwrite given params" do + post_issue(description: 'Manually entered description') + + expect(assigns(:issue).description).to eq('Manually entered description') + end + + it 'resolves the discussion in the merge_request' do + post_issue(title: 'Hello') + discussion.first_note.reload + + expect(discussion.resolved?).to eq(true) + end + end + context 'Akismet is enabled' do before do allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true) diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb new file mode 100644 index 00000000000..762cab0c0e1 --- /dev/null +++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb @@ -0,0 +1,76 @@ +require 'rails_helper' + +feature 'Resolving all open discussions in a merge request from an issue', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) } + let(:merge_request) { create(:merge_request, source_project: project) } + let!(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: merge_request, project: project)]).first } + + before do + project.team << [user, :master] + login_as user + end + + context 'with the internal tracker disabled' do + before do + project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + end + + it 'does not show a link to create a new issue' do + expect(page).not_to have_link 'open an issue to resolve them later' + end + end + + context 'merge request has discussions that need to be resolved' do + before do + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + end + + it 'shows a warning that the merge request contains unresolved discussions' do + expect(page).to have_content 'This merge request has unresolved discussions' + end + + it 'has a link to resolve all discussions by creating an issue' do + page.within '.mr-widget-body' do + expect(page).to have_link 'open an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_for_resolving_discussions: merge_request.iid) + end + end + + context 'creating an issue for discussions' do + before do + page.click_link 'open an issue to resolve them later', href: new_namespace_project_issue_path(project.namespace, project, merge_request_for_resolving_discussions: merge_request.iid) + end + + it 'shows an issue with the title filled in' do + title_field = page.find_field('issue[title]') + + expect(title_field.value).to include(merge_request.title) + end + + it 'has a mention of the discussion in the description' do + description_field = page.find_field('issue[description]') + + expect(description_field.value).to include(discussion.first_note.note) + end + + it 'has a hidden field for the merge request' do + merge_request_field = find('#merge_request_for_resolving_discussions', visible: false) + + expect(merge_request_field.value).to eq(merge_request.iid.to_s) + end + + it 'can create a new issue for the project' do + expect { click_button 'Submit issue' }.to change { project.issues.reload.size }.by(1) + end + + it 'resolves the discussion in the merge request' do + click_button 'Submit issue' + + discussion.first_note.reload + + expect(discussion.resolved?).to eq(true) + end + end + end +end diff --git a/spec/models/discussion_spec.rb b/spec/models/discussion_spec.rb index 2a67c60b978..bc32fadd391 100644 --- a/spec/models/discussion_spec.rb +++ b/spec/models/discussion_spec.rb @@ -521,6 +521,15 @@ describe Discussion, model: true do end end + describe "#first_note_to_resolve" do + it "returns the first not that still needs to be resolved" do + allow(first_note).to receive(:to_be_resolved?).and_return(false) + allow(second_note).to receive(:to_be_resolved?).and_return(true) + + expect(subject.first_note_to_resolve).to eq(second_note) + end + end + describe "#collapsed?" do context "when a diff discussion" do before do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 2cc818af6c7..925232169f1 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1124,6 +1124,46 @@ describe MergeRequest, models: true do allow(subject).to receive(:diff_discussions).and_return([first_discussion, second_discussion, third_discussion]) end + describe '#resolvable_discussions' do + before do + allow(first_discussion).to receive(:to_be_resolved?).and_return(true) + allow(second_discussion).to receive(:to_be_resolved?).and_return(false) + allow(third_discussion).to receive(:to_be_resolved?).and_return(false) + end + + it 'includes only discussions that need to be resolved' do + expect(subject.resolvable_discussions).to eq([first_discussion]) + end + end + + describe '#discussions_can_be_resolved_by? user' do + let(:user) { build(:user) } + + context 'all discussions can be resolved by the user' do + before do + allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true) + allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true) + allow(third_discussion).to receive(:can_resolve?).with(user).and_return(true) + end + + it 'allows a user to resolve the discussions' do + expect(subject.discussions_can_be_resolved_by?(user)).to be(true) + end + end + + context 'one discussion cannot be resolved by the user' do + before do + allow(first_discussion).to receive(:can_resolve?).with(user).and_return(true) + allow(second_discussion).to receive(:can_resolve?).with(user).and_return(true) + allow(third_discussion).to receive(:can_resolve?).with(user).and_return(false) + end + + it 'allows a user to resolve the discussions' do + expect(subject.discussions_can_be_resolved_by?(user)).to be(false) + end + end + end + describe "#discussions_resolvable?" do context "when all discussions are unresolvable" do before do diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 5700f800c2e..553983575c4 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -692,6 +692,32 @@ describe API::Issues, api: true do ]) end + context 'resolving issues in a merge request' do + let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first } + let(:merge_request) { discussion.noteable } + let(:project) { merge_request.source_project } + before do + project.team << [user, :master] + post api("/projects/#{project.id}/issues", user), + title: 'New Issue', + merge_request_for_resolving_discussions: merge_request.iid + end + + it 'creates a new project issue' do + expect(response).to have_http_status(:created) + end + + it 'resolves the discussions in a merge request' do + discussion.first_note.reload + + expect(discussion.resolved?).to be(true) + end + + it 'assigns a description to the issue mentioning the merge request' do + expect(json_response['description']).to include(merge_request.to_reference) + end + end + context 'with due date' do it 'creates a new project issue' do due_date = 2.weeks.from_now.strftime('%Y-%m-%d') diff --git a/spec/services/discussions/resolve_service_spec.rb b/spec/services/discussions/resolve_service_spec.rb new file mode 100644 index 00000000000..12c3cdf28c6 --- /dev/null +++ b/spec/services/discussions/resolve_service_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe Discussions::ResolveService do + describe '#execute' do + let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first } + let(:project) { merge_request.project } + let(:merge_request) { discussion.noteable } + let(:user) { create(:user) } + let(:service) { described_class.new(discussion.noteable.project, user, merge_request: merge_request) } + + before do + project.team << [user, :master] + end + + it "doesn't resolve discussions the user can't resolve" do + expect(discussion).to receive(:can_resolve?).with(user).and_return(false) + + service.execute(discussion) + + expect(discussion.resolved?).to be(false) + end + + it 'resolves the discussion' do + service.execute(discussion) + + expect(discussion.resolved?).to be(true) + end + + it 'executes the notification service' do + expect_any_instance_of(MergeRequests::ResolvedDiscussionNotificationService).to receive(:execute).with(discussion.noteable) + + service.execute(discussion) + end + + it 'adds a system note to the discussion' do + issue = create(:issue, project: project) + + expect(SystemNoteService).to receive(:discussion_continued_in_issue).with(discussion, project, user, issue) + service = described_class.new(project, user, merge_request: merge_request, follow_up_issue: issue) + service.execute(discussion) + end + + it 'can resolve multiple discussions at once' do + other_discussion = Discussion.for_diff_notes([create(:diff_note_on_merge_request, noteable: discussion.noteable, project: discussion.noteable.source_project)]).first + + service.execute([discussion, other_discussion]) + + expect(discussion.resolved?).to be(true) + expect(other_discussion.resolved?).to be(true) + end + end +end diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb new file mode 100644 index 00000000000..4cfba35c830 --- /dev/null +++ b/spec/services/issues/build_service_spec.rb @@ -0,0 +1,130 @@ +require 'spec_helper.rb' + +describe Issues::BuildService, services: true do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + project.team << [user, :developer] + end + + context 'for discussions in a merge request' do + let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) } + let(:issue) { described_class.new(project, user, merge_request_for_resolving_discussions: merge_request).execute } + + def position_on_line(line_number) + Gitlab::Diff::Position.new( + old_path: "files/ruby/popen.rb", + new_path: "files/ruby/popen.rb", + old_line: nil, + new_line: line_number, + diff_refs: merge_request.diff_refs + ) + end + + describe '#items_for_discussions' do + it 'has an item for each discussion' do + create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.source_project, position: position_on_line(13)) + service = described_class.new(project, user, merge_request_for_resolving_discussions: merge_request) + + service.execute + + expect(service.items_for_discussions.size).to eq(2) + end + end + + describe '#item_for_discussion' do + let(:service) { described_class.new(project, user, merge_request_for_resolving_discussions: merge_request) } + + it 'mentions the author of the note' do + discussion = Discussion.new([create(:diff_note_on_merge_request, author: create(:user, username: 'author'))]) + expect(service.item_for_discussion(discussion)).to include('@author') + end + + it 'wraps the note in a blockquote' do + note_text = "This is a string\n"\ + ">>>\n"\ + "with a blockquote\n"\ + "> That has a quote\n"\ + ">>>\n" + note_result = "This is a string\n"\ + "> with a blockquote\n"\ + "> > That has a quote\n" + discussion = Discussion.new([create(:diff_note_on_merge_request, note: note_text)]) + expect(service.item_for_discussion(discussion)).to include(">>>\n#{note_result}\n>>>") + end + end + + describe '#execute' do + it 'has the merge request reference in the title' do + expect(issue.title).to include(merge_request.title) + end + + it 'has the reference of the merge request in the description' do + expect(issue.description).to include(merge_request.to_reference) + end + + it 'does not assign title when a title was given' do + issue = described_class.new(project, user, + merge_request_for_resolving_discussions: merge_request, + title: 'What an issue').execute + + expect(issue.title).to eq('What an issue') + end + + it 'does not assign description when a description was given' do + issue = described_class.new(project, user, + merge_request_for_resolving_discussions: merge_request, + description: 'Fix at your earliest conveignance').execute + + expect(issue.description).to eq('Fix at your earliest conveignance') + end + + describe 'with multiple discussions' do + before do + create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.target_project, position: position_on_line(15)) + end + + it 'mentions all the authors in the description' do + authors = merge_request.diff_discussions.map(&:author) + + expect(issue.description).to include(*authors.map(&:to_reference)) + end + + it 'has a link for each unresolved discussion in the description' do + notes = merge_request.diff_discussions.map(&:first_note) + links = notes.map { |note| Gitlab::UrlBuilder.build(note) } + + expect(issue.description).to include(*links) + end + + it 'mentions additional notes' do + create_list(:diff_note_on_merge_request, 2, noteable: merge_request, project: merge_request.target_project, position: position_on_line(15)) + + expect(issue.description).to include('(+2 comments)') + end + end + end + end + + context 'For a merge request without discussions' do + let(:merge_request) { create(:merge_request, source_project: project) } + + describe '#execute' do + it 'mentions the merge request in the description' do + issue = described_class.new(project, user, merge_request_for_resolving_discussions: merge_request).execute + + expect(issue.description).to include("Review the conversation in #{merge_request.to_reference}") + end + end + end + + describe '#execute' do + it 'builds a new issues with given params' do + issue = described_class.new(project, user, title: 'Issue #1', description: 'Issue description').execute + + expect(issue.title).to eq('Issue #1') + expect(issue.description).to eq('Issue description') + end + end +end diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 5c0331ebe66..8bde61ee336 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -136,5 +136,48 @@ describe Issues::CreateService, services: true do end it_behaves_like 'new issuable record that supports slash commands' + + context 'for a merge request' do + let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first } + let(:merge_request) { discussion.noteable } + let(:project) { merge_request.source_project } + let(:opts) { { merge_request_for_resolving_discussions: merge_request } } + + before do + project.team << [user, :master] + end + + it 'resolves the discussion for the merge request' do + described_class.new(project, user, opts).execute + discussion.first_note.reload + + expect(discussion.resolved?).to be(true) + end + + it 'added a system note to the discussion' do + described_class.new(project, user, opts).execute + + reloaded_discussion = MergeRequest.find(merge_request.id).discussions.first + + expect(reloaded_discussion.last_note.system).to eq(true) + end + + it 'assigns the title and description for the issue' do + issue = described_class.new(project, user, opts).execute + + expect(issue.title).not_to be_nil + expect(issue.description).not_to be_nil + end + + it 'can set nil explicityly to the title and description' do + issue = described_class.new(project, user, + merge_request_for_resolving_discussions: merge_request, + description: nil, + title: nil).execute + + expect(issue.description).to be_nil + expect(issue.title).to be_nil + end + end end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 435cfb07292..07a9d8e1997 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -712,4 +712,32 @@ describe SystemNoteService, services: true do end end end + + describe '.discussion_continued_in_issue' do + let(:discussion) { Discussion.for_diff_notes([create(:diff_note_on_merge_request)]).first } + let(:merge_request) { discussion.noteable } + let(:project) { merge_request.source_project } + let(:issue) { create(:issue, project: project) } + let(:user) { create(:user) } + + def reloaded_merge_request + MergeRequest.find(merge_request.id) + end + + before do + project.team << [user, :developer] + end + + it 'creates a new note in the discussion' do + # we need to completely rebuild the merge request object, or the `@discussions` on the merge request are not reloaded. + expect { SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue) }. + to change { reloaded_merge_request.discussions.first.notes.size }.by(1) + end + + it 'mentions the created issue in the system note' do + note = SystemNoteService.discussion_continued_in_issue(discussion, project, user, issue) + + expect(note.note).to include(issue.to_reference) + end + end end From 7a9ba9bb85c1ab0e4bb4f116ce45b9a82aea3096 Mon Sep 17 00:00:00 2001 From: winniehell Date: Mon, 5 Dec 2016 23:02:23 +0100 Subject: [PATCH 033/140] Add failing test for #25191 --- spec/lib/banzai/filter/relative_link_filter_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index 2bfa51deb20..df2dd173b57 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -175,7 +175,7 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do allow_any_instance_of(described_class).to receive(:uri_type).and_return(:raw) doc = filter(image(escaped)) - expect(doc.at_css('img')['src']).to match '/raw/' + expect(doc.at_css('img')['src']).to eq "/#{project_path}/raw/#{Addressable::URI.escape(ref)}/#{escaped}" end context 'when requested path is a file in the repo' do From 30f050491694e27232ddab679785ddac2dd46514 Mon Sep 17 00:00:00 2001 From: awhildy Date: Sun, 27 Nov 2016 21:25:45 -0800 Subject: [PATCH 034/140] Create animation page and clean up index Add guidance on timings and easing [ci skip] Detail when not to use easing Add dropdown and hover examples Add quick update animation --- doc/development/ux_guide/animation.md | 42 ++++++++++++++++++ doc/development/ux_guide/basics.md | 15 ------- doc/development/ux_guide/copy.md | 4 +- .../ux_guide/img/animation-dropdown.gif | Bin 0 -> 22483 bytes .../ux_guide/img/animation-hover.gif | Bin 0 -> 247388 bytes .../ux_guide/img/animation-quickupdate.gif | Bin 0 -> 6441 bytes doc/development/ux_guide/index.md | 17 ++++--- 7 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 doc/development/ux_guide/animation.md create mode 100644 doc/development/ux_guide/img/animation-dropdown.gif create mode 100644 doc/development/ux_guide/img/animation-hover.gif create mode 100644 doc/development/ux_guide/img/animation-quickupdate.gif diff --git a/doc/development/ux_guide/animation.md b/doc/development/ux_guide/animation.md new file mode 100644 index 00000000000..daeb15460c2 --- /dev/null +++ b/doc/development/ux_guide/animation.md @@ -0,0 +1,42 @@ +# Animation + +Motion is a tool to help convey important relationships, changes or transitions between elements. It should be used sparingly and intentionally, highlighting the right elements at the right moment. + +## Timings + +The longer distance an object travel, the timing should be longer for the animation. However, when in doubt, we should avoid large, full screen animations. + +Subtle animations, or objects leaving the screen should take **100-200 milliseconds**. Objects entering the screen, or motion we want to use to direct user attention can take between **200-400 milliseconds**. We should avoid animations of longer than 400 milliseconds as they will make the experience appear sluggish. If a specific animation feels like it will need more than 400 milliseconds, revisit the animation to see if there is a simpler, easier, shorter animation to implement. + +## Easing + +Easing specifies the rate of change of a parameter over time (see [easings.net](http://easings.net/)). Adding an easing curve will make the motion feel more natural. Being consistent with the easing curves will make the whole experience feel more cohesive and connected. + +* When an object is entering the screen, or transforming the scale, position, or shape, use the **easeOutQuint** curve (`cubic-bezier(0.23, 1, 0.32, 1)`) +* When an object is leaving the screen, or transforming the opacity or color, no easing curve is needed. It shouldn't _slow down_ as it is exiting the screen, as that draws attention on the leaving object, where we don't want it. Adding easing to opacity and color transitions will make the motion appear less smooth. Therefore, for these cases, motion should just be **linear**. + +## Types of animations + +### Hover + +Interactive elements (links, buttons, etc.) should have a hover state. A subtle animation for this transition adds a polished feel. We should target a `200ms linear` transition for a color hover effect. + +View the [interactive example](http://codepen.io/awhildy/full/GNyEvM/) here. + +![Hover animation](img/animation-hover.gif) + +### Dropdowns + +The dropdown menu should feel like it is appearing from the triggering element. Combining a position shift `400ms cubic-bezier(0.23, 1, 0.32, 1)` with a opacity animation `200ms linear` on the second half of the motion achieves this affect. + +View the [interactive example](http://codepen.io/awhildy/full/jVLJpb/) here. + +![Dropdown animation](img/animation-dropdown.gif) + +### Quick update + +When information is updating in place, a quick, subtle animation is needed. The previous content should cut out, and the new content should have a quick, `200ms linear` fade in. + +![Quick update animation](img/animation-quickupdate.gif) + +> TODO: Add guidance for other kinds of animation \ No newline at end of file diff --git a/doc/development/ux_guide/basics.md b/doc/development/ux_guide/basics.md index a29cfa096b2..76b739386a5 100644 --- a/doc/development/ux_guide/basics.md +++ b/doc/development/ux_guide/basics.md @@ -5,8 +5,6 @@ * [Typography](#typography) * [Icons](#icons) * [Color](#color) -* [Motion](#motion) -* [Voice and tone](#voice-and-tone) --- @@ -61,16 +59,3 @@ GitLab uses Font Awesome icons throughout our interface. > TODO: Establish a perspective for color in terms of our personality and rationalize with Marketing usage. ---- - -## Motion - -Motion is a tool to help convey important relationships, changes or transitions between elements. It should be used sparingly and intentionally, highlighting the right elements at the right moment. - -> TODO: Determine a more concrete perspective on motion, create consistent easing/timing curves to follow. - ---- - -## Voice and tone - -The copy for GitLab is clear and direct. We strike a clear balance between professional and friendly. We can empathesize with users (such as celebrating completing all Todos), and remain respectful of the importance of the work. We are that trusted, friendly coworker that is helpful and understanding. diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md index b557fb47120..8896d450f14 100644 --- a/doc/development/ux_guide/copy.md +++ b/doc/development/ux_guide/copy.md @@ -1,6 +1,8 @@ # Copy -The copy and messaging is a core part of the experience of GitLab and the conversation with our users. Follow the below conventions throughout GitLab. +The copy for GitLab is clear and direct. We strike a clear balance between professional and friendly. We can empathesize with users (such as celebrating completing all Todos), and remain respectful of the importance of the work. We are that trusted, friendly coworker that is helpful and understanding. + +The copy and messaging is a core part of the experience of GitLab and the conversation with our users. Follow the below conventions throughout GitLab. >**Note:** We are currently inconsistent with this guidance. Images below are created to illustrate the point. As this guidance is refined, we will ensure that our experiences align. diff --git a/doc/development/ux_guide/img/animation-dropdown.gif b/doc/development/ux_guide/img/animation-dropdown.gif new file mode 100644 index 0000000000000000000000000000000000000000..c9b31d2616587295530c0dad7a266d65b8450839 GIT binary patch literal 22483 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 N{a@22{~!BH{S{uAA^ZRU literal 0 HcmV?d00001 diff --git a/doc/development/ux_guide/img/animation-hover.gif b/doc/development/ux_guide/img/animation-hover.gif new file mode 100644 index 0000000000000000000000000000000000000000..37ad9c76828a438f8fc6c6f903ddc38abd8d1b3b GIT binary patch literal 247388 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 UyK=&=oUkh=?8^T;Z!5L`1%E6jH2?qr literal 0 HcmV?d00001 diff --git a/doc/development/ux_guide/img/animation-quickupdate.gif b/doc/development/ux_guide/img/animation-quickupdate.gif new file mode 100644 index 0000000000000000000000000000000000000000..8db70bc3d24590a63a03bc6c1eb3b1aaa98d1f8d GIT binary patch literal 6441 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

;F|TQ;ximT20f z2-yzsN=*+N($z?D5Q3$+ITLsAbP(t3VW)h<^M)~co=y2Qaq|JJ|6GX?FX9~Ri&TcV z&7kP1ZfsSRzO7~G)v?l)>H2sktuQe?D~nmmJ(->yN$*08q5IQdk(isvMeY8WnaI+3 z-nvQy;pzmh^BEd(l~Q?(;i}TQVxtYKZI%RSSv%Dy+d)HCl~T!RdhtO!t%wr6D&Bk< zCU2`{htz}o>hB|{$tC4B4rwH>iv?r&cc^yPq_Ex87#Q?-1P*`gD$0@VvJ_7Ps&Qq4T{`-pab44n@*!bt ze!8qErz7W-g6lCe`BFbOXtC?;Tz?0V<6>&L^5(4?Sz|skZyw0WJRx!zuE8QVg>-OeqarSD-3>L@fM*pn~Uq+ zYe#MjS-zvaWMe8=3!7qGapbYzs8{>@kNn3k9O?##pzZs#TsA-5(8BRR%s(m94uI7VK+ed~%)HdupEBW6vh@Ua2vba>?4^&WOReZN zY2#eWirvY_>66wa9VmWFwd?MgHL@L}uC_+l_-pz|=`$i&@3lJZT%?jpYYhBdY}liUcxs1s<0H@mI9Y5JVY6ZoR0*i#K+Y`*9_331kXDavV@82l7fQiWEpk#)%bac-I+&-U%rVl4aMg(UCaF-itkN{cz zwDdG~ZK7HV%NZqUcMM78pj9_WGFB|1AI~JOFv*;5?BFQOo#m~^&ZmYiN|?v6qHQnf zs+j>P^>d2odEIWQNZsd>paR3ULsUaHr`XdbzPtyG{d&KP7=0uUo{%vMzuGqQn|zv> zh_+@ZqYV(8pV2+vn==GRA_nsW$(#bWsf}>0!r5=$BKfm zDg`BMXO7<<$?Ixxc#*_lIjsNfbui#Bhh_eCnW*+qm41XFh@aFr#lN zWy1<=B+=vMY0<|cxQ93yOl)lBQQX)Mv_UAom$X&gyh5i4axj6wjgb%nT4C5z^s|d@ z!b$L2T7R?D2oI?Tu*rMfpo+(l)ohaLRm5UW@sk3?VZr7D)-rg&XG=CsxZxe%Dg~Qu z?Z;dVn&(u_Tx4oFPK9djBP_&^^GCF|?!Rkef>mokJ_1szoe z6ok-F5#Cf>Y$9eF4HP+L*r=E~UUV)zQ{VXxy?*|(5?bzjAwbbg)ozvysDz|{Xd z?G=apiw2|N(aNsr(3zU(8mBsR$0(j21=_$00nxxJN|b~y3vn`*%mKyA2&4ej3ppL# zw^2oSp?fe>b1@o-0{}Y*@^FxpY8-^dHsR3_0yFKDjuf8}goqbn5=j3bq<_OyxC+TB z669Y77e0m@0(6h42WkWP*z6mJFs8OPapmu!u9%S<+KXdiUjOWU2?3LhK{{zJ3Te>%_K O|IGZS@cBEPm;ML2Jf1QD literal 0 HcmV?d00001 diff --git a/doc/development/ux_guide/index.md b/doc/development/ux_guide/index.md index 8aed11ebac3..8a849f239dc 100644 --- a/doc/development/ux_guide/index.md +++ b/doc/development/ux_guide/index.md @@ -12,7 +12,17 @@ These guiding principles set a solid foundation for our design system, and shoul --- ### [Basics](basics.md) -The basic ingredients of our experience establish our personality and feel. This section includes details about typography, color, and motion. +The basic ingredients of our experience establish our personality and feel. This section includes details about typography, iconography, and color. + +--- + +### [Animation](animation.md) +Guidance on the timing, curving and motion for GitLab. + +--- + +### [Copy](copy.md) +Conventions on text and messaging within labels, buttons, and other components. --- @@ -26,11 +36,6 @@ The GitLab experience is broken apart into several surfaces. Each of these surfa --- -### [Copy](copy.md) -Conventions on text and messaging within labels, buttons, and other components. - ---- - ### [Features](features.md) The previous building blocks are combined into complete features in the GitLab UX. Examples include our navigation, filters, search results, and empty states. From d6c2f5f5699495260aa2a99ea09a1764e9ec5e9c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 6 Dec 2016 15:46:48 +1100 Subject: [PATCH 035/140] Use `--extended-regexp` in lint-doc for compatibility with Darwin grep --- scripts/lint-doc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh index 7c4e8276902..62236ed539a 100755 --- a/scripts/lint-doc.sh +++ b/scripts/lint-doc.sh @@ -3,7 +3,7 @@ cd "$(dirname "$0")/.." # Use long options (e.g. --header instead of -H) for curl examples in documentation. -grep --perl-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ +grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/ if [ $? == 0 ] then echo '✖ ERROR: Short options should not be used in documentation!' >&2 From 4e249d5baea99a52915025fc2827d01ab2d77c18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 2 Dec 2016 13:54:57 +0100 Subject: [PATCH 036/140] Use :maximum instead of :within for length validators with a 0..N range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/ci/variable.rb | 4 ++-- app/models/concerns/issuable.rb | 2 +- app/models/environment.rb | 2 +- app/models/key.rb | 16 +++++++++---- app/models/namespace.rb | 14 +++++------ app/models/project.rb | 4 ++-- app/models/snippet.rb | 8 +++++-- .../lib/gitlab/github_import/importer_spec.rb | 2 +- spec/models/ci/variable_spec.rb | 7 ++++++ spec/models/concerns/issuable_spec.rb | 2 +- spec/models/environment_spec.rb | 4 ++-- spec/models/key_spec.rb | 8 +++++-- spec/models/namespace_spec.rb | 13 +++++++--- spec/models/project_spec.rb | 10 +++++--- spec/models/snippet_spec.rb | 24 +++++++++++++++++-- spec/models/user_spec.rb | 2 +- spec/requests/api/deploy_keys_spec.rb | 4 +--- spec/support/matchers/is_within.rb | 9 ------- 18 files changed, 89 insertions(+), 46 deletions(-) delete mode 100644 spec/support/matchers/is_within.rb diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 94d9e2b3208..2c8698d8b5d 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -4,10 +4,10 @@ module Ci belongs_to :project, foreign_key: :gl_project_id - validates_uniqueness_of :key, scope: :gl_project_id validates :key, presence: true, - length: { within: 0..255 }, + uniqueness: { scope: :gl_project_id }, + length: { maximum: 255 }, format: { with: /\A[a-zA-Z0-9_]+\z/, message: "can contain only letters, digits and '_'." } diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 69d8afc45da..0ea7b1b1098 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -41,7 +41,7 @@ module Issuable has_one :metrics validates :author, presence: true - validates :title, presence: true, length: { within: 0..255 } + validates :title, presence: true, length: { maximum: 255 } scope :authored, ->(user) { where(author_id: user) } scope :assigned_to, ->(u) { where(assignee_id: u.id)} diff --git a/app/models/environment.rb b/app/models/environment.rb index a7f4156fc2e..96700143ddd 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -9,7 +9,7 @@ class Environment < ActiveRecord::Base validates :name, presence: true, uniqueness: { scope: :project_id }, - length: { within: 0..255 }, + length: { maximum: 255 }, format: { with: Gitlab::Regex.environment_name_regex, message: Gitlab::Regex.environment_name_regex_message } diff --git a/app/models/key.rb b/app/models/key.rb index ff8dda2dc89..a5d25409730 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -8,10 +8,18 @@ class Key < ActiveRecord::Base before_validation :generate_fingerprint - validates :title, presence: true, length: { within: 0..255 } - validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ } - validates :key, format: { without: /\n|\r/, message: 'should be a single line' } - validates :fingerprint, uniqueness: true, presence: { message: 'cannot be generated' } + validates :title, + presence: true, + length: { maximum: 255 } + validates :key, + presence: true, + length: { maximum: 5000 }, + format: { with: /\A(ssh|ecdsa)-.*\Z/ } + validates :key, + format: { without: /\n|\r/, message: 'should be a single line' } + validates :fingerprint, + uniqueness: true, + presence: { message: 'cannot be generated' } delegate :name, :email, to: :user, prefix: true diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 891dffac648..7a545f752b6 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -12,17 +12,17 @@ class Namespace < ActiveRecord::Base validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, - length: { within: 0..255 }, - namespace_name: true, presence: true, - uniqueness: true + uniqueness: true, + length: { maximum: 255 }, + namespace_name: true - validates :description, length: { within: 0..255 } + validates :description, length: { maximum: 255 } validates :path, - length: { within: 1..255 }, - namespace: true, presence: true, - uniqueness: { case_sensitive: false } + uniqueness: { case_sensitive: false }, + length: { maximum: 255 }, + namespace: true delegate :name, to: :owner, allow_nil: true, prefix: true diff --git a/app/models/project.rb b/app/models/project.rb index f01cb613b85..e783e455a49 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -172,13 +172,13 @@ class Project < ActiveRecord::Base validates :description, length: { maximum: 2000 }, allow_blank: true validates :name, presence: true, - length: { within: 0..255 }, + length: { maximum: 255 }, format: { with: Gitlab::Regex.project_name_regex, message: Gitlab::Regex.project_name_regex_message } validates :path, presence: true, project_path: true, - length: { within: 0..255 }, + length: { maximum: 255 }, format: { with: Gitlab::Regex.project_path_regex, message: Gitlab::Regex.project_path_regex_message } validates :namespace, presence: true diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 8ff4e7ae718..99493e561ab 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -27,9 +27,9 @@ class Snippet < ActiveRecord::Base delegate :name, :email, to: :author, prefix: true, allow_nil: true validates :author, presence: true - validates :title, presence: true, length: { within: 0..255 } + validates :title, presence: true, length: { maximum: 255 } validates :file_name, - length: { within: 0..255 }, + length: { maximum: 255 }, format: { with: Gitlab::Regex.file_name_regex, message: Gitlab::Regex.file_name_regex_message } @@ -94,6 +94,10 @@ class Snippet < ActiveRecord::Base 0 end + def file_name + super.to_s + end + # alias for compatibility with blobs and highlighting def path file_name diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb index 000b9aa6f83..9e027839f59 100644 --- a/spec/lib/gitlab/github_import/importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer_spec.rb @@ -155,7 +155,7 @@ describe Gitlab::GithubImport::Importer, lib: true do message: 'The remote data could not be fully imported.', errors: [ { type: :label, url: "https://api.github.com/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" }, - { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank, Title is too short (minimum is 0 characters)" }, + { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" }, { type: :wiki, errors: "Gitlab::Shell::Error" }, { type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" } ] diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 4e7833c3162..bee9f714849 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -5,6 +5,13 @@ describe Ci::Variable, models: true do let(:secret_value) { 'secret' } + it { is_expected.to validate_presence_of(:key) } + it { is_expected.to validate_uniqueness_of(:key).scoped_to(:gl_project_id) } + it { is_expected.to validate_length_of(:key).is_at_most(255) } + it { is_expected.to allow_value('foo').for(:key) } + it { is_expected.not_to allow_value('foo bar').for(:key) } + it { is_expected.not_to allow_value('foo/bar').for(:key) } + before :each do subject.value = secret_value end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 6f84bffe046..4fa06a8c60a 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -35,7 +35,7 @@ describe Issue, "Issuable" do it { is_expected.to validate_presence_of(:iid) } it { is_expected.to validate_presence_of(:author) } it { is_expected.to validate_presence_of(:title) } - it { is_expected.to validate_length_of(:title).is_at_least(0).is_at_most(255) } + it { is_expected.to validate_length_of(:title).is_at_most(255) } end describe "Scope" do diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index d06665197db..c8170164898 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -13,9 +13,9 @@ describe Environment, models: true do it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_uniqueness_of(:name).scoped_to(:project_id) } - it { is_expected.to validate_length_of(:name).is_within(0..255) } + it { is_expected.to validate_length_of(:name).is_at_most(255) } - it { is_expected.to validate_length_of(:external_url).is_within(0..255) } + it { is_expected.to validate_length_of(:external_url).is_at_most(255) } # To circumvent a not null violation of the name column: # https://github.com/thoughtbot/shoulda-matchers/issues/336 diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 90731f55470..2a33d819138 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -7,9 +7,13 @@ describe Key, models: true do describe "Validation" do it { is_expected.to validate_presence_of(:title) } + it { is_expected.to validate_length_of(:title).is_at_most(255) } + it { is_expected.to validate_presence_of(:key) } - it { is_expected.to validate_length_of(:title).is_within(0..255) } - it { is_expected.to validate_length_of(:key).is_within(0..5000) } + it { is_expected.to validate_length_of(:key).is_at_most(5000) } + it { is_expected.to allow_value('ssh-foo').for(:key) } + it { is_expected.to allow_value('ecdsa-foo').for(:key) } + it { is_expected.not_to allow_value('foo-bar').for(:key) } end describe "Methods" do diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 431b3e4435f..ba0ed4a3603 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -4,11 +4,18 @@ describe Namespace, models: true do let!(:namespace) { create(:namespace) } it { is_expected.to have_many :projects } - it { is_expected.to validate_presence_of :name } + + it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_uniqueness_of(:name) } - it { is_expected.to validate_presence_of :path } + it { is_expected.to validate_length_of(:name).is_at_most(255) } + + it { is_expected.to validate_length_of(:description).is_at_most(255) } + + it { is_expected.to validate_presence_of(:path) } it { is_expected.to validate_uniqueness_of(:path) } - it { is_expected.to validate_presence_of :owner } + it { is_expected.to validate_length_of(:path).is_at_most(255) } + + it { is_expected.to validate_presence_of(:owner) } describe "Mass assignment" do end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8abcce42ce0..f7c8c97fdee 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -131,14 +131,18 @@ describe Project, models: true do it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) } - it { is_expected.to validate_length_of(:name).is_within(0..255) } + it { is_expected.to validate_length_of(:name).is_at_most(255) } it { is_expected.to validate_presence_of(:path) } it { is_expected.to validate_uniqueness_of(:path).scoped_to(:namespace_id) } - it { is_expected.to validate_length_of(:path).is_within(0..255) } - it { is_expected.to validate_length_of(:description).is_within(0..2000) } + it { is_expected.to validate_length_of(:path).is_at_most(255) } + + it { is_expected.to validate_length_of(:description).is_at_most(2000) } + it { is_expected.to validate_presence_of(:creator) } + it { is_expected.to validate_presence_of(:namespace) } + it { is_expected.to validate_presence_of(:repository_storage) } it 'does not allow new projects beyond user limits' do diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index f62f6bacbaa..279dc30c357 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -23,9 +23,9 @@ describe Snippet, models: true do it { is_expected.to validate_presence_of(:author) } it { is_expected.to validate_presence_of(:title) } - it { is_expected.to validate_length_of(:title).is_within(0..255) } + it { is_expected.to validate_length_of(:title).is_at_most(255) } - it { is_expected.to validate_length_of(:file_name).is_within(0..255) } + it { is_expected.to validate_length_of(:file_name).is_at_most(255) } it { is_expected.to validate_presence_of(:content) } @@ -46,6 +46,26 @@ describe Snippet, models: true do end end + describe '#file_name' do + let(:project) { create(:empty_project) } + + context 'file_name is nil' do + let(:snippet) { create(:snippet, project: project, file_name: nil) } + + it 'returns an empty string' do + expect(snippet.file_name).to eq '' + end + end + + context 'file_name is not nil' do + let(:snippet) { create(:snippet, project: project, file_name: 'foo.txt') } + + it 'returns the file_name' do + expect(snippet.file_name).to eq 'foo.txt' + end + end + end + describe "#content_html_invalidated?" do let(:snippet) { create(:snippet, content: "md", content_html: "html", file_name: "foo.md") } it "invalidates the HTML cache of content when the filename changes" do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 14c891994d0..95fe2dc65d9 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -79,7 +79,7 @@ describe User, models: true do it { is_expected.to allow_value(0).for(:projects_limit) } it { is_expected.not_to allow_value(-1).for(:projects_limit) } - it { is_expected.to validate_length_of(:bio).is_within(0..255) } + it { is_expected.to validate_length_of(:bio).is_at_most(255) } it_behaves_like 'an object with email-formated attributes', :email do subject { build(:user) } diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb index 5fa7299044e..aabab8e6ae6 100644 --- a/spec/requests/api/deploy_keys_spec.rb +++ b/spec/requests/api/deploy_keys_spec.rb @@ -75,7 +75,6 @@ describe API::DeployKeys, api: true do expect(response).to have_http_status(400) expect(json_response['message']['key']).to eq([ 'can\'t be blank', - 'is too short (minimum is 0 characters)', 'is invalid' ]) end @@ -85,8 +84,7 @@ describe API::DeployKeys, api: true do expect(response).to have_http_status(400) expect(json_response['message']['title']).to eq([ - 'can\'t be blank', - 'is too short (minimum is 0 characters)' + 'can\'t be blank' ]) end diff --git a/spec/support/matchers/is_within.rb b/spec/support/matchers/is_within.rb deleted file mode 100644 index 0c35fc7e899..00000000000 --- a/spec/support/matchers/is_within.rb +++ /dev/null @@ -1,9 +0,0 @@ -# Extend shoulda-matchers -module Shoulda::Matchers::ActiveModel - class ValidateLengthOfMatcher - # Shortcut for is_at_least and is_at_most - def is_within(range) - is_at_least(range.min) && is_at_most(range.max) - end - end -end From b1d6dd4c7479d01ac8b3392c55fb3c24dc18c4ae Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 11:00:52 +0100 Subject: [PATCH 037/140] Restore legacy statuses support in ci status helpers --- app/helpers/ci_status_helper.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index ff507765255..da0ebc0040a 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -23,6 +23,8 @@ module CiStatusHelper case status when 'success' 'passed' + when 'success_with_warnings' + 'passed with warnings' else status end @@ -41,6 +43,8 @@ module CiStatusHelper case status when 'success' 'icon_status_success' + when 'success_with_warnings' + 'icon_status_warning' when 'failed' 'icon_status_failed' when 'pending' From 40a118072d82dab27588b7907b26642e9212f969 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 11:02:12 +0100 Subject: [PATCH 038/140] Remove unsued variable from merge request widget --- app/views/projects/merge_requests/widget/_heading.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 6d9b91ad0e7..9ab7971b56c 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -8,14 +8,13 @@ = link_to "##{@pipeline.id}", namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'pipeline' = ci_label_for_status(status) for - - commit = @merge_request.diff_head_commit = succeed "." do = link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace" %span.ci-coverage - elsif @merge_request.has_ci? - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX - - # Remove in later versions when services like Jenkins will set CI status via Commit status API + - # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API .mr-widget-heading - %w[success skipped canceled failed running pending].each do |status| .ci_widget{class: "ci-#{status}", style: "display:none"} From c8b2aa8de527099cf902d5fbfd058f9dee772f24 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 11:05:01 +0100 Subject: [PATCH 039/140] Simplify ci status helper with detailed status --- app/helpers/ci_status_helper.rb | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index da0ebc0040a..29ab1404f41 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -36,30 +36,30 @@ module CiStatusHelper end def ci_icon_for_status(status) + if detailed_status?(status) + return custom_icon(status.icon) + end + icon_name = - if detailed_status?(status) - status.icon + case status + when 'success' + 'icon_status_success' + when 'success_with_warnings' + 'icon_status_warning' + when 'failed' + 'icon_status_failed' + when 'pending' + 'icon_status_pending' + when 'running' + 'icon_status_running' + when 'play' + 'icon_play' + when 'created' + 'icon_status_created' + when 'skipped' + 'icon_status_skipped' else - case status - when 'success' - 'icon_status_success' - when 'success_with_warnings' - 'icon_status_warning' - when 'failed' - 'icon_status_failed' - when 'pending' - 'icon_status_pending' - when 'running' - 'icon_status_running' - when 'play' - 'icon_play' - when 'created' - 'icon_status_created' - when 'skipped' - 'icon_status_skipped' - else - 'icon_status_canceled' - end + 'icon_status_canceled' end custom_icon(icon_name) From bdc13c3142507650e9170cc7b9b63232bb1cfdad Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 11:22:31 +0100 Subject: [PATCH 040/140] Untangle status label and text in ci status helper --- app/helpers/ci_status_helper.rb | 12 ++++++++++-- lib/gitlab/ci/status/core.rb | 11 +++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 29ab1404f41..8e19752a8a1 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -5,8 +5,8 @@ module CiStatusHelper end def ci_status_with_icon(status, target = nil) - content = ci_icon_for_status(status) + ci_label_for_status(status) - klass = "ci-status ci-#{status}" # TODO, add support for detailed status + content = ci_icon_for_status(status) + ci_text_for_status(status) + klass = "ci-status ci-#{status}" if target link_to content, target, class: klass @@ -15,6 +15,14 @@ module CiStatusHelper end end + def ci_text_for_status(status) + if detailed_status?(status) + status.text + else + status + end + end + def ci_label_for_status(status) if detailed_status?(status) return status.label diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index fbfe257eeca..10e04d5be03 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -22,6 +22,17 @@ module Gitlab "#{@subject.class.name.demodulize}: #{label}" end + # Deprecation warning: this method is here because we need to maintain + # backwards compatibility with legacy statuses. We often do something + # like "ci-status ci-status-#{status}" to set CSS class. + # + # `to_s` method should be renamed to `group` at some point, after + # phasing legacy satuses out. + # + def to_s + self.class.name.demodulize.downcase + end + def has_details? raise NotImplementedError end From 79e132fad4a3428c1d65e877be65d094a559649c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 11:31:41 +0100 Subject: [PATCH 041/140] Add status label information to pipeline header --- app/views/projects/pipelines/_info.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index 229bdfb0e8d..bbfaa6391a1 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -2,6 +2,7 @@ .header-main-content = ci_status_with_icon(@pipeline.detailed_status) %strong Pipeline ##{@commit.pipelines.last.id} + #{ci_label_for_status(@pipeline.detailed_status)}, triggered #{time_ago_with_tooltip(@commit.authored_date)} by = author_avatar(@commit, size: 24) = commit_author_link(@commit) From e94f378b619849c46bf6b736ea9c61c19956b57f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 12:16:52 +0100 Subject: [PATCH 042/140] Improve support for icons in new detailed statuses --- app/views/projects/pipelines/_info.html.haml | 1 - lib/gitlab/ci/status/core.rb | 2 +- lib/gitlab/ci/status/pipeline/success_with_warnings.rb | 4 ++++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index bbfaa6391a1..229bdfb0e8d 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -2,7 +2,6 @@ .header-main-content = ci_status_with_icon(@pipeline.detailed_status) %strong Pipeline ##{@commit.pipelines.last.id} - #{ci_label_for_status(@pipeline.detailed_status)}, triggered #{time_ago_with_tooltip(@commit.authored_date)} by = author_avatar(@commit, size: 24) = commit_author_link(@commit) diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb index 10e04d5be03..ce4108fdcf2 100644 --- a/lib/gitlab/ci/status/core.rb +++ b/lib/gitlab/ci/status/core.rb @@ -30,7 +30,7 @@ module Gitlab # phasing legacy satuses out. # def to_s - self.class.name.demodulize.downcase + self.class.name.demodulize.downcase.underscore end def has_details? diff --git a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb index 97dfba81ff5..4b040d60df8 100644 --- a/lib/gitlab/ci/status/pipeline/success_with_warnings.rb +++ b/lib/gitlab/ci/status/pipeline/success_with_warnings.rb @@ -17,6 +17,10 @@ module Gitlab 'icon_status_warning' end + def to_s + 'success_with_warnings' + end + def self.matches?(pipeline) pipeline.success? && pipeline.has_warnings? end From 67cd3b36382ed2fafc29a620112a9de96f3899f6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 12:24:27 +0100 Subject: [PATCH 043/140] Use status text when redering pipelines list --- app/views/projects/ci/pipelines/_pipeline.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 00a30870dad..a037c7ab37c 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -7,7 +7,7 @@ %td.commit-link = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{status}" do = ci_icon_for_status(detailed_status) - = ci_label_for_status(detailed_status) + = ci_text_for_status(detailed_status) %td = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do From 157f4fe17fdd0044229c6830bb85fd26759a69cc Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 12:28:07 +0100 Subject: [PATCH 044/140] Use detailed status as CSS class in pipelines list --- app/views/projects/ci/pipelines/_pipeline.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index a037c7ab37c..0f08f4e8592 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -5,7 +5,7 @@ %tr.commit %td.commit-link - = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{status}" do + = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "ci-status ci-#{detailed_status}" do = ci_icon_for_status(detailed_status) = ci_text_for_status(detailed_status) From 7b99b18643b443109d790940ffdf40c301e2c85d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 6 Dec 2016 12:33:53 +0100 Subject: [PATCH 045/140] Add text example for pipeline status without action --- spec/lib/gitlab/ci/status/pipeline/factory_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index 543dae0640d..d6243940f2e 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -23,7 +23,9 @@ describe Gitlab::Ci::Status::Pipeline::Factory do it 'extends core status with common pipeline methods' do expect(status).to have_details - expect(status.details_path).to include "pipelines/#{pipeline.id}" + expect(status).not_to have_action + expect(status.details_path) + .to include "pipelines/#{pipeline.id}" end end end From 5eb12bd75fff65bb2ea8677cc317877e45d3d6f8 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 6 Dec 2016 13:22:45 +0100 Subject: [PATCH 046/140] Remove caching of Repository#has_visible_content? This method already uses the cached method Repository#branch_count so there's no point in also caching has_visible_content?. Fixes gitlab-org/gitlab-ce#25278 --- app/models/repository.rb | 14 +-------- .../remove-has-visible-content-caching.yml | 4 +++ spec/models/repository_spec.rb | 31 ++----------------- 3 files changed, 7 insertions(+), 42 deletions(-) create mode 100644 changelogs/unreleased/remove-has-visible-content-caching.yml diff --git a/app/models/repository.rb b/app/models/repository.rb index e2e7d08abac..3c4b0212af7 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -85,11 +85,7 @@ class Repository # This method return true if repository contains some content visible in project page. # def has_visible_content? - return @has_visible_content unless @has_visible_content.nil? - - @has_visible_content = cache.fetch(:has_visible_content?) do - branch_count > 0 - end + branch_count > 0 end def commit(ref = 'HEAD') @@ -374,12 +370,6 @@ class Repository return unless empty? expire_method_caches(%i(empty?)) - expire_has_visible_content_cache - end - - def expire_has_visible_content_cache - cache.expire(:has_visible_content?) - @has_visible_content = nil end def lookup_cache @@ -467,7 +457,6 @@ class Repository # Runs code after a new branch has been created. def after_create_branch expire_branches_cache - expire_has_visible_content_cache repository_event(:push_branch) end @@ -481,7 +470,6 @@ class Repository # Runs code after an existing branch has been removed. def after_remove_branch - expire_has_visible_content_cache expire_branches_cache end diff --git a/changelogs/unreleased/remove-has-visible-content-caching.yml b/changelogs/unreleased/remove-has-visible-content-caching.yml new file mode 100644 index 00000000000..e2940c60443 --- /dev/null +++ b/changelogs/unreleased/remove-has-visible-content-caching.yml @@ -0,0 +1,4 @@ +--- +title: Remove visible content caching +merge_request: +author: diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index b797d19161d..d9b0e63eeb6 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -768,7 +768,6 @@ describe Repository, models: true do expect(repository).not_to receive(:expire_root_ref_cache) expect(repository).not_to receive(:expire_emptiness_caches) expect(repository).to receive(:expire_branches_cache) - expect(repository).to receive(:expire_has_visible_content_cache) repository.update_branch_with_hooks(user, 'new-feature') { new_rev } end @@ -786,7 +785,6 @@ describe Repository, models: true do expect(empty_repository).to receive(:expire_root_ref_cache) expect(empty_repository).to receive(:expire_emptiness_caches) expect(empty_repository).to receive(:expire_branches_cache) - expect(empty_repository).to receive(:expire_has_visible_content_cache) empty_repository.commit_file(user, 'CHANGELOG', 'Changelog!', 'Updates file content', 'master', false) @@ -829,15 +827,6 @@ describe Repository, models: true do expect(subject).to eq(true) end - - it 'caches the output' do - expect(repository).to receive(:branch_count). - once. - and_return(3) - - repository.has_visible_content? - repository.has_visible_content? - end end end @@ -918,20 +907,6 @@ describe Repository, models: true do end end - describe '#expire_has_visible_content_cache' do - it 'expires the visible content cache' do - repository.has_visible_content? - - expect(repository).to receive(:branch_count). - once. - and_return(0) - - repository.expire_has_visible_content_cache - - expect(repository.has_visible_content?).to eq(false) - end - end - describe '#expire_branch_cache' do # This method is private but we need it for testing purposes. Sadly there's # no other proper way of testing caching operations. @@ -967,7 +942,6 @@ describe Repository, models: true do allow(repository).to receive(:empty?).and_return(true) expect(cache).to receive(:expire).with(:empty?) - expect(repository).to receive(:expire_has_visible_content_cache) repository.expire_emptiness_caches end @@ -976,7 +950,6 @@ describe Repository, models: true do allow(repository).to receive(:empty?).and_return(false) expect(cache).not_to receive(:expire).with(:empty?) - expect(repository).not_to receive(:expire_has_visible_content_cache) repository.expire_emptiness_caches end @@ -1204,7 +1177,7 @@ describe Repository, models: true do describe '#after_create_branch' do it 'flushes the visible content cache' do - expect(repository).to receive(:expire_has_visible_content_cache) + expect(repository).to receive(:expire_branches_cache) repository.after_create_branch end @@ -1212,7 +1185,7 @@ describe Repository, models: true do describe '#after_remove_branch' do it 'flushes the visible content cache' do - expect(repository).to receive(:expire_has_visible_content_cache) + expect(repository).to receive(:expire_branches_cache) repository.after_remove_branch end From 4e06818d330b4f15f334561500d506f0d24b9dfe Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Nov 2016 15:32:23 +0100 Subject: [PATCH 047/140] Support pipelines API Pass `updated_at` to get only incremental changes since last update --- app/models/ci/pipeline.rb | 29 +++++++++++++++---- app/models/commit_status.rb | 11 +------ .../projects/ci/pipelines/_pipeline.html.haml | 7 +++-- app/views/projects/commit/_pipeline.html.haml | 2 +- .../projects/commit/_pipelines_list.haml | 2 +- app/views/projects/pipelines/index.html.haml | 2 +- 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index caf6908505e..859c6b483f4 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -98,19 +98,38 @@ module Ci sha[0...8] end - def self.stages - # We use pluck here due to problems with MySQL which doesn't allow LIMIT/OFFSET in queries - CommitStatus.where(pipeline: pluck(:id)).stages - end - def self.total_duration where.not(duration: nil).sum(:duration) end + def stages + statuses.group('stage').select(:stage) + .order('max(stage_idx)') + end + + def stages_with_statuses + status_sql = statuses.latest.where('stage=sg.stage').status_sql + + stages_with_statuses = CommitStatus.from(self.stages, :sg). + pluck('sg.stage', status_sql) + + stages_with_statuses.map do |stage| + OpenStruct.new( + name: stage.first, + status: stage.last, + pipeline: self + ) + end + end + def stages_with_latest_statuses statuses.latest.includes(project: :namespace).order(:stage_idx).group_by(&:stage) end + def artifacts + builds.latest.with_artifacts_not_expired + end + def project_id project.id end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index c345bf293c9..d9021a38ce3 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -119,16 +119,7 @@ class CommitStatus < ActiveRecord::Base def self.stages # We group by stage name, but order stages by theirs' index - unscoped.from(all, :sg).group('stage').order('max(stage_idx)', 'stage').pluck('sg.stage') - end - - def self.stages_status - # We execute subquery for each stage to calculate a stage status - statuses = unscoped.from(all, :sg).group('stage').pluck('sg.stage', all.where('stage=sg.stage').status_sql) - statuses.inject({}) do |h, k| - h[k.first] = k.last - h - end + unscoped.from(all, :sg).group('stage').order('max(stage_idx)', 'stage').select('sg.stage') end def failed_but_allowed? diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 0f08f4e8592..d42df00b47f 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -45,9 +45,10 @@ - stages_status = pipeline.statuses.latest.stages_status %td.stage-cell - - stages.each do |stage| - - status = stages_status[stage] - - tooltip = "#{stage.titleize}: #{status || 'not found'}" + - pipeline.statuses.latest.stages_status.each do |stage| + - name = stage.first + - status = stage.last + - tooltip = "#{name.titleize}: #{status || 'not found'}" - if status .stage-container = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index 1174158eb65..cd9ed46d2c1 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -62,5 +62,5 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - - pipeline.statuses.relevant.stages.each do |stage| + - pipeline.stages.each do |stage| = render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.relevant.where(stage: stage) diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index 2dc91a9b762..7f42fde0fea 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -12,4 +12,4 @@ %th Stages %th %th - = render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, show_commit: false + = render pipelines, commit_sha: true, stage: true, allow_retry: true, show_commit: false diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 4bc49072f35..8340ce516db 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -51,6 +51,6 @@ %th Stages %th %th.hidden-xs - = render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages + = render @pipelines, commit_sha: true, stage: true, allow_retry: true = paginate @pipelines, theme: 'gitlab' From fa1105b10b4f5dbce46bd72eb6374fe7f8d51f56 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 15 Nov 2016 15:20:37 +0100 Subject: [PATCH 048/140] Fix broken pipeline rendering [ci skip] --- app/models/ci/pipeline.rb | 10 ++++++---- app/views/projects/ci/pipelines/_pipeline.html.haml | 13 +++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 859c6b483f4..05303007625 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -21,8 +21,6 @@ module Ci after_create :keep_around_commits, unless: :importing? - delegate :stages, to: :statuses - state_machine :status, initial: :created do event :enqueue do transition created: :pending @@ -102,15 +100,19 @@ module Ci where.not(duration: nil).sum(:duration) end - def stages + def stages_query statuses.group('stage').select(:stage) .order('max(stage_idx)') end + def stages + self.stages_query.pluck(:stage) + end + def stages_with_statuses status_sql = statuses.latest.where('stage=sg.stage').status_sql - stages_with_statuses = CommitStatus.from(self.stages, :sg). + stages_with_statuses = CommitStatus.from(self.stages_query, :sg). pluck('sg.stage', status_sql) stages_with_statuses.map do |stage| diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index d42df00b47f..e4a963a278c 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -43,16 +43,13 @@ - else Cant find HEAD commit for this branch - - stages_status = pipeline.statuses.latest.stages_status %td.stage-cell - - pipeline.statuses.latest.stages_status.each do |stage| - - name = stage.first - - status = stage.last - - tooltip = "#{name.titleize}: #{status || 'not found'}" - - if status + - pipeline.stages_with_statuses.each do |stage| + - if stage.status + - tooltip = "#{stage.name.titleize}: #{stage.status || 'not found'}" .stage-container - = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do - = ci_icon_for_status(status) + = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage.name), class: "has-tooltip ci-status-icon-#{stage.status}", title: tooltip do + = ci_icon_for_status(stage.status) %td - if pipeline.duration From d865aedafc2282f898b4bd2fdfd3660c47203c37 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 14:17:42 +0100 Subject: [PATCH 049/140] Introduce `Ci::Stage`, right now this is artificial object that is build dynamically. --- app/models/ci/pipeline.rb | 24 ++++++------------- app/models/ci/stage.rb | 23 ++++++++++++++++++ app/models/commit_status.rb | 9 ++----- .../notify/pipeline_success_email.html.haml | 2 +- .../notify/pipeline_success_email.text.erb | 2 +- app/views/projects/builds/_sidebar.html.haml | 2 +- app/views/projects/commit/_ci_stage.html.haml | 15 ------------ app/views/projects/commit/_pipeline.html.haml | 14 +++++------ .../projects/pipelines/_with_tabs.html.haml | 12 ++++------ app/views/projects/pipelines/index.html.haml | 1 - app/views/projects/stage/_stage.html.haml | 14 +++++++++++ lib/gitlab/data_builder/pipeline.rb | 2 +- 12 files changed, 61 insertions(+), 59 deletions(-) create mode 100644 app/models/ci/stage.rb delete mode 100644 app/views/projects/commit/_ci_stage.html.haml create mode 100644 app/views/projects/stage/_stage.html.haml diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 05303007625..b6b9a90a589 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -100,34 +100,24 @@ module Ci where.not(duration: nil).sum(:duration) end - def stages_query - statuses.group('stage').select(:stage) - .order('max(stage_idx)') + def stages_count + statuses.select(:stage).distinct.count end def stages - self.stages_query.pluck(:stage) - end - - def stages_with_statuses status_sql = statuses.latest.where('stage=sg.stage').status_sql - stages_with_statuses = CommitStatus.from(self.stages_query, :sg). + stages_query = statuses.group('stage').select(:stage) + .order('max(stage_idx)') + + stages_with_statuses = CommitStatus.from(stages_query, :sg). pluck('sg.stage', status_sql) stages_with_statuses.map do |stage| - OpenStruct.new( - name: stage.first, - status: stage.last, - pipeline: self - ) + Ci::Stage.new(self, stage.first, status: stage.last) end end - def stages_with_latest_statuses - statuses.latest.includes(project: :namespace).order(:stage_idx).group_by(&:stage) - end - def artifacts builds.latest.with_artifacts_not_expired end diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb new file mode 100644 index 00000000000..f1cac09c4e9 --- /dev/null +++ b/app/models/ci/stage.rb @@ -0,0 +1,23 @@ +module Ci + class Stage < ActiveRecord::Base + include ActiveModel::Model + + attr_reader :pipeline, :name + + def initialize(pipeline, name: name, status: status = nil) + @pipeline, @name, @status = pipeline, name, status + end + + def status + @status ||= statuses.latest.status + end + + def statuses + pipeline.statuses.where(stage: stage) + end + + def builds + pipeline.builds.where(stage: stage) + end + end +end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index d9021a38ce3..2a537dc2a13 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -41,8 +41,8 @@ class CommitStatus < ActiveRecord::Base where("#{quoted_when} <> ? OR status <> ?", 'on_failure', 'skipped') end - scope :latest_ci_stages, -> { latest.ordered.includes(project: :namespace) } - scope :retried_ci_stages, -> { retried.ordered.includes(project: :namespace) } + scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) } + scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) } state_machine :status do event :enqueue do @@ -117,11 +117,6 @@ class CommitStatus < ActiveRecord::Base name.gsub(/\d+[\s:\/\\]+\d+\s*/, '').strip end - def self.stages - # We group by stage name, but order stages by theirs' index - unscoped.from(all, :sg).group('stage').order('max(stage_idx)', 'stage').select('sg.stage') - end - def failed_but_allowed? allow_failure? && (failed? || canceled?) end diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml index 697c8d19257..56c1949ab2b 100644 --- a/app/views/notify/pipeline_success_email.html.haml +++ b/app/views/notify/pipeline_success_email.html.haml @@ -133,7 +133,7 @@ %tr.success-message %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 5px;text-align:center;"} - build_count = @pipeline.statuses.latest.size - - stage_count = @pipeline.stages.size + - stage_count = @pipeline.stages_count Pipeline %a{href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"} = "\##{@pipeline.id}" diff --git a/app/views/notify/pipeline_success_email.text.erb b/app/views/notify/pipeline_success_email.text.erb index ae22d474f2c..40e5e306426 100644 --- a/app/views/notify/pipeline_success_email.text.erb +++ b/app/views/notify/pipeline_success_email.text.erb @@ -16,7 +16,7 @@ Commit Author: <%= commit.author_name %> <% end -%> <% build_count = @pipeline.statuses.latest.size -%> -<% stage_count = @pipeline.stages.size -%> +<% stage_count = @pipeline.stages_count -%> Pipeline #<%= @pipeline.id %> ( <%= pipeline_url(@pipeline) %> ) successfully completed <%= build_count %> <%= 'build'.pluralize(build_count) %> in <%= stage_count %> <%= 'stage'.pluralize(stage_count) %>. You're receiving this email because of your account on <%= Gitlab.config.gitlab.host %>. diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index d5004f6a066..a45612fb28b 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -111,7 +111,7 @@ %span.label.label-primary = tag - - if @build.pipeline.stages.many? + - if @build.pipeline.stages_count.many? .dropdown.build-dropdown .title Stage %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} diff --git a/app/views/projects/commit/_ci_stage.html.haml b/app/views/projects/commit/_ci_stage.html.haml deleted file mode 100644 index 3a3d750439f..00000000000 --- a/app/views/projects/commit/_ci_stage.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -%tr - %th{colspan: 10} - %strong - %a{name: stage} - - status = statuses.latest.status - %span{class: "ci-status-link ci-status-icon-#{status}"} - = ci_icon_for_status(status) - - if stage -   - = stage.titleize - = render statuses.latest_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true - = render statuses.retried_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true -%tr - %td{colspan: 10} -   diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index cd9ed46d2c1..2cd40bb1106 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -27,16 +27,15 @@ .row-content-block.build-content.middle-block.pipeline-graph.hidden .pipeline-visualization %ul.stage-column-list - - stages = pipeline.stages_with_latest_statuses - - stages.each do |stage, statuses| + - pipeline.stages.each do |stage| %li.stage-column .stage-name - %a{name: stage} - - if stage - = stage.titleize + %a{name: stage.name} + - if stage.name + = stage.name.titleize .builds-container %ul - = render "projects/commit/pipeline_stage", statuses: statuses + = render "projects/commit/pipeline_stage", statuses: stage.statuses - if pipeline.yaml_errors.present? @@ -62,5 +61,4 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - - pipeline.stages.each do |stage| - = render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.relevant.where(stage: stage) + = render pipeline.stages diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 3464e155a1b..57e793d2e59 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -15,13 +15,12 @@ .build-content.middle-block.pipeline-graph .pipeline-visualization %ul.stage-column-list - - stages = pipeline.stages_with_latest_statuses - - stages.each do |stage, statuses| + - pipeline.stages.each do |stage| %li.stage-column .stage-name - %a{name: stage} - - if stage - = stage.titleize + %a{name: stage.name} + - if stage.name + = stage.name.titleize .builds-container %ul = render "projects/commit/pipeline_stage", statuses: statuses @@ -50,5 +49,4 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - - pipeline.statuses.relevant.stages.each do |stage| - = render 'projects/commit/ci_stage', stage: stage, statuses: pipeline.statuses.relevant.where(stage: stage) + = render pipeline.stages diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 8340ce516db..e1e787dbde4 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -37,7 +37,6 @@ %span CI Lint %div.content-list.pipelines - - stages = @pipelines.stages - if @pipelines.blank? %div .nothing-here-block No pipelines to show diff --git a/app/views/projects/stage/_stage.html.haml b/app/views/projects/stage/_stage.html.haml new file mode 100644 index 00000000000..717075620d9 --- /dev/null +++ b/app/views/projects/stage/_stage.html.haml @@ -0,0 +1,14 @@ +%tr + %th{colspan: 10} + %strong + %a{name: subject.name} + %span{class: "ci-status-link ci-status-icon-#{subject.status}"} + = ci_icon_for_status(subject.status) + - if subject.name +   + = subject.name.titleize + = render subject.statuses.latest_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true + = render subject.statuses.retried_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true +%tr + %td{colspan: 10} +   diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb index 06a783ebc1c..e0284d55da8 100644 --- a/lib/gitlab/data_builder/pipeline.rb +++ b/lib/gitlab/data_builder/pipeline.rb @@ -22,7 +22,7 @@ module Gitlab sha: pipeline.sha, before_sha: pipeline.before_sha, status: pipeline.status, - stages: pipeline.stages, + stages: pipeline.stages.map(&:name), created_at: pipeline.created_at, finished_at: pipeline.finished_at, duration: pipeline.duration From 2f972ad47be9a0c1f9b6acefc6638751145b7078 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 14:22:41 +0100 Subject: [PATCH 050/140] Preserve stage values and use StaticModel --- app/models/ci/stage.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index f1cac09c4e9..8f7727aebaa 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -1,23 +1,29 @@ module Ci - class Stage < ActiveRecord::Base - include ActiveModel::Model + # Currently this is artificial object, constructed dynamically + # We should migrate this object to actual database record in the future + class Stage + include StaticModel attr_reader :pipeline, :name - def initialize(pipeline, name: name, status: status = nil) + def initialize(pipeline, name: name, status: nil) @pipeline, @name, @status = pipeline, name, status end + def to_param + name + end + def status @status ||= statuses.latest.status end def statuses - pipeline.statuses.where(stage: stage) + @statuses ||= pipeline.statuses.where(stage: stage) end def builds - pipeline.builds.where(stage: stage) + @builds ||= pipeline.builds.where(stage: stage) end end end From d47aef58cd88fb813390c904bd24525e24d41483 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 14:28:02 +0100 Subject: [PATCH 051/140] Add Ci::Status::Stage --- app/models/ci/stage.rb | 2 ++ lib/gitlab/ci/status/stage/common.rb | 24 +++++++++++++++++ lib/gitlab/ci/status/stage/factory.rb | 39 +++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 lib/gitlab/ci/status/stage/common.rb create mode 100644 lib/gitlab/ci/status/stage/factory.rb diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 8f7727aebaa..2e7f15a16d7 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -6,6 +6,8 @@ module Ci attr_reader :pipeline, :name + delegate :project, to: :pipeline + def initialize(pipeline, name: name, status: nil) @pipeline, @name, @status = pipeline, name, status end diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb new file mode 100644 index 00000000000..a1513024c6c --- /dev/null +++ b/lib/gitlab/ci/status/stage/common.rb @@ -0,0 +1,24 @@ +module Gitlab + module Ci + module Status + module Stage + module Common + def has_details? + true + end + + def details_path + namespace_project_pipeline_path(@subject.project.namespace, + @subject.project, + @subject, + anchor: subject.name) + end + + def has_action? + false + end + end + end + end + end +end diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb new file mode 100644 index 00000000000..2e485ee22a9 --- /dev/null +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -0,0 +1,39 @@ +module Gitlab + module Ci + module Status + module Stage + class Factory + EXTENDED_STATUSES = [] + + def initialize(stage) + @stage = stage + @status = stage.status || :created + end + + def fabricate! + if extended_status + extended_status.new(core_status) + else + core_status + end + end + + private + + def core_status + Gitlab::Ci::Status + .const_get(@status.capitalize) + .new(@stage) + .extend(Status::Pipeline::Common) + end + + def extended_status + @extended ||= EXTENDED_STATUSES.find do |status| + status.matches?(@stage) + end + end + end + end + end + end +end From 10499677e2cbabc6331837d20afc0e0fe68ddc37 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 14:38:01 +0100 Subject: [PATCH 052/140] Added Stage tests --- app/models/ci/stage.rb | 4 +++ lib/gitlab/ci/status/stage/factory.rb | 2 +- .../lib/gitlab/ci/status/stage/common_spec.rb | 26 +++++++++++++++ .../gitlab/ci/status/stage/factory_spec.rb | 33 +++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 spec/lib/gitlab/ci/status/stage/common_spec.rb create mode 100644 spec/lib/gitlab/ci/status/stage/factory_spec.rb diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 2e7f15a16d7..d5ff97c935a 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -20,6 +20,10 @@ module Ci @status ||= statuses.latest.status end + def detailed_status + Gitlab::Ci::Status::Stage::Factory.new(self).fabricate! + end + def statuses @statuses ||= pipeline.statuses.where(stage: stage) end diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb index 2e485ee22a9..a2f7ad81d39 100644 --- a/lib/gitlab/ci/status/stage/factory.rb +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -24,7 +24,7 @@ module Gitlab Gitlab::Ci::Status .const_get(@status.capitalize) .new(@stage) - .extend(Status::Pipeline::Common) + .extend(Status::Stage::Common) end def extended_status diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb new file mode 100644 index 00000000000..c3cb30a35e4 --- /dev/null +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Stage::Common do + let(:pipeline) { create(:ci_pipeline) } + let(:stage) { Ci::Stage.new(pipeline, 'test') } + + subject do + Class.new(Gitlab::Ci::Status::Core) + .new(pipeline).extend(described_class) + end + + it 'does not have action' do + expect(subject).not_to have_action + end + + it 'has details' do + expect(subject).to have_details + end + + it 'links to the pipeline details page' do + expect(subject.details_path) + .to include "pipelines/#{pipeline.id}" + expect(subject.details_path) + .to include "##{stage.name}" + end +end diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb new file mode 100644 index 00000000000..a04fd569fc5 --- /dev/null +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Stage::Factory do + let(:pipeline) { create(:ci_pipeline) } + let(:stage) { Ci::Stage.new(pipeline, 'test') } + + subject do + described_class.new(stage) + end + + let(:status) do + subject.fabricate! + end + + context 'when stage has a core status' do + HasStatus::AVAILABLE_STATUSES.each do |core_status| + context "when core status is #{core_status}" do + let(:build) { create(:ci_build, pipeline: pipeline, stage: stage.name, status: core_status) } + + it "fabricates a core status #{core_status}" do + expect(status).to be_a( + Gitlab::Ci::Status.const_get(core_status.capitalize)) + end + + it 'extends core status with common pipeline methods' do + expect(status).to have_details + expect(status.details_path).to include "pipelines/#{pipeline.id}" + expect(status.details_path).to include "##{stage.name}" + end + end + end + end +end From 13cee6d7fc568f42a43dd78cc86c033d06faf2b3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 14:47:35 +0100 Subject: [PATCH 053/140] Fix test failures --- app/models/ci/stage.rb | 6 +++--- lib/gitlab/ci/status/stage/common.rb | 4 ++-- spec/lib/gitlab/ci/status/stage/common_spec.rb | 4 ++-- spec/lib/gitlab/ci/status/stage/factory_spec.rb | 6 ++++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index d5ff97c935a..fe1c5c642e1 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -8,7 +8,7 @@ module Ci delegate :project, to: :pipeline - def initialize(pipeline, name: name, status: nil) + def initialize(pipeline, name:, status: nil) @pipeline, @name, @status = pipeline, name, status end @@ -25,11 +25,11 @@ module Ci end def statuses - @statuses ||= pipeline.statuses.where(stage: stage) + @statuses ||= pipeline.statuses.where(stage: name) end def builds - @builds ||= pipeline.builds.where(stage: stage) + @builds ||= pipeline.builds.where(stage: name) end end end diff --git a/lib/gitlab/ci/status/stage/common.rb b/lib/gitlab/ci/status/stage/common.rb index a1513024c6c..14c437d2b98 100644 --- a/lib/gitlab/ci/status/stage/common.rb +++ b/lib/gitlab/ci/status/stage/common.rb @@ -10,8 +10,8 @@ module Gitlab def details_path namespace_project_pipeline_path(@subject.project.namespace, @subject.project, - @subject, - anchor: subject.name) + @subject.pipeline, + anchor: @subject.name) end def has_action? diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index c3cb30a35e4..f15d6047878 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Common do let(:pipeline) { create(:ci_pipeline) } - let(:stage) { Ci::Stage.new(pipeline, 'test') } + let(:stage) { Ci::Stage.new(pipeline, name: 'test') } subject do Class.new(Gitlab::Ci::Status::Core) - .new(pipeline).extend(described_class) + .new(stage).extend(described_class) end it 'does not have action' do diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index a04fd569fc5..2d22bd1e2a0 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Factory do let(:pipeline) { create(:ci_pipeline) } - let(:stage) { Ci::Stage.new(pipeline, 'test') } + let(:stage) { Ci::Stage.new(pipeline, name: 'test') } subject do described_class.new(stage) @@ -15,7 +15,9 @@ describe Gitlab::Ci::Status::Stage::Factory do context 'when stage has a core status' do HasStatus::AVAILABLE_STATUSES.each do |core_status| context "when core status is #{core_status}" do - let(:build) { create(:ci_build, pipeline: pipeline, stage: stage.name, status: core_status) } + let!(:build) do + create(:ci_build, pipeline: pipeline, stage: 'test', status: core_status) + end it "fabricates a core status #{core_status}" do expect(status).to be_a( From 6d80b94a89cd2151cbf37f6f98f79d23df7fa638 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 14:48:37 +0100 Subject: [PATCH 054/140] Fix handling of skipped vs success status --- app/models/concerns/has_status.rb | 6 +++--- app/services/ci/process_pipeline_service.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index 2f5aa91a964..215367cc1dc 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -4,7 +4,7 @@ module HasStatus AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped] STARTED_STATUSES = %w[running success failed skipped] ACTIVE_STATUSES = %w[pending running] - COMPLETED_STATUSES = %w[success failed canceled] + COMPLETED_STATUSES = %w[success failed canceled skipped] ORDERED_STATUSES = %w[failed pending running canceled success skipped] class_methods do @@ -23,9 +23,9 @@ module HasStatus canceled = scope.canceled.select('count(*)').to_sql "(CASE - WHEN (#{builds})=(#{success}) THEN 'success' + WHEN (#{builds})=(#{skipped}) THEN 'skipped' WHEN (#{builds})=(#{created}) THEN 'created' - WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'skipped' + WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success' WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled' WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running' diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb index 2e028c44d8b..79eb97b7b55 100644 --- a/app/services/ci/process_pipeline_service.rb +++ b/app/services/ci/process_pipeline_service.rb @@ -44,11 +44,11 @@ module Ci def valid_statuses_for_when(value) case value when 'on_success' - %w[success] + %w[success skipped] when 'on_failure' %w[failed] when 'always' - %w[success failed] + %w[success failed skipped] else [] end From 260d754ca89c14297e0e360d35d7914d57e290bf Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 17:52:50 +0100 Subject: [PATCH 055/140] Fix handling of allowed to failure jobs --- app/models/ci/pipeline.rb | 2 +- app/models/commit_status.rb | 7 +-- app/models/concerns/has_status.rb | 3 +- .../projects/ci/pipelines/_pipeline.html.haml | 2 +- spec/models/ci/pipeline_spec.rb | 51 +++++++++++++++---- spec/models/commit_status_spec.rb | 45 +--------------- spec/models/concerns/has_status_spec.rb | 2 +- 7 files changed, 49 insertions(+), 63 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index b6b9a90a589..aa23b5cf97c 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -114,7 +114,7 @@ module Ci pluck('sg.stage', status_sql) stages_with_statuses.map do |stage| - Ci::Stage.new(self, stage.first, status: stage.last) + Ci::Stage.new(self, name: stage.first, status: stage.last) end end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 2a537dc2a13..cf90475f4d4 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -31,14 +31,9 @@ class CommitStatus < ActiveRecord::Base end scope :exclude_ignored, -> do - quoted_when = connection.quote_column_name('when') # We want to ignore failed_but_allowed jobs where("allow_failure = ? OR status IN (?)", - false, all_state_names - [:failed, :canceled]). - # We want to ignore skipped manual jobs - where("#{quoted_when} <> ? OR status <> ?", 'manual', 'skipped'). - # We want to ignore skipped on_failure - where("#{quoted_when} <> ? OR status <> ?", 'on_failure', 'skipped') + false, all_state_names - [:failed, :canceled]) end scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) } diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index 215367cc1dc..43f312d319e 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -24,8 +24,9 @@ module HasStatus "(CASE WHEN (#{builds})=(#{skipped}) THEN 'skipped' + WHEN (#{builds})=(#{success}) THEN 'success' WHEN (#{builds})=(#{created}) THEN 'created' - WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success' + WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'skipped' WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled' WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running' diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index e4a963a278c..b58dceb58c9 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -44,7 +44,7 @@ Cant find HEAD commit for this branch %td.stage-cell - - pipeline.stages_with_statuses.each do |stage| + - pipeline.stages.each do |stage| - if stage.status - tooltip = "#{stage.name.titleize}: #{stage.status || 'not found'}" .stage-container diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 3f93d9ddf19..cdc858c13b4 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -20,8 +20,6 @@ describe Ci::Pipeline, models: true do it { is_expected.to respond_to :git_author_email } it { is_expected.to respond_to :short_sha } - it { is_expected.to delegate_method(:stages).to(:statuses) } - describe '#valid_commit_sha' do context 'commit.sha can not start with 00000000' do before do @@ -125,16 +123,51 @@ describe Ci::Pipeline, models: true do end describe '#stages' do - let(:pipeline2) { FactoryGirl.create :ci_pipeline, project: project } - subject { CommitStatus.where(pipeline: [pipeline, pipeline2]).stages } - before do - FactoryGirl.create :ci_build, pipeline: pipeline2, stage: 'test', stage_idx: 1 - FactoryGirl.create :ci_build, pipeline: pipeline, stage: 'build', stage_idx: 0 + create(:commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success') + create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed') + create(:commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running') + create(:commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success') end - it 'return all stages' do - is_expected.to eq(%w(build test)) + subject { pipeline.stages } + + context 'stages list' do + it 'returns ordered list of stages' do + expect(subject.map(&:name)).to eq(%w[build test deploy]) + end + end + + it 'returns a valid number of stages' do + expect(pipeline.stages_count).to eq(3) + end + + context 'stages with statuses' do + let(:statuses) do + subject.map do |stage| + [stage.name, stage.status] + end + end + + it 'returns list of stages with statuses' do + expect(statuses).to eq([['build', 'failed'], + ['test', 'success'], + ['deploy', 'running'] + ]) + end + + context 'when build is retried' do + before do + create(:commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success') + end + + it 'ignores the previous state' do + expect(statuses).to eq([['build', 'success'], + ['test', 'success'], + ['deploy', 'running'] + ]) + end + end end end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 80c2a1bc7a9..1ec08c2a9d0 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -175,7 +175,7 @@ describe CommitStatus, models: true do end it 'returns statuses without what we want to ignore' do - is_expected.to eq(statuses.values_at(1, 2, 4, 5, 6, 8, 9)) + is_expected.to eq(statuses.values_at(0, 1, 2, 3, 4, 5, 6, 8, 9)) end end @@ -200,49 +200,6 @@ describe CommitStatus, models: true do end end - describe '#stages' do - before do - create :commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success' - create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed' - create :commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running' - create :commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success' - end - - context 'stages list' do - subject { CommitStatus.where(pipeline: pipeline).stages } - - it 'returns ordered list of stages' do - is_expected.to eq(%w[build test deploy]) - end - end - - context 'stages with statuses' do - subject { CommitStatus.where(pipeline: pipeline).latest.stages_status } - - it 'returns list of stages with statuses' do - is_expected.to eq({ - 'build' => 'failed', - 'test' => 'success', - 'deploy' => 'running' - }) - end - - context 'when build is retried' do - before do - create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success' - end - - it 'ignores a previous state' do - is_expected.to eq({ - 'build' => 'success', - 'test' => 'success', - 'deploy' => 'running' - }) - end - end - end - end - describe '#commit' do it 'returns commit pipeline has been created for' do expect(commit_status.commit).to eq project.commit diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb index 9defb17dc92..4d0f51fe82a 100644 --- a/spec/models/concerns/has_status_spec.rb +++ b/spec/models/concerns/has_status_spec.rb @@ -48,7 +48,7 @@ describe HasStatus do [create(type, status: :failed, allow_failure: true)] end - it { is_expected.to eq 'success' } + it { is_expected.to eq 'skipped' } end context 'success and canceled' do From 401c155e16e4966be538fd14f23e268cd3383aa7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 5 Dec 2016 18:16:34 +0100 Subject: [PATCH 056/140] Update stage rendering views --- app/views/projects/builds/_sidebar.html.haml | 4 ++-- app/views/projects/commit/_pipeline.html.haml | 16 +++----------- .../projects/commit/_pipeline_stage.html.haml | 14 ------------ app/views/projects/pipelines/_graph.html.haml | 4 ++++ .../projects/pipelines/_with_tabs.html.haml | 15 +++---------- app/views/projects/stage/_graph.html.haml | 22 +++++++++++++++++++ .../_in_stage_group.html.haml} | 0 app/views/projects/stage/_stage.html.haml | 2 +- 8 files changed, 35 insertions(+), 42 deletions(-) delete mode 100644 app/views/projects/commit/_pipeline_stage.html.haml create mode 100644 app/views/projects/pipelines/_graph.html.haml create mode 100644 app/views/projects/stage/_graph.html.haml rename app/views/projects/{commit/_pipeline_status_group.html.haml => stage/_in_stage_group.html.haml} (100%) diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index a45612fb28b..ce8b66b1945 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -111,7 +111,7 @@ %span.label.label-primary = tag - - if @build.pipeline.stages_count.many? + - if @build.pipeline.stages_count > 1 .dropdown.build-dropdown .title Stage %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'} @@ -120,7 +120,7 @@ %ul.dropdown-menu - @build.pipeline.stages.each do |stage| %li - %a.stage-item= stage + %a.stage-item= stage.name .builds-container - HasStatus::ORDERED_STATUSES.each do |build_status| diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index 2cd40bb1106..4fc5e15592a 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -25,18 +25,7 @@ = time_interval_in_words pipeline.duration .row-content-block.build-content.middle-block.pipeline-graph.hidden - .pipeline-visualization - %ul.stage-column-list - - pipeline.stages.each do |stage| - %li.stage-column - .stage-name - %a{name: stage.name} - - if stage.name - = stage.name.titleize - .builds-container - %ul - = render "projects/commit/pipeline_stage", statuses: stage.statuses - + = render "projects/pipelines/graph", subject: pipeline - if pipeline.yaml_errors.present? .bs-callout.bs-callout-danger @@ -61,4 +50,5 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - = render pipeline.stages + - pipeline.stages.each do |stage| + = render "projects/stage/stage", subject: stage diff --git a/app/views/projects/commit/_pipeline_stage.html.haml b/app/views/projects/commit/_pipeline_stage.html.haml deleted file mode 100644 index f9a9c8707f5..00000000000 --- a/app/views/projects/commit/_pipeline_stage.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -- status_groups = statuses.sort_by(&:name).group_by(&:group_name) -- status_groups.each do |group_name, grouped_statuses| - - if grouped_statuses.one? - - status = grouped_statuses.first - - is_playable = status.playable? && can?(current_user, :update_build, @project) - %li.build{ class: ("playable" if is_playable) } - .curve - .build-content - = render "projects/#{status.to_partial_path}_pipeline", subject: status - - else - %li.build - .curve - .dropdown.inline.build-content - = render "projects/commit/pipeline_status_group", name: group_name, subject: grouped_statuses diff --git a/app/views/projects/pipelines/_graph.html.haml b/app/views/projects/pipelines/_graph.html.haml new file mode 100644 index 00000000000..3bb6426c156 --- /dev/null +++ b/app/views/projects/pipelines/_graph.html.haml @@ -0,0 +1,4 @@ +.pipeline-visualization + %ul.stage-column-list + - subject.stages.each do |stage| + = render "projects/stage/graph", subject: stage diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 57e793d2e59..2ace9339af3 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -13,17 +13,7 @@ .tab-content #js-tab-pipeline.tab-pane .build-content.middle-block.pipeline-graph - .pipeline-visualization - %ul.stage-column-list - - pipeline.stages.each do |stage| - %li.stage-column - .stage-name - %a{name: stage.name} - - if stage.name - = stage.name.titleize - .builds-container - %ul - = render "projects/commit/pipeline_stage", statuses: statuses + = render "projects/pipelines/graph", subject: pipeline #js-tab-builds.tab-pane - if pipeline.yaml_errors.present? @@ -49,4 +39,5 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - = render pipeline.stages + - pipeline.stages.each do |stage| + = render "projects/stage/stage", subject: stage diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml new file mode 100644 index 00000000000..f1d11db58ab --- /dev/null +++ b/app/views/projects/stage/_graph.html.haml @@ -0,0 +1,22 @@ +%li.stage-column + .stage-name + %a{ name: subject.name } + - if subject.name + = subject.name.titleize + .builds-container + %ul + - statuses = subject.statuses.latest + - status_groups = statuses.sort_by(&:name).group_by(&:group_name) + - status_groups.each do |group_name, grouped_statuses| + - if grouped_statuses.one? + - status = grouped_statuses.first + - is_playable = status.playable? && can?(current_user, :update_build, @project) + %li.build{ class: ("playable" if is_playable) } + .curve + .build-content + = render "projects/#{status.to_partial_path}_pipeline", subject: status + - else + %li.build + .curve + .dropdown.inline.build-content + = render "projects/stage/in_stage_group", name: group_name, subject: grouped_statuses diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml similarity index 100% rename from app/views/projects/commit/_pipeline_status_group.html.haml rename to app/views/projects/stage/_in_stage_group.html.haml diff --git a/app/views/projects/stage/_stage.html.haml b/app/views/projects/stage/_stage.html.haml index 717075620d9..055d8fca38b 100644 --- a/app/views/projects/stage/_stage.html.haml +++ b/app/views/projects/stage/_stage.html.haml @@ -1,7 +1,7 @@ %tr %th{colspan: 10} %strong - %a{name: subject.name} + %a{ name: subject.name } %span{class: "ci-status-link ci-status-icon-#{subject.status}"} = ci_icon_for_status(subject.status) - if subject.name From 9d85320c67395399ad52467fde2bacf62427ed4b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 6 Dec 2016 15:07:02 +0100 Subject: [PATCH 057/140] Run builds with runners with tags gitlab-org and 2gb --- .gitlab-ci.yml | 108 +++++++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8de7ca897ad..e522d47d19d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,12 @@ stages: - post-test - pages -# Prepare and merge knapsack tests +# Predefined scopes +.dedicated-runner: &dedicated-runner + tags: + - gitlab-org + - 2gb + .knapsack-state: &knapsack-state services: [] variables: @@ -45,47 +50,14 @@ stages: paths: - knapsack/ -knapsack: - <<: *knapsack-state - stage: prepare - script: - - mkdir -p knapsack/ - - '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json' - - '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json' - -update-knapsack: - <<: *knapsack-state - stage: post-test - script: - - scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json - - scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json - - rm -f knapsack/*_node_*.json - only: - - master@gitlab-org/gitlab-ce - - master@gitlab-org/gitlab-ee - - master@gitlab/gitlabhq - - master@gitlab/gitlab-ee - .use-db: &use-db services: - mysql:latest - redis:alpine -setup-test-env: - <<: *use-db - stage: prepare - script: - - bundle exec rake assets:precompile 2>/dev/null - - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' - artifacts: - expire_in: 7d - paths: - - public/assets - - tmp/tests - - .rspec-knapsack: &rspec-knapsack stage: test + <<: *dedicated-runner <<: *use-db script: - JOB_NAME=( $CI_BUILD_NAME ) @@ -103,6 +75,7 @@ setup-test-env: .spinach-knapsack: &spinach-knapsack stage: test + <<: *dedicated-runner <<: *use-db script: - JOB_NAME=( $CI_BUILD_NAME ) @@ -118,6 +91,44 @@ setup-test-env: - knapsack/ - coverage/ +# Prepare and merge knapsack tests + +knapsack: + <<: *knapsack-state + <<: *dedicated-runner + stage: prepare + script: + - mkdir -p knapsack/ + - '[[ -f knapsack/rspec_report.json ]] || echo "{}" > knapsack/rspec_report.json' + - '[[ -f knapsack/spinach_report.json ]] || echo "{}" > knapsack/spinach_report.json' + +setup-test-env: + <<: *use-db + <<: *dedicated-runner + stage: prepare + script: + - bundle exec rake assets:precompile 2>/dev/null + - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' + artifacts: + expire_in: 7d + paths: + - public/assets + - tmp/tests + +update-knapsack: + <<: *knapsack-state + <<: *dedicated-runner + stage: post-test + script: + - scripts/merge-reports knapsack/rspec_report.json knapsack/rspec_node_*.json + - scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json + - rm -f knapsack/*_node_*.json + only: + - master@gitlab-org/gitlab-ce + - master@gitlab-org/gitlab-ee + - master@gitlab/gitlabhq + - master@gitlab/gitlab-ee + rspec 0 20: *rspec-knapsack rspec 1 20: *rspec-knapsack rspec 2 20: *rspec-knapsack @@ -166,10 +177,12 @@ spinach 9 10: *spinach-knapsack .rspec-knapsack-ruby21: &rspec-knapsack-ruby21 <<: *rspec-knapsack + <<: *dedicated-runner <<: *ruby-21 .spinach-knapsack-ruby21: &spinach-knapsack-ruby21 <<: *spinach-knapsack + <<: *dedicated-runner <<: *ruby-21 rspec 0 20 ruby21: *rspec-knapsack-ruby21 @@ -214,6 +227,7 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21 .exec: &exec <<: *ruby-static-analysis + <<: *dedicated-runner stage: test script: - bundle exec $CI_BUILD_NAME @@ -249,12 +263,14 @@ rake ee_compat_check: rake db:migrate:reset: stage: test <<: *use-db + <<: *dedicated-runner script: - rake db:migrate:reset rake db:seed_fu: stage: test <<: *use-db + <<: *dedicated-runner variables: SIZE: "1" SETUP_DB: "false" @@ -276,6 +292,7 @@ teaspoon: - node_modules/ stage: test <<: *use-db + <<: *dedicated-runner script: - npm install - npm link istanbul @@ -288,20 +305,23 @@ teaspoon: lint-doc: stage: test + <<: *dedicated-runner image: "phusion/baseimage:latest" before_script: [] script: - scripts/lint-doc.sh bundler:check: - stage: test - <<: *ruby-static-analysis - script: + stage: test + <<: *dedicated-runner + <<: *ruby-static-analysis + script: - bundle check bundler:audit: stage: test <<: *ruby-static-analysis + <<: *dedicated-runner only: - master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ee @@ -313,6 +333,7 @@ bundler:audit: migration paths: stage: test <<: *use-db + <<: *dedicated-runner variables: SETUP_DB: "false" only: @@ -334,6 +355,7 @@ migration paths: coverage: stage: post-test services: [] + <<: *dedicated-runner variables: SETUP_DB: "false" USE_BUNDLE_INSTALL: "true" @@ -347,6 +369,7 @@ coverage: - coverage/assets/ lint:javascript: + <<: *dedicated-runner cache: paths: - node_modules/ @@ -358,6 +381,7 @@ lint:javascript: - npm --silent run eslint lint:javascript:report: + <<: *dedicated-runner cache: paths: - node_modules/ @@ -379,6 +403,7 @@ lint:javascript:report: trigger_docs: stage: post-test image: "alpine" + <<: *dedicated-runner before_script: - apk update && apk add curl variables: @@ -394,6 +419,7 @@ trigger_docs: notify:slack: stage: post-test + <<: *dedicated-runner variables: SETUP_DB: "false" USE_BUNDLE_INSTALL: "false" @@ -409,6 +435,7 @@ notify:slack: pages: before_script: [] stage: pages + <<: *dedicated-runner dependencies: - coverage - teaspoon @@ -423,11 +450,12 @@ pages: paths: - public only: - - master + - master@gitlab-org/gitlab-ce # Insurance in case a gem needed by one of our releases gets yanked from # rubygems.org in the future. cache gems: + <<: *dedicated-runner only: - tags variables: @@ -437,3 +465,5 @@ cache gems: artifacts: paths: - vendor/cache + only: + - master@gitlab-org/gitlab-ce From 9c149094f61888e23f610ec2364adefd98dbef9a Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 5 Dec 2016 12:55:57 -0600 Subject: [PATCH 058/140] Remove tags.scss --- app/assets/stylesheets/framework/nav.scss | 10 +++ app/assets/stylesheets/pages/tags.scss | 7 -- .../projects/buttons/_download.html.haml | 79 +++++++++---------- app/views/projects/tags/show.html.haml | 21 ++--- 4 files changed, 60 insertions(+), 57 deletions(-) delete mode 100644 app/assets/stylesheets/pages/tags.scss diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index c84a71a624d..69da520f21f 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -268,6 +268,16 @@ width: auto; } } + + &.multi-line { + .nav-text { + line-height: 20px; + } + + .nav-controls { + padding: 17px 0; + } + } } .layout-nav { diff --git a/app/assets/stylesheets/pages/tags.scss b/app/assets/stylesheets/pages/tags.scss deleted file mode 100644 index 24ebd3e7cfa..00000000000 --- a/app/assets/stylesheets/pages/tags.scss +++ /dev/null @@ -1,7 +0,0 @@ -.tag-buttons { - line-height: 40px; - - .btn:not(.dropdown-toggle) { - margin-left: 10px; - } -} diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 7b995bd8735..40bfa01a45a 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -1,42 +1,41 @@ - if !project.empty_repo? && can?(current_user, :download_code, project) - %span{class: 'download-button'} - .dropdown.inline - %button.btn{ 'data-toggle' => 'dropdown' } - = icon('download') - = icon("caret-down") - %span.sr-only - Select Archive Format - %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } - %li.dropdown-header Source code - %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do - %i.fa.fa-download - %span Download zip - %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do - %i.fa.fa-download - %span Download tar.gz - %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do - %i.fa.fa-download - %span Download tar.bz2 - %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar'), rel: 'nofollow' do - %i.fa.fa-download - %span Download tar + .dropdown.inline.download-button + %button.btn{ 'data-toggle' => 'dropdown' } + = icon('download') + = icon("caret-down") + %span.sr-only + Select Archive Format + %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + %li.dropdown-header Source code + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do + %i.fa.fa-download + %span Download zip + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do + %i.fa.fa-download + %span Download tar.gz + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do + %i.fa.fa-download + %span Download tar.bz2 + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar'), rel: 'nofollow' do + %i.fa.fa-download + %span Download tar - - pipeline = project.pipelines.latest_successful_for(ref) - - if pipeline - - artifacts = pipeline.builds.latest.with_artifacts - - if artifacts.any? - %li.dropdown-header Artifacts - - unless pipeline.latest? - - latest_pipeline = project.pipeline_for(ref) - %li - .unclickable= ci_status_for_statuseable(latest_pipeline) - %li.dropdown-header Previous Artifacts - - artifacts.each do |job| - %li - = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, "#{ref}/download", job: job.name), rel: 'nofollow' do - %i.fa.fa-download - %span Download '#{job.name}' + - pipeline = project.pipelines.latest_successful_for(ref) + - if pipeline + - artifacts = pipeline.builds.latest.with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - unless pipeline.latest? + - latest_pipeline = project.pipeline_for(ref) + %li + .unclickable= ci_status_for_statuseable(latest_pipeline) + %li.dropdown-header Previous Artifacts + - artifacts.each do |job| + %li + = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, "#{ref}/download", job: job.name), rel: 'nofollow' do + %i.fa.fa-download + %span Download '#{job.name}' diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 155af755759..12facb6eb73 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -3,8 +3,16 @@ = render "projects/commits/head" %div{ class: container_class } - .sub-header-block - .pull-right.tag-buttons + .top-area.multi-line + .nav-text + .title + %span.item-title= @tag.name + - if @commit + = render 'projects/branches/commit', commit: @commit, project: @project + - else + Cant find HEAD commit for this tag + + .nav-controls - if can?(current_user, :push_code, @project) = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do = icon("pencil") @@ -15,15 +23,8 @@ = render 'projects/buttons/download', project: @project, ref: @tag.name - if can?(current_user, :admin_project, @project) .pull-right - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o - .tag-info.append-bottom-10 - .title - %span.item-title= @tag.name - - if @commit - = render 'projects/branches/commit', commit: @commit, project: @project - - else - Cant find HEAD commit for this tag - if @tag.message.present? %pre.body = strip_gpg_signature(@tag.message) From 4447652e8191cd0f1543568813c854bfdc2c8a61 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 6 Dec 2016 08:27:42 -0600 Subject: [PATCH 059/140] Move image styles to framework --- .../stylesheets/{pages/appearances.scss => framework/images.scss} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/assets/stylesheets/{pages/appearances.scss => framework/images.scss} (100%) diff --git a/app/assets/stylesheets/pages/appearances.scss b/app/assets/stylesheets/framework/images.scss similarity index 100% rename from app/assets/stylesheets/pages/appearances.scss rename to app/assets/stylesheets/framework/images.scss From 4aaf6af3cc7970cbe5414d323f290b4a94e479d0 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 6 Dec 2016 08:29:01 -0600 Subject: [PATCH 060/140] Move award emojis to framwork --- app/assets/stylesheets/framework.scss | 2 ++ app/assets/stylesheets/{pages => framework}/awards.scss | 0 2 files changed, 2 insertions(+) rename app/assets/stylesheets/{pages => framework}/awards.scss (100%) diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 4aaff7d04f1..c0ddaacd558 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -40,3 +40,5 @@ @import "framework/blank"; @import "framework/wells.scss"; @import "framework/page-header.scss"; +@import "framework/awards.scss"; +@import "framework/images.scss"; diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/framework/awards.scss similarity index 100% rename from app/assets/stylesheets/pages/awards.scss rename to app/assets/stylesheets/framework/awards.scss From 2b5a24a8a96bba788db202805cc4f4ec2d278a3f Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Tue, 6 Dec 2016 15:42:11 +0100 Subject: [PATCH 061/140] Explain "js: true" in "deleted_source_branch_spec.rb" [ci skip] --- spec/features/merge_requests/deleted_source_branch_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb index 778b3a90cf3..d5c9ed8a3b7 100644 --- a/spec/features/merge_requests/deleted_source_branch_spec.rb +++ b/spec/features/merge_requests/deleted_source_branch_spec.rb @@ -1,5 +1,8 @@ require 'spec_helper' +# This test serves as a regression test for a bug that caused an error +# message to be shown by JavaScript when the source branch was deleted. +# Please do not remove "js: true". describe 'Deleted source branch', feature: true, js: true do let(:user) { create(:user) } let(:merge_request) { create(:merge_request) } From 953a10947bea8ff75290cd50fae4ae1f7636a71d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 6 Dec 2016 14:49:37 +0100 Subject: [PATCH 062/140] Added Ci::Stage specs --- app/models/ci/pipeline.rb | 4 + lib/gitlab/data_builder/pipeline.rb | 2 +- spec/factories/ci/stages.rb | 13 ++ .../gitlab/ci/status/pipeline/factory_spec.rb | 4 +- .../lib/gitlab/ci/status/stage/common_spec.rb | 4 +- .../gitlab/ci/status/stage/factory_spec.rb | 10 +- spec/models/ci/pipeline_spec.rb | 4 + spec/models/ci/stage_spec.rb | 133 ++++++++++++++++++ 8 files changed, 165 insertions(+), 9 deletions(-) create mode 100644 spec/factories/ci/stages.rb create mode 100644 spec/models/ci/stage_spec.rb diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index aa23b5cf97c..d14825c082a 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -104,6 +104,10 @@ module Ci statuses.select(:stage).distinct.count end + def stages_name + statuses.order(:stage_idx).distinct.pluck(:stage) + end + def stages status_sql = statuses.latest.where('stage=sg.stage').status_sql diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb index e0284d55da8..e50e54b6e99 100644 --- a/lib/gitlab/data_builder/pipeline.rb +++ b/lib/gitlab/data_builder/pipeline.rb @@ -22,7 +22,7 @@ module Gitlab sha: pipeline.sha, before_sha: pipeline.before_sha, status: pipeline.status, - stages: pipeline.stages.map(&:name), + stages: pipeline.stages_name, created_at: pipeline.created_at, finished_at: pipeline.finished_at, duration: pipeline.duration diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb new file mode 100644 index 00000000000..ee3b17b8bf1 --- /dev/null +++ b/spec/factories/ci/stages.rb @@ -0,0 +1,13 @@ +FactoryGirl.define do + factory :ci_stage, class: Ci::Stage do + transient do + name 'test' + status nil + pipeline factory: :ci_empty_pipeline + end + + initialize_with do + Ci::Stage.new(pipeline, name: name, status: status) + end + end +end diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index d6243940f2e..939ad2edf04 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do Gitlab::Ci::Status.const_get(core_status.capitalize)) end - it 'extends core status with common pipeline methods' do + it 'extends core status with common stage methods' do expect(status).to have_details expect(status).not_to have_action expect(status.details_path) @@ -45,7 +45,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do .to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings end - it 'extends core status with common pipeline methods' do + it 'extends core status with common stage methods' do expect(status).to have_details end end diff --git a/spec/lib/gitlab/ci/status/stage/common_spec.rb b/spec/lib/gitlab/ci/status/stage/common_spec.rb index f15d6047878..f3259c6f23e 100644 --- a/spec/lib/gitlab/ci/status/stage/common_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/common_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Common do - let(:pipeline) { create(:ci_pipeline) } - let(:stage) { Ci::Stage.new(pipeline, name: 'test') } + let(:pipeline) { create(:ci_empty_pipeline) } + let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') } subject do Class.new(Gitlab::Ci::Status::Core) diff --git a/spec/lib/gitlab/ci/status/stage/factory_spec.rb b/spec/lib/gitlab/ci/status/stage/factory_spec.rb index 2d22bd1e2a0..17929665c83 100644 --- a/spec/lib/gitlab/ci/status/stage/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/stage/factory_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe Gitlab::Ci::Status::Stage::Factory do - let(:pipeline) { create(:ci_pipeline) } - let(:stage) { Ci::Stage.new(pipeline, name: 'test') } + let(:pipeline) { create(:ci_empty_pipeline) } + let(:stage) { build(:ci_stage, pipeline: pipeline, name: 'test') } subject do described_class.new(stage) @@ -15,8 +15,10 @@ describe Gitlab::Ci::Status::Stage::Factory do context 'when stage has a core status' do HasStatus::AVAILABLE_STATUSES.each do |core_status| context "when core status is #{core_status}" do - let!(:build) do + before do create(:ci_build, pipeline: pipeline, stage: 'test', status: core_status) + create(:commit_status, pipeline: pipeline, stage: 'test', status: core_status) + create(:ci_build, pipeline: pipeline, stage: 'build', status: :failed) end it "fabricates a core status #{core_status}" do @@ -24,7 +26,7 @@ describe Gitlab::Ci::Status::Stage::Factory do Gitlab::Ci::Status.const_get(core_status.capitalize)) end - it 'extends core status with common pipeline methods' do + it 'extends core status with common stage methods' do expect(status).to have_details expect(status.details_path).to include "pipelines/#{pipeline.id}" expect(status.details_path).to include "##{stage.name}" diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index cdc858c13b4..8158e71dd55 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -142,6 +142,10 @@ describe Ci::Pipeline, models: true do expect(pipeline.stages_count).to eq(3) end + it 'returns a valid names of stages' do + expect(pipeline.stages_name).to eq(['build', 'test', 'deploy']) + end + context 'stages with statuses' do let(:statuses) do subject.map do |stage| diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb new file mode 100644 index 00000000000..f232761dba2 --- /dev/null +++ b/spec/models/ci/stage_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' + +describe Ci::Stage, models: true do + let(:stage) { build(:ci_stage) } + let(:pipeline) { stage.pipeline } + let(:stage_name) { stage.name } + + describe '#expectations' do + subject { stage } + + it { is_expected.to include_module(StaticModel) } + + it { is_expected.to respond_to(:pipeline) } + it { is_expected.to respond_to(:name) } + + it { is_expected.to delegate_method(:project).to(:pipeline) } + end + + describe '#statuses' do + let!(:stage_build) { create_job(:ci_build) } + let!(:commit_status) { create_job(:commit_status) } + let!(:other_build) { create_job(:ci_build, stage: 'other stage') } + + subject { stage.statuses } + + it "returns only matching statuses" do + is_expected.to contain_exactly(stage_build, commit_status) + end + end + + describe '#builds' do + let!(:stage_build) { create_job(:ci_build) } + let!(:commit_status) { create_job(:commit_status) } + + subject { stage.builds } + + it "returns only builds" do + is_expected.to contain_exactly(stage_build) + end + end + + describe '#status' do + subject { stage.status } + + context 'if status is already defined' do + let(:stage) { build(:ci_stage, status: 'success') } + + it "returns defined status" do + is_expected.to eq('success') + end + end + + context 'if status has to be calculated' do + let!(:stage_build) { create_job(:ci_build, status: :failed) } + + it "returns status of a build" do + is_expected.to eq('failed') + end + + context 'and builds are retried' do + let!(:new_build) { create_job(:ci_build, status: :success) } + + it "returns status of latest build" do + is_expected.to eq('success') + end + end + end + end + + describe '#detailed_status' do + subject { stage.detailed_status } + + context 'when build is created' do + let!(:stage_build) { create_job(:ci_build, status: :created) } + + it 'returns detailed status for created stage' do + expect(subject.text).to eq 'created' + end + end + + context 'when build is pending' do + let!(:stage_build) { create_job(:ci_build, status: :pending) } + + it 'returns detailed status for pending stage' do + expect(subject.text).to eq 'pending' + end + end + + context 'when build is running' do + let!(:stage_build) { create_job(:ci_build, status: :running) } + + it 'returns detailed status for running stage' do + expect(subject.text).to eq 'running' + end + end + + context 'when build is successful' do + let!(:stage_build) { create_job(:ci_build, status: :success) } + + it 'returns detailed status for successful stage' do + expect(subject.text).to eq 'passed' + end + end + + context 'when build is failed' do + let!(:stage_build) { create_job(:ci_build, status: :failed) } + + it 'returns detailed status for failed stage' do + expect(subject.text).to eq 'failed' + end + end + + context 'when build is canceled' do + let!(:stage_build) { create_job(:ci_build, status: :canceled) } + + it 'returns detailed status for canceled stage' do + expect(subject.text).to eq 'canceled' + end + end + + context 'when build is skipped' do + let!(:stage_build) { create_job(:ci_build, status: :skipped) } + + it 'returns detailed status for skipped stage' do + expect(subject.text).to eq 'skipped' + end + end + end + + def create_job(type, status: 'success', stage: stage_name) + create(type, pipeline: pipeline, stage: stage, status: status) + end +end From ee07628721eec27587d3eebedc9193cd73dd781b Mon Sep 17 00:00:00 2001 From: Pedro Moreira da Silva Date: Tue, 6 Dec 2016 15:06:14 +0000 Subject: [PATCH 063/140] Fix active nav badges not inheriting the link color. --- app/assets/stylesheets/framework/nav.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index c84a71a624d..981cb0adba1 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -71,6 +71,10 @@ border-bottom: 2px solid $link-underline-blue; color: $black; font-weight: 600; + + .badge { + color: $black; + } } .badge { From bdd1193f57a2e2e9c38f5f267179bd62a876f030 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 6 Dec 2016 09:18:17 -0600 Subject: [PATCH 064/140] Remove confirmation.scss --- .../stylesheets/pages/confirmation.scss | 32 ------------------- .../devise/confirmations/almost_there.haml | 7 ++-- 2 files changed, 4 insertions(+), 35 deletions(-) delete mode 100644 app/assets/stylesheets/pages/confirmation.scss diff --git a/app/assets/stylesheets/pages/confirmation.scss b/app/assets/stylesheets/pages/confirmation.scss deleted file mode 100644 index 8aab5e8231d..00000000000 --- a/app/assets/stylesheets/pages/confirmation.scss +++ /dev/null @@ -1,32 +0,0 @@ -.well-confirmation { - margin-bottom: 20px; - border-bottom: 1px solid $gray-darker; - - > h1, - h2, - h3, - h4, - h5, - h6 { - font-weight: 400; - } - - .lead { - margin-bottom: 20px; - } - - ul, - ol { - padding-left: 0; - } - - li { - list-style-type: none; - } -} - -.confirmation-content { - a { - color: $md-link-color; - } -} diff --git a/app/views/devise/confirmations/almost_there.haml b/app/views/devise/confirmations/almost_there.haml index 20cd7b0179d..fb70d158096 100644 --- a/app/views/devise/confirmations/almost_there.haml +++ b/app/views/devise/confirmations/almost_there.haml @@ -1,12 +1,13 @@ -.well-confirmation.text-center +.well-confirmation.text-center.append-bottom-20 %h1.prepend-top-0 Almost there... - %p.lead + %p.lead.append-bottom-20 Please check your email to confirm your account + %hr - if current_application_settings.after_sign_up_text.present? .well-confirmation.text-center = markdown_field(current_application_settings, :after_sign_up_text) -%p.confirmation-content.text-center +%p.text-center No confirmation email received? Please check your spam folder or .append-bottom-20.prepend-top-20.text-center %a.btn.btn-lg.btn-success{ href: new_user_confirmation_path } From c7d7f11389838acd658d1427c0d2ebb80fd8df3b Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Tue, 6 Dec 2016 10:31:56 -0500 Subject: [PATCH 065/140] Bump gitlab-shell version to 4.0.3 --- GITLAB_SHELL_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index fcdb2e109f6..c4e41f94594 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -4.0.0 +4.0.3 From a526e156a117e16d5d135aa69c0011d464697d58 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Tue, 6 Dec 2016 16:08:53 +0000 Subject: [PATCH 066/140] Encourage bug reporters to mention if they use GitLab.com [ci skip] --- .gitlab/issue_templates/Bug.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index ac38f0c9521..6d7d88c6791 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -21,6 +21,8 @@ logs, and code as it's very hard to read otherwise.) ### Output of checks +(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com) + #### Results of GitLab application Check (For installations with omnibus-gitlab package run and paste the output of: From 51a921baf90be2a6654990b9b7d062f4c613a64b Mon Sep 17 00:00:00 2001 From: Bob Van Landuyt Date: Tue, 6 Dec 2016 17:13:14 +0100 Subject: [PATCH 067/140] A simpler implementation of finding a merge request Following a discussion in !7180 --- lib/api/issues.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/api/issues.rb b/lib/api/issues.rb index cfb7c45de8e..26c8f2fecd0 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -28,14 +28,6 @@ module API new_params end - - def merge_request_for_resolving_discussions - return unless merge_request_iid = params[:merge_request_for_resolving_discussions] - - @merge_request_for_resolving_discussions ||= MergeRequestsFinder.new(current_user, project_id: user_project.id). - execute. - find_by(iid: merge_request_iid) - end end resource :issues do @@ -179,7 +171,12 @@ module API attrs = attributes_for_keys(keys) attrs[:labels] = params[:labels] if params[:labels] - attrs[:merge_request_for_resolving_discussions] = merge_request_for_resolving_discussions if params[:merge_request_for_resolving_discussions] + + if merge_request_iid = params[:merge_request_for_resolving_discussions] + attrs[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id). + execute. + find_by(iid: merge_request_iid) + end # Convert and filter out invalid confidential flags attrs['confidential'] = to_boolean(attrs['confidential']) From 7e42c22991896e52a28537ff61a48334bf84f7c9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 6 Dec 2016 18:50:48 +0200 Subject: [PATCH 068/140] Fix 404 error when visit group label edit page Signed-off-by: Dmitriy Zaporozhets --- config/routes/group.rb | 22 +++++++++++----------- spec/features/groups/labels/edit_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 spec/features/groups/labels/edit_spec.rb diff --git a/config/routes/group.rb b/config/routes/group.rb index 9fe72990994..776c31c9dac 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -1,15 +1,5 @@ resources :groups, only: [:index, :new, :create] -scope(path: 'groups/*id', - controller: :groups, - constraints: { id: Gitlab::Regex.namespace_route_regex }) do - get :edit, as: :edit_group - get :issues, as: :issues_group - get :merge_requests, as: :merge_requests_group - get :projects, as: :projects_group - get :activity, as: :activity_group -end - scope(path: 'groups/*group_id', module: :groups, as: :group, @@ -22,10 +12,20 @@ scope(path: 'groups/*group_id', resource :avatar, only: [:destroy] resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create] - resources :labels, except: [:show], constraints: { id: /\d+/ } do + resources :labels, except: [:show] do post :toggle_subscription, on: :member end end +scope(path: 'groups/*id', + controller: :groups, + constraints: { id: Gitlab::Regex.namespace_route_regex }) do + get :edit, as: :edit_group + get :issues, as: :issues_group + get :merge_requests, as: :merge_requests_group + get :projects, as: :projects_group + get :activity, as: :activity_group +end + # Must be last route in this file get 'groups/*id' => 'groups#show', as: :group_canonical, constraints: { id: Gitlab::Regex.namespace_route_regex } diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb new file mode 100644 index 00000000000..69281cecb7b --- /dev/null +++ b/spec/features/groups/labels/edit_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +feature 'Edit group label', feature: true do + given(:user) { create(:user) } + given(:group) { create(:group) } + given(:label) { create(:group_label, group: group) } + + background do + group.add_owner(user) + login_as(user) + visit edit_group_label_path(group, label) + end + + scenario 'update label with new title' do + fill_in 'label_title', with: 'new label name' + click_button 'Save changes' + + expect(current_path).to eq(root_path) + expect(label.reload.title).to eq('new label name') + end +end From e16bc50d363fe65bbceb8aed39a0e19ec91b3329 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Tue, 6 Dec 2016 13:54:35 +0500 Subject: [PATCH 069/140] Move admin logs spinach test to rspec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- .../move-admin-logs-spinach-test-to-rspec.yml | 4 ++++ features/admin/logs.feature | 8 -------- features/steps/admin/logs.rb | 11 ----------- spec/features/admin/admin_browses_logs_spec.rb | 15 +++++++++++++++ 4 files changed, 19 insertions(+), 19 deletions(-) create mode 100644 changelogs/unreleased/move-admin-logs-spinach-test-to-rspec.yml delete mode 100644 features/admin/logs.feature delete mode 100644 features/steps/admin/logs.rb create mode 100644 spec/features/admin/admin_browses_logs_spec.rb diff --git a/changelogs/unreleased/move-admin-logs-spinach-test-to-rspec.yml b/changelogs/unreleased/move-admin-logs-spinach-test-to-rspec.yml new file mode 100644 index 00000000000..696aa8510a0 --- /dev/null +++ b/changelogs/unreleased/move-admin-logs-spinach-test-to-rspec.yml @@ -0,0 +1,4 @@ +--- +title: Move admin logs spinach test to rspec +merge_request: 7945 +author: Semyon Pupkov diff --git a/features/admin/logs.feature b/features/admin/logs.feature deleted file mode 100644 index ceb3bc34927..00000000000 --- a/features/admin/logs.feature +++ /dev/null @@ -1,8 +0,0 @@ -@admin -Feature: Admin Logs - Background: - Given I sign in as an admin - - Scenario: On Admin Logs - Given I visit admin logs page - Then I should see tabs with available logs diff --git a/features/steps/admin/logs.rb b/features/steps/admin/logs.rb deleted file mode 100644 index 63881d69146..00000000000 --- a/features/steps/admin/logs.rb +++ /dev/null @@ -1,11 +0,0 @@ -class Spinach::Features::AdminLogs < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - - step 'I should see tabs with available logs' do - expect(page).to have_content 'test.log' - expect(page).to have_content 'githost.log' - expect(page).to have_content 'application.log' - end -end diff --git a/spec/features/admin/admin_browses_logs_spec.rb b/spec/features/admin/admin_browses_logs_spec.rb new file mode 100644 index 00000000000..d880f3f07db --- /dev/null +++ b/spec/features/admin/admin_browses_logs_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'Admin browses logs' do + before do + login_as :admin + end + + it 'shows available log files' do + visit admin_logs_path + + expect(page).to have_content 'test.log' + expect(page).to have_content 'githost.log' + expect(page).to have_content 'application.log' + end +end From 9d9fe3c82efa8c0ec75863b5b533af6fdbe48f89 Mon Sep 17 00:00:00 2001 From: Ryan Harris Date: Tue, 6 Dec 2016 12:55:24 -0500 Subject: [PATCH 070/140] Use default btn styling for Housekeeping button on projects settings page --- app/views/projects/edit.html.haml | 2 +- .../unreleased/25324-change-housekeeping-btn-to-default.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/25324-change-housekeeping-btn-to-default.yml diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 3a5af2723c6..f8d856fe152 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -145,7 +145,7 @@ such as compressing file revisions and removing unreachable objects. .col-lg-9 = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project), - method: :post, class: "btn btn-save" + method: :post, class: "btn btn-default" %hr .row.prepend-top-default .col-lg-3 diff --git a/changelogs/unreleased/25324-change-housekeeping-btn-to-default.yml b/changelogs/unreleased/25324-change-housekeeping-btn-to-default.yml new file mode 100644 index 00000000000..0770f9752a0 --- /dev/null +++ b/changelogs/unreleased/25324-change-housekeeping-btn-to-default.yml @@ -0,0 +1,4 @@ +--- +title: Changed Housekeeping button on project settings page to default styling +merge_request: +author: Ryan Harris From d33b22f23890f5d67f1e88a41efc4d8adec6611c Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Mon, 5 Dec 2016 23:50:13 +0500 Subject: [PATCH 071/140] Move admin hooks spinach to rspec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- .../move-admin-hooks-spinach-test-to-rspec.yml | 4 ++++ features/admin/hooks.feature | 9 --------- features/steps/admin/hooks.rb | 15 --------------- spec/features/admin/admin_hooks_spec.rb | 17 +++++++++-------- 4 files changed, 13 insertions(+), 32 deletions(-) create mode 100644 changelogs/unreleased/move-admin-hooks-spinach-test-to-rspec.yml delete mode 100644 features/admin/hooks.feature delete mode 100644 features/steps/admin/hooks.rb diff --git a/changelogs/unreleased/move-admin-hooks-spinach-test-to-rspec.yml b/changelogs/unreleased/move-admin-hooks-spinach-test-to-rspec.yml new file mode 100644 index 00000000000..7dfd741985a --- /dev/null +++ b/changelogs/unreleased/move-admin-hooks-spinach-test-to-rspec.yml @@ -0,0 +1,4 @@ +--- +title: Move admin hooks spinach to rspec +merge_request: 7942 +author: Semyon Pupkov diff --git a/features/admin/hooks.feature b/features/admin/hooks.feature deleted file mode 100644 index 5ca332d9f1c..00000000000 --- a/features/admin/hooks.feature +++ /dev/null @@ -1,9 +0,0 @@ -@admin -Feature: Admin Hooks - Background: - Given I sign in as an admin - - Scenario: On Admin Hooks - Given I visit admin hooks page - Then I submit the form with enabled SSL verification - And I see new hook with enabled SSL verification \ No newline at end of file diff --git a/features/steps/admin/hooks.rb b/features/steps/admin/hooks.rb deleted file mode 100644 index 541e25fcb70..00000000000 --- a/features/steps/admin/hooks.rb +++ /dev/null @@ -1,15 +0,0 @@ -class Spinach::Features::AdminHooks < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedAdmin - - step "I submit the form with enabled SSL verification" do - fill_in 'hook_url', with: 'http://google.com' - check "Enable SSL verification" - click_on "Add System Hook" - end - - step "I see new hook with enabled SSL verification" do - expect(page).to have_content "SSL Verification: enabled" - end -end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index b3ce72b1452..f246997d5a2 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -26,16 +26,17 @@ describe "Admin::Hooks", feature: true do end describe "New Hook" do - before do - @url = FFaker::Internet.uri("http") - visit admin_hooks_path - fill_in "hook_url", with: @url - expect { click_button "Add System Hook" }.to change(SystemHook, :count).by(1) - end + let(:url) { FFaker::Internet.uri('http') } - it "opens new hook popup" do + it 'adds new hook' do + visit admin_hooks_path + fill_in 'hook_url', with: url + check 'Enable SSL verification' + + expect { click_button 'Add System Hook' }.to change(SystemHook, :count).by(1) + expect(page).to have_content 'SSL Verification: enabled' expect(current_path).to eq(admin_hooks_path) - expect(page).to have_content(@url) + expect(page).to have_content(url) end end From bc380e48486903637ec9da977ad2d5bab3dc9dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Thu, 6 Oct 2016 17:50:47 +0000 Subject: [PATCH 072/140] Update custom_hooks.md for global custom hooks and chained hook info https://gitlab.com/gitlab-org/gitlab-shell/merge_requests/93 --- doc/administration/custom_hooks.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md index 0387d730489..06291705702 100644 --- a/doc/administration/custom_hooks.md +++ b/doc/administration/custom_hooks.md @@ -42,6 +42,25 @@ Follow the steps below to set up a custom hook: That's it! Assuming the hook code is properly implemented the hook will fire as appropriate. +## Chained hooks support + +> [Introduced][93] in GitLab Shell 4.1.0. + +The hooks could be also placed in `hooks/.d` (global) or `custom_hooks/.d` (per project) +directories supporting chained execution of the hooks. + +The hooks are searched and executed in this order: +1. `.git/hooks/` - symlink to `gitlab-shell/hooks` global dir +1. `.git/hooks/` - executed by `git` itself, this is `gitlab-shell/hooks/` +1. `.git/custom_hooks/` - per project hook (this is already existing behavior) +1. `.git/custom_hooks/.d/*` - per project hooks +1. `.git/hooks/.d/*` - global hooks: all executable files (minus editor backup files) + +Files in `.d` directories need to be executable and not match the backup file pattern (`*~`). + +The hooks of the same type are executed in order and execution stops on the first +script exiting with non-zero value. + ## Custom error messages > [Introduced][5073] in GitLab 8.10. @@ -54,3 +73,4 @@ STDERR takes precedence over STDOUT. [hooks]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks [5073]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5073 +[93]: https://gitlab.com/gitlab-org/gitlab-shell/merge_requests/93 From 0fe3b3608a64e18178eb308cbd1cb3bb4a2c16ad Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 6 Dec 2016 13:17:36 -0600 Subject: [PATCH 073/140] Remove dashboard.scss --- .../stylesheets/framework/variables.scss | 6 --- app/assets/stylesheets/pages/dashboard.scss | 43 ------------------- 2 files changed, 49 deletions(-) delete mode 100644 app/assets/stylesheets/pages/dashboard.scss diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 647dcfc5187..18716813c48 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -427,12 +427,6 @@ $common-gray-dark: #444; $common-red: $gl-text-red; $common-green: $gl-text-green; - -/* -* Dashboard -*/ -$dashboard-project-access-icon-color: #888; - /* * Editor */ diff --git a/app/assets/stylesheets/pages/dashboard.scss b/app/assets/stylesheets/pages/dashboard.scss deleted file mode 100644 index 4421ed6a0b9..00000000000 --- a/app/assets/stylesheets/pages/dashboard.scss +++ /dev/null @@ -1,43 +0,0 @@ -.dashboard { - .side { - .panel { - .panel-heading { - background: $background-color; - border-top-left-radius: 0; - } - - border-top-left-radius: 0; - } - } -} - -.dashboard-search-filter { - padding: 5px; - - .search-text-input { - float: left; - @extend .col-md-2; - } - - .btn { - margin-left: 5px; - float: left; - } -} - -.project-access-icon { - margin-left: 10px; - float: left; - margin-right: 15px; - margin-bottom: 15px; - - i { - color: $dashboard-project-access-icon-color; - } -} - -.dash-project-access-icon { - float: left; - margin-right: 5px; - width: 16px; -} From b738b2a7dcb58f39e1ef2f1722a300a0a825a2a0 Mon Sep 17 00:00:00 2001 From: ja-me-sk Date: Tue, 6 Dec 2016 13:47:14 -0700 Subject: [PATCH 074/140] new DevOps report, 404s, typos correct heading level for topics Update puppet dev ops report for 2016 --- doc/university/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/university/README.md b/doc/university/README.md index 8917636c59b..12727e9d56f 100644 --- a/doc/university/README.md +++ b/doc/university/README.md @@ -5,7 +5,7 @@ GitLab University is the best place to learn about **Version Control with Git an It doesn't replace, but accompanies our great [Documentation](https://docs.gitlab.com) and [Blog Articles](https://about.gitlab.com/blog/). -Would you like to contribute to GitLab University? Then please take a look at our contribution [process](/process) for more information. +Would you like to contribute to GitLab University? Then please take a look at our contribution [process](process) for more information. ## Gitlab University Curriculum @@ -97,7 +97,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project 1. [Markdown in GitLab](../user/markdown.md) 1. [Issues and Merge Requests - Video](https://www.youtube.com/watch?v=raXvuwet78M) -1. [Due Dates and Milestones fro GitLab Issues](https://about.gitlab.com/2016/08/05/feature-highlight-set-dates-for-issues/) +1. [Due Dates and Milestones for GitLab Issues](https://about.gitlab.com/2016/08/05/feature-highlight-set-dates-for-issues/) 1. [How to Use GitLab Labels](https://about.gitlab.com/2016/08/17/using-gitlab-labels/) 1. [Applying GitLab Labels Automatically](https://about.gitlab.com/2016/08/19/applying-gitlab-labels-automatically/) 1. [GitLab Issue Board - Product Page](https://about.gitlab.com/solutions/issueboard/) @@ -147,7 +147,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project 1. [Xebia Labs: Dev Ops Terminology](https://xebialabs.com/glossary/) 1. [Xebia Labs: Periodic Table of DevOps Tools](https://xebialabs.com/periodic-table-of-devops-tools/) -1. [Puppet Labs: State of Dev Ops 2015 - Book](https://puppetlabs.com/sites/default/files/2015-state-of-devops-report.pdf) +1. [Puppet Labs: State of Dev Ops 2016 - Book](https://puppet.com/resources/white-paper/2016-state-of-devops-report) #### 3.2. Installing GitLab with Omnibus @@ -173,7 +173,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project #### 3.6 Custom Languages -1. [How to add Syntax Highlighting Support for Custom Langauges to GitLab - Video](how to add support for your favorite language to GitLab) +1. [How to add Syntax Highlighting Support for Custom Languages to GitLab - Video](https://youtu.be/6WxTMqatrrA) #### 3.7. Scalability and High Availability @@ -198,7 +198,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project --- -## 4. External Articles +### 4. External Articles 1. [2011 WSJ article by Marc Andreessen - Software is Eating the World](http://www.wsj.com/articles/SB10001424053111903480904576512250915629460) 1. [2014 Blog post by Chris Dixon - Software eats software development](http://cdixon.org/2014/04/13/software-eats-software-development/) @@ -206,7 +206,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project --- -## 5. Resources for GitLab Team Members +### 5. Resources for GitLab Team Members *Some content can only be accessed by GitLab team members* From 96f162125dabb3d3ff21cb95abf97e5af6ee5589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 7 Dec 2016 09:59:52 +0100 Subject: [PATCH 075/140] Handle an edge-case whith invitees MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the project has invitees, no group members were returned due to a `user_id NOT IN (42, NULL)` query which always returned [] since a `user_id` would be NULL, thus the condition could never match. Signed-off-by: Rémy Coutable --- .../projects/project_members_controller.rb | 14 +++- .../projects/members/group_members_spec.rb | 83 +++++++++++++++++-- 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 10bc75b4c5e..3fb8bba3cd0 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -13,7 +13,13 @@ class Projects::ProjectMembersController < Projects::ApplicationController group = @project.group if group - group_members = group.group_members.where.not(user_id: @project_members.select(:user_id)) + # We need `.where.not(user_id: nil)` here otherwise when a group has an + # invitee, it would make the following query return 0 rows since a NULL + # user_id would be present in the subquery + # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values + # FIXME: This whole logic should be moved to a finder! + non_null_user_ids = @project_members.where.not(user_id: nil).select(:user_id) + group_members = group.group_members.where.not(user_id: non_null_user_ids) group_members = group_members.non_invite unless can?(current_user, :admin_group, @group) end @@ -29,13 +35,13 @@ class Projects::ProjectMembersController < Projects::ApplicationController @group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) end - members_id = @project_members.pluck(:id) + member_ids = @project_members.pluck(:id) if group_members - members_id << group_members.pluck(:id) + member_ids += group_members.pluck(:id) end - @project_members = Member.where(id: members_id.flatten).order(access_level: :desc).page(params[:page]) + @project_members = Member.where(id: member_ids).order(access_level: :desc).page(params[:page]) @requesters = AccessRequestsFinder.new(@project).execute(current_user) diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb index 39235a1cd4f..7d0065ee2c4 100644 --- a/spec/features/projects/members/group_members_spec.rb +++ b/spec/features/projects/members/group_members_spec.rb @@ -2,20 +2,89 @@ require 'spec_helper' feature 'Projects members', feature: true do let(:user) { create(:user) } - let(:group) { create(:group) } - let(:project) { create(:project, group: group) } + let(:developer) { create(:user) } + let(:group) { create(:group, :public, :access_requestable) } + let(:project) { create(:empty_project, :public, :access_requestable, creator: user, group: group) } + let(:project_invitee) { create(:project_member, project: project, invite_token: '123', invite_email: 'test1@abc.com', user: nil) } + let(:group_invitee) { create(:group_member, group: group, invite_token: '123', invite_email: 'test2@abc.com', user: nil) } + let(:project_requester) { create(:user) } + let(:group_requester) { create(:user) } background do + project.team << [developer, :developer] group.add_owner(user) login_as(user) - visit namespace_project_project_members_path(project.namespace, project) end - it 'shows group members in list' do - expect(page).to have_selector('.group_member') + context 'with a group invitee' do + before do + group_invitee + visit namespace_project_project_members_path(project.namespace, project) + end - page.within first('.content-list .member') do - expect(page).to have_content(group.name) + scenario 'does not appear in the project members page' do + page.within first('.content-list') do + expect(page).not_to have_content('test2@abc.com') + end + end + end + + context 'with a group and a project invitee' do + before do + group_invitee + project_invitee + visit namespace_project_project_members_path(project.namespace, project) + end + + scenario 'shows the project invitee, the project developer, and the group owner' do + page.within first('.content-list') do + expect(page).to have_content('test1@abc.com') + expect(page).not_to have_content('test2@abc.com') + + # Project developer + expect(page).to have_content(developer.name) + + # Group owner + expect(page).to have_content(user.name) + expect(page).to have_content(group.name) + end + end + end + + context 'with a group requester' do + before do + group.request_access(group_requester) + visit namespace_project_project_members_path(project.namespace, project) + end + + scenario 'does not appear in the project members page' do + page.within first('.content-list') do + expect(page).not_to have_content(group_requester.name) + end + end + end + + context 'with a group and a project requesters' do + before do + group.request_access(group_requester) + project.request_access(project_requester) + visit namespace_project_project_members_path(project.namespace, project) + end + + scenario 'shows the project requester, the project developer, and the group owner' do + page.within first('.content-list') do + expect(page).to have_content(project_requester.name) + expect(page).not_to have_content(group_requester.name) + end + + page.within all('.content-list').last do + # Project developer + expect(page).to have_content(developer.name) + + # Group owner + expect(page).to have_content(user.name) + expect(page).to have_content(group.name) + end end end end From 3e7818e93a69d2f628c864e40b00ab0871bff3dc Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 7 Nov 2016 15:15:14 +0100 Subject: [PATCH 076/140] Grapify the issues API --- lib/api/helpers.rb | 16 -- lib/api/issues.rb | 263 +++++++++++++------------------ spec/requests/api/issues_spec.rb | 26 +-- 3 files changed, 126 insertions(+), 179 deletions(-) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7f94ede7940..898ca470a30 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -217,22 +217,6 @@ module API end end - def issuable_order_by - if params["order_by"] == 'updated_at' - 'updated_at' - else - 'created_at' - end - end - - def issuable_sort - if params["sort"] == 'asc' - :asc - else - :desc - end - end - def filter_by_iid(items, iid) items.where(iid: iid) end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 26c8f2fecd0..c9124649cbb 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -1,6 +1,7 @@ module API - # Issues API class Issues < Grape::API + include PaginationParams + before { authenticate! } helpers do @@ -20,77 +21,68 @@ module API issues.includes(:milestone).where('milestones.title' => milestone) end - def issue_params - new_params = declared(params, include_parent_namespace: false, include_missing: false).to_h - new_params = new_params.with_indifferent_access - new_params.delete(:id) - new_params.delete(:issue_id) + params :issues_params do + optional :labels, type: String, desc: 'Comma-separated list of label names' + optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at', + desc: 'Return issues ordered by `created_at` or `updated_at` fields.' + optional :sort, type: String, values: %w[asc desc], default: 'desc', + desc: 'Return issues sorted in `asc` or `desc` order.' + use :pagination + end - new_params + params :issue_params do + optional :description, type: String, desc: 'The description of an issue' + optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue' + optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue' + optional :labels, type: String, desc: 'Comma-separated list of label names' + optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY' + optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential' + optional :state_event, type: String, values: %w[open close], + desc: 'State of the issue' end end resource :issues do - # Get currently authenticated user's issues - # - # Parameters: - # state (optional) - Return "opened" or "closed" issues - # labels (optional) - Comma-separated list of label names - # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` - # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - # - # Example Requests: - # GET /issues - # GET /issues?state=opened - # GET /issues?state=closed - # GET /issues?labels=foo - # GET /issues?labels=foo,bar - # GET /issues?labels=foo,bar&state=opened + desc "Get currently authenticated user's issues" do + success Entities::Issue + end + params do + optional :state, type: String, values: %w[opened closed all], default: 'all', + desc: 'Return opened, closed, or all issues' + use :issues_params + end get do issues = current_user.issues.inc_notes_with_associations - issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? + issues = filter_issues_state(issues, params[:state]) issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? - issues = issues.reorder(issuable_order_by => issuable_sort) + issues = issues.reorder(params[:order_by] => params[:sort]) present paginate(issues), with: Entities::Issue, current_user: current_user end end + params do + requires :id, type: String, desc: 'The ID of a group' + end resource :groups do - # Get a list of group issues - # - # Parameters: - # id (required) - The ID of a group - # state (optional) - Return "opened" or "closed" issues - # labels (optional) - Comma-separated list of label names - # milestone (optional) - Milestone title - # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` - # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - # - # Example Requests: - # GET /groups/:id/issues - # GET /groups/:id/issues?state=opened - # GET /groups/:id/issues?state=closed - # GET /groups/:id/issues?labels=foo - # GET /groups/:id/issues?labels=foo,bar - # GET /groups/:id/issues?labels=foo,bar&state=opened - # GET /groups/:id/issues?milestone=1.0.0 - # GET /groups/:id/issues?milestone=1.0.0&state=closed + desc 'Get a list of group issues' do + success Entities::Issue + end + params do + optional :state, type: String, values: %w[opened closed all], default: 'opened', + desc: 'Return opened, closed, or all issues' + use :issues_params + end get ":id/issues" do - group = find_group!(params[:id]) + group = find_group!(params.delete(:id)) - params[:state] ||= 'opened' params[:group_id] = group.id params[:milestone_title] = params.delete(:milestone) params[:label_name] = params.delete(:labels) - if params[:order_by] || params[:sort] - # The Sortable concern takes 'created_desc', not 'created_at_desc' (for example) - params[:sort] = "#{issuable_order_by.sub('_at', '')}_#{issuable_sort}" - end - issues = IssuesFinder.new(current_user, params).execute + issues = issues.reorder(params[:order_by] => params[:sort]) present paginate(issues), with: Entities::Issue, current_user: current_user end end @@ -98,32 +90,19 @@ module API params do requires :id, type: String, desc: 'The ID of a project' end - resource :projects do - # Get a list of project issues - # - # Parameters: - # id (required) - The ID of a project - # iid (optional) - Return the project issue having the given `iid` - # state (optional) - Return "opened" or "closed" issues - # labels (optional) - Comma-separated list of label names - # milestone (optional) - Milestone title - # order_by (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` - # sort (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - # - # Example Requests: - # GET /projects/:id/issues - # GET /projects/:id/issues?state=opened - # GET /projects/:id/issues?state=closed - # GET /projects/:id/issues?labels=foo - # GET /projects/:id/issues?labels=foo,bar - # GET /projects/:id/issues?labels=foo,bar&state=opened - # GET /projects/:id/issues?milestone=1.0.0 - # GET /projects/:id/issues?milestone=1.0.0&state=closed - # GET /issues?iid=42 + desc 'Get a list of project issues' do + success Entities::Issue + end + params do + optional :state, type: String, values: %w[opened closed all], default: 'all', + desc: 'Return opened, closed, or all issues' + optional :iid, type: Integer, desc: 'The IID of the issue' + use :issues_params + end get ":id/issues" do issues = IssuesFinder.new(current_user, project_id: user_project.id).execute.inc_notes_with_associations - issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? + issues = filter_issues_state(issues, params[:state]) issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? issues = filter_by_iid(issues, params[:iid]) unless params[:iid].nil? @@ -131,59 +110,49 @@ module API issues = filter_issues_milestone(issues, params[:milestone]) end - issues = issues.reorder(issuable_order_by => issuable_sort) - + issues = issues.reorder(params[:order_by] => params[:sort]) present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project end - # Get a single project issue - # - # Parameters: - # id (required) - The ID of a project - # issue_id (required) - The ID of a project issue - # Example Request: - # GET /projects/:id/issues/:issue_id + desc 'Get a single project issue' do + success Entities::Issue + end + params do + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + end get ":id/issues/:issue_id" do - @issue = find_project_issue(params[:issue_id]) - present @issue, with: Entities::Issue, current_user: current_user, project: user_project + issue = find_project_issue(params[:issue_id]) + present issue, with: Entities::Issue, current_user: current_user, project: user_project end - # Create a new project issue - # - # Parameters: - # id (required) - The ID of a project - # title (required) - The title of an issue - # description (optional) - The description of an issue - # assignee_id (optional) - The ID of a user to assign issue - # milestone_id (optional) - The ID of a milestone to assign issue - # labels (optional) - The labels of an issue - # created_at (optional) - Date time string, ISO 8601 formatted - # due_date (optional) - Date time string in the format YEAR-MONTH-DAY - # confidential (optional) - Boolean parameter if the issue should be confidential - # merge_request_for_resolving_discussions (optional) - The IID of a merge request for which to resolve discussions - # Example Request: - # POST /projects/:id/issues + desc 'Create a new project issue' do + success Entities::Issue + end + params do + requires :title, type: String, desc: 'The title of an issue' + optional :created_at, type: DateTime, + desc: 'Date time when the issue was created. Available only for admins and project owners.' + optional :merge_request_for_resolving_discussions, type: Integer, + desc: 'The IID of a merge request for which to resolve discussions' + use :issue_params + end post ':id/issues' do - required_attributes! [:title] + # Setting created_at time only allowed for admins and project owners + unless current_user.admin? || user_project.owner == current_user + params.delete(:created_at) + end - keys = [:title, :description, :assignee_id, :milestone_id, :due_date, :confidential, :labels, :merge_request_for_resolving_discussions] - keys << :created_at if current_user.admin? || user_project.owner == current_user - attrs = attributes_for_keys(keys) - - attrs[:labels] = params[:labels] if params[:labels] + issue_params = declared_params(include_missing: false) if merge_request_iid = params[:merge_request_for_resolving_discussions] - attrs[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id). + issue_params[:merge_request_for_resolving_discussions] = MergeRequestsFinder.new(current_user, project_id: user_project.id). execute. find_by(iid: merge_request_iid) end - # Convert and filter out invalid confidential flags - attrs['confidential'] = to_boolean(attrs['confidential']) - attrs.delete('confidential') if attrs['confidential'].nil? - - issue = ::Issues::CreateService.new(user_project, current_user, attrs.merge(request: request, api: true)).execute - + issue = ::Issues::CreateService.new(user_project, + current_user, + issue_params.merge(request: request, api: true)).execute if issue.spam? render_api_error!({ error: 'Spam detected' }, 400) end @@ -199,31 +168,26 @@ module API success Entities::Issue end params do - requires :id, type: String, desc: 'The ID of a project' - requires :issue_id, type: Integer, desc: "The ID of a project issue" - optional :title, type: String, desc: 'The new title of the issue' - optional :description, type: String, desc: 'The description of an issue' - optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue' - optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue' - optional :labels, type: String, desc: 'The labels of an issue' - optional :state_event, type: String, values: ['close', 'reopen'], desc: 'The state event of an issue' - # TODO 9.0, use the Grape DateTime type here - optional :updated_at, type: String, desc: 'Date time string, ISO 8601 formatted' - optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY' - # TODO 9.0, use the Grape boolean type here - optional :confidential, type: String, desc: 'Boolean parameter if the issue should be confidential' + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + optional :title, type: String, desc: 'The title of an issue' + optional :updated_at, type: DateTime, + desc: 'Date time when the issue was updated. Available only for admins and project owners.' + use :issue_params + at_least_one_of :title, :description, :assignee_id, :milestone_id, + :labels, :created_at, :due_date, :confidential, :state_event end put ':id/issues/:issue_id' do - issue = user_project.issues.find(params[:issue_id]) + issue = user_project.issues.find(params.delete(:issue_id)) authorize! :update_issue, issue - # Convert and filter out invalid confidential flags - params[:confidential] = to_boolean(params[:confidential]) - params.delete(:confidential) if params[:confidential].nil? + # Setting created_at time only allowed for admins and project owners + unless current_user.admin? || user_project.owner == current_user + params.delete(:updated_at) + end - params.delete(:updated_at) unless current_user.admin? || user_project.owner == current_user - - issue = ::Issues::UpdateService.new(user_project, current_user, issue_params).execute(issue) + issue = ::Issues::UpdateService.new(user_project, + current_user, + declared_params(include_missing: false)).execute(issue) if issue.valid? present issue, with: Entities::Issue, current_user: current_user, project: user_project @@ -232,19 +196,19 @@ module API end end - # Move an existing issue - # - # Parameters: - # id (required) - The ID of a project - # issue_id (required) - The ID of a project issue - # to_project_id (required) - The ID of the new project - # Example Request: - # POST /projects/:id/issues/:issue_id/move + desc 'Move an existing issue' do + success Entities::Issue + end + params do + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + requires :to_project_id, type: Integer, desc: 'The ID of the new project' + end post ':id/issues/:issue_id/move' do - required_attributes! [:to_project_id] + issue = user_project.issues.find_by(id: params[:issue_id]) + not_found!('Issue') unless issue - issue = user_project.issues.find(params[:issue_id]) - new_project = Project.find(params[:to_project_id]) + new_project = Project.find_by(id: params[:to_project_id]) + not_found!('Project') unless new_project begin issue = ::Issues::MoveService.new(user_project, current_user).execute(issue, new_project) @@ -254,16 +218,13 @@ module API end end - # - # Delete a project issue - # - # Parameters: - # id (required) - The ID of a project - # issue_id (required) - The ID of a project issue - # Example Request: - # DELETE /projects/:id/issues/:issue_id + desc 'Delete a project issue' + params do + requires :issue_id, type: Integer, desc: 'The ID of a project issue' + end delete ":id/issues/:issue_id" do issue = user_project.issues.find_by(id: params[:issue_id]) + not_found!('Issue') unless issue authorize!(:destroy_issue, issue) issue.destroy diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 553983575c4..5c80dd98dc7 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -72,13 +72,6 @@ describe API::Issues, api: true do expect(json_response.last).to have_key('web_url') end - it "adds pagination headers and keep query params" do - get api("/issues?state=closed&per_page=3", user) - expect(response.headers['Link']).to eq( - '; rel="first", ; rel="last"' % [user.private_token, user.private_token] - ) - end - it 'returns an array of closed issues' do get api('/issues?state=closed', user) expect(response).to have_http_status(200) @@ -649,9 +642,8 @@ describe API::Issues, api: true do post api("/projects/#{project.id}/issues", user), title: 'new issue', confidential: 'foo' - expect(response).to have_http_status(201) - expect(json_response['title']).to eq('new issue') - expect(json_response['confidential']).to be_falsy + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('confidential is invalid') end it "sends notifications for subscribers of newly added labels" do @@ -862,8 +854,8 @@ describe API::Issues, api: true do put api("/projects/#{project.id}/issues/#{confidential_issue.id}", user), confidential: 'foo' - expect(response).to have_http_status(200) - expect(json_response['confidential']).to be_truthy + expect(response).to have_http_status(400) + expect(json_response['error']).to eq('confidential is invalid') end end end @@ -985,6 +977,14 @@ describe API::Issues, api: true do expect(json_response['state']).to eq 'opened' end end + + context 'when issue does not exist' do + it 'returns 404 when trying to move an issue' do + delete api("/projects/#{project.id}/issues/123", user) + + expect(response).to have_http_status(404) + end + end end describe '/projects/:id/issues/:issue_id/move' do @@ -1033,6 +1033,7 @@ describe API::Issues, api: true do to_project_id: target_project.id expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Issue Not Found') end end @@ -1042,6 +1043,7 @@ describe API::Issues, api: true do to_project_id: target_project.id expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Project Not Found') end end From dec384cf23787e156eac8d633f70fb40479a5283 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 7 Dec 2016 11:31:01 +0100 Subject: [PATCH 077/140] Update outdated visible content spec descriptions --- spec/models/repository_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index d9b0e63eeb6..b5a42edd192 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1176,7 +1176,7 @@ describe Repository, models: true do end describe '#after_create_branch' do - it 'flushes the visible content cache' do + it 'expires the branch caches' do expect(repository).to receive(:expire_branches_cache) repository.after_create_branch @@ -1184,7 +1184,7 @@ describe Repository, models: true do end describe '#after_remove_branch' do - it 'flushes the visible content cache' do + it 'expires the branch caches' do expect(repository).to receive(:expire_branches_cache) repository.after_remove_branch From 9e307b93b7c57a14de6e425566f88511024859a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 2 Dec 2016 14:33:29 +0100 Subject: [PATCH 078/140] Allow public access to some Tag API endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- changelogs/unreleased/public-tags-api.yml | 4 ++ doc/api/tags.md | 7 ++- lib/api/tags.rb | 1 - spec/requests/api/tags_spec.rb | 55 ++++++++++++++++++++--- 4 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/public-tags-api.yml diff --git a/changelogs/unreleased/public-tags-api.yml b/changelogs/unreleased/public-tags-api.yml new file mode 100644 index 00000000000..f5e844470b2 --- /dev/null +++ b/changelogs/unreleased/public-tags-api.yml @@ -0,0 +1,4 @@ +--- +title: Allow public access to some Tag API endpoints +merge_request: +author: diff --git a/doc/api/tags.md b/doc/api/tags.md index 14573d48fe4..7f78ffc2390 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -2,7 +2,9 @@ ## List project repository tags -Get a list of repository tags from a project, sorted by name in reverse alphabetical order. +Get a list of repository tags from a project, sorted by name in reverse +alphabetical order. This endpoint can be accessed without authentication if the +repository is publicly accessible. ``` GET /projects/:id/repository/tags @@ -40,7 +42,8 @@ Parameters: ## Get a single repository tag -Get a specific repository tag determined by its name. +Get a specific repository tag determined by its name. This endpoint can be +accessed without authentication if the repository is publicly accessible. ``` GET /projects/:id/repository/tags/:tag_name diff --git a/lib/api/tags.rb b/lib/api/tags.rb index cd33f9a9903..5b345db3a41 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -1,7 +1,6 @@ module API # Git Tags API class Tags < Grape::API - before { authenticate! } before { authorize! :download_code, user_project } params do diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 06fa94fae87..a1c32ae65ba 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -15,6 +15,31 @@ describe API::Tags, api: true do let(:tag_name) { project.repository.tag_names.sort.reverse.first } let(:description) { 'Awesome release!' } + shared_examples_for 'repository tags' do + it 'returns the repository tags' do + get api("/projects/#{project.id}/repository/tags", current_user) + + expect(response).to have_http_status(200) + + first_tag = json_response.first + + expect(first_tag['name']).to eq(tag_name) + end + end + + context 'when unauthenticated' do + it_behaves_like 'repository tags' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end + + context 'when authenticated' do + it_behaves_like 'repository tags' do + let(:current_user) { user } + end + end + context 'without releases' do it "returns an array of project tags" do get api("/projects/#{project.id}/repository/tags", user) @@ -45,17 +70,33 @@ describe API::Tags, api: true do describe 'GET /projects/:id/repository/tags/:tag_name' do let(:tag_name) { project.repository.tag_names.sort.reverse.first } - it 'returns a specific tag' do - get api("/projects/#{project.id}/repository/tags/#{tag_name}", user) + shared_examples_for 'repository tag' do + it 'returns the repository tag' do + get api("/projects/#{project.id}/repository/tags/#{tag_name}", current_user) - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(tag_name) + expect(response).to have_http_status(200) + + expect(json_response['name']).to eq(tag_name) + end + + it 'returns 404 for an invalid tag name' do + get api("/projects/#{project.id}/repository/tags/foobar", current_user) + + expect(response).to have_http_status(404) + end end - it 'returns 404 for an invalid tag name' do - get api("/projects/#{project.id}/repository/tags/foobar", user) + context 'when unauthenticated' do + it_behaves_like 'repository tag' do + let(:project) { create(:project, :public) } + let(:current_user) { nil } + end + end - expect(response).to have_http_status(404) + context 'when authenticated' do + it_behaves_like 'repository tag' do + let(:current_user) { user } + end end end From 9cc19d9b2ce6af378dce67260c13f208ddc40bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 5 Dec 2016 16:00:26 +0100 Subject: [PATCH 079/140] Remove wrong '.builds-feature' class from the MR settings fieldset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .../_merge_request_merge_settings.html.haml | 15 ++++ .../_merge_request_settings.html.haml | 26 +++---- app/views/projects/edit.html.haml | 3 +- ...ttings-hidden-when-builds-are-disabled.yml | 4 ++ .../settings/merge_requests_settings_spec.rb | 70 +++++++++++++++++++ 5 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 app/views/projects/_merge_request_merge_settings.html.haml create mode 100644 changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml create mode 100644 spec/features/projects/settings/merge_requests_settings_spec.rb diff --git a/app/views/projects/_merge_request_merge_settings.html.haml b/app/views/projects/_merge_request_merge_settings.html.haml new file mode 100644 index 00000000000..afe2fd7fd7b --- /dev/null +++ b/app/views/projects/_merge_request_merge_settings.html.haml @@ -0,0 +1,15 @@ +- form = local_assigns.fetch(:form) + +.form-group + .checkbox.builds-feature + = form.label :only_allow_merge_if_build_succeeds do + = form.check_box :only_allow_merge_if_build_succeeds + %strong Only allow merge requests to be merged if the build succeeds + %br + %span.descr + Builds need to be configured to enable this feature. + = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_build_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds') + .checkbox + = form.label :only_allow_merge_if_all_discussions_are_resolved do + = form.check_box :only_allow_merge_if_all_discussions_are_resolved + %strong Only allow merge requests to be merged if all discussions are resolved diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml index 6e143c4b570..818010bc7d3 100644 --- a/app/views/projects/_merge_request_settings.html.haml +++ b/app/views/projects/_merge_request_settings.html.haml @@ -1,18 +1,8 @@ -.merge-requests-feature - %fieldset.builds-feature - %hr - %h5.prepend-top-0 - Merge Requests - .form-group - .checkbox - = f.label :only_allow_merge_if_build_succeeds do - = f.check_box :only_allow_merge_if_build_succeeds - %strong Only allow merge requests to be merged if the build succeeds - %br - %span.descr - Builds need to be configured to enable this feature. - = link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_build_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds') - .checkbox - = f.label :only_allow_merge_if_all_discussions_are_resolved do - = f.check_box :only_allow_merge_if_all_discussions_are_resolved - %strong Only allow merge requests to be merged if all discussions are resolved +- form = local_assigns.fetch(:form) + +%fieldset.features.merge-requests-feature.append-bottom-default + %hr + %h5.prepend-top-0 + Merge Requests + + = render 'projects/merge_request_merge_settings', form: form diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 3a5af2723c6..01cd8fa0938 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -112,7 +112,8 @@ %span.descr Enable Container Registry for this project = link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank' - = render 'merge_request_settings', f: f + = render 'merge_request_settings', form: f + %hr %fieldset.features.append-bottom-default %h5.prepend-top-0 diff --git a/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml b/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml new file mode 100644 index 00000000000..a7576e2cbdb --- /dev/null +++ b/changelogs/unreleased/25171-fix-mr-features-settings-hidden-when-builds-are-disabled.yml @@ -0,0 +1,4 @@ +--- +title: Remove wrong '.builds-feature' class from the MR settings fieldset +merge_request: 7930 +author: diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb new file mode 100644 index 00000000000..4bfaa499272 --- /dev/null +++ b/spec/features/projects/settings/merge_requests_settings_spec.rb @@ -0,0 +1,70 @@ +require 'spec_helper' + +feature 'Project settings > Merge Requests', feature: true, js: true do + include GitlabRoutingHelper + + let(:project) { create(:empty_project, :public) } + let(:user) { create(:user) } + + background do + project.team << [user, :master] + login_as(user) + end + + context 'when Merge Request and Builds are initially enabled' do + before do + project.project_feature.update_attribute('merge_requests_access_level', ProjectFeature::ENABLED) + end + + context 'when Builds are initially enabled' do + before do + project.project_feature.update_attribute('builds_access_level', ProjectFeature::ENABLED) + visit edit_project_path(project) + end + + scenario 'shows the Merge Requests settings' do + expect(page).to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + + select 'Disabled', from: "project_project_feature_attributes_merge_requests_access_level" + + expect(page).not_to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') + end + end + + context 'when Builds are initially disabled' do + before do + project.project_feature.update_attribute('builds_access_level', ProjectFeature::DISABLED) + visit edit_project_path(project) + end + + scenario 'shows the Merge Requests settings that do not depend on Builds feature' do + expect(page).not_to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + + select 'Everyone with access', from: "project_project_feature_attributes_builds_access_level" + + expect(page).to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + end + end + end + + context 'when Merge Request are initially disabled' do + before do + project.project_feature.update_attribute('merge_requests_access_level', ProjectFeature::DISABLED) + visit edit_project_path(project) + end + + scenario 'does not show the Merge Requests settings' do + expect(page).not_to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).not_to have_content('Only allow merge requests to be merged if all discussions are resolved') + + select 'Everyone with access', from: "project_project_feature_attributes_merge_requests_access_level" + + expect(page).to have_content('Only allow merge requests to be merged if the build succeeds') + expect(page).to have_content('Only allow merge requests to be merged if all discussions are resolved') + end + end +end From c4e46c5740426f656c019b51b9731ea8a16f42d0 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 7 Dec 2016 12:47:06 +0100 Subject: [PATCH 080/140] Fix success status --- app/models/ci/pipeline.rb | 3 ++- app/models/concerns/has_status.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index d14825c082a..fda8228a1e9 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -105,7 +105,8 @@ module Ci end def stages_name - statuses.order(:stage_idx).distinct.pluck(:stage) + statuses.order(:stage_idx).distinct. + pluck(:stage, :stage_idx).map(&:first) end def stages diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index 43f312d319e..90432fc4050 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -26,7 +26,7 @@ module HasStatus WHEN (#{builds})=(#{skipped}) THEN 'skipped' WHEN (#{builds})=(#{success}) THEN 'success' WHEN (#{builds})=(#{created}) THEN 'created' - WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'skipped' + WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success' WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled' WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running' From 93c72e0f71919968972e874519e01370edcce022 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 7 Dec 2016 13:14:45 +0100 Subject: [PATCH 081/140] Add Ci::Status::Factory --- lib/gitlab/ci/status/factory.rb | 43 +++++++++++++++++++++++ lib/gitlab/ci/status/pipeline/factory.rb | 30 +++------------- lib/gitlab/ci/status/stage/factory.rb | 28 ++------------- spec/lib/gitlab/ci/status/factory_spec.rb | 26 ++++++++++++++ 4 files changed, 76 insertions(+), 51 deletions(-) create mode 100644 lib/gitlab/ci/status/factory.rb create mode 100644 spec/lib/gitlab/ci/status/factory_spec.rb diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb new file mode 100644 index 00000000000..b2f896f2211 --- /dev/null +++ b/lib/gitlab/ci/status/factory.rb @@ -0,0 +1,43 @@ +module Gitlab + module Ci + module Status + class Factory + attr_reader :subject + + def initialize(subject) + @subject = subject + end + + def fabricate! + if extended_status + extended_status.new(core_status) + else + core_status + end + end + + private + + def subject_status + @subject_status ||= subject.status + end + + def core_status + Gitlab::Ci::Status + .const_get(subject_status.capitalize) + .new(subject) + end + + def extended_status + @extended ||= extended_statuses.find do |status| + status.matches?(subject) + end + end + + def extended_statuses + [] + end + end + end + end +end diff --git a/lib/gitlab/ci/status/pipeline/factory.rb b/lib/gitlab/ci/status/pipeline/factory.rb index 71d27bf7cf5..4ac4ec671d0 100644 --- a/lib/gitlab/ci/status/pipeline/factory.rb +++ b/lib/gitlab/ci/status/pipeline/factory.rb @@ -2,35 +2,15 @@ module Gitlab module Ci module Status module Pipeline - class Factory - EXTENDED_STATUSES = [Pipeline::SuccessWithWarnings] - - def initialize(pipeline) - @pipeline = pipeline - @status = pipeline.status || :created - end - - def fabricate! - if extended_status - extended_status.new(core_status) - else - core_status - end - end - + class Factory < Status::Factory private - def core_status - Gitlab::Ci::Status - .const_get(@status.capitalize) - .new(@pipeline) - .extend(Status::Pipeline::Common) + def extended_statuses + [Pipeline::SuccessWithWarnings] end - def extended_status - @extended ||= EXTENDED_STATUSES.find do |status| - status.matches?(@pipeline) - end + def core_status + super.extend(Status::Pipeline::Common) end end end diff --git a/lib/gitlab/ci/status/stage/factory.rb b/lib/gitlab/ci/status/stage/factory.rb index a2f7ad81d39..c6522d5ada1 100644 --- a/lib/gitlab/ci/status/stage/factory.rb +++ b/lib/gitlab/ci/status/stage/factory.rb @@ -2,35 +2,11 @@ module Gitlab module Ci module Status module Stage - class Factory - EXTENDED_STATUSES = [] - - def initialize(stage) - @stage = stage - @status = stage.status || :created - end - - def fabricate! - if extended_status - extended_status.new(core_status) - else - core_status - end - end - + class Factory < Status::Factory private def core_status - Gitlab::Ci::Status - .const_get(@status.capitalize) - .new(@stage) - .extend(Status::Stage::Common) - end - - def extended_status - @extended ||= EXTENDED_STATUSES.find do |status| - status.matches?(@stage) - end + super.extend(Status::Stage::Common) end end end diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb new file mode 100644 index 00000000000..8ebdbddb001 --- /dev/null +++ b/spec/lib/gitlab/ci/status/factory_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe Gitlab::Ci::Status::Factory do + let(:object) { double(status: :created) } + + subject do + described_class.new(object) + end + + let(:status) do + subject.fabricate! + end + + context 'when object has a core status' do + HasStatus::AVAILABLE_STATUSES.each do |core_status| + context "when core status is #{core_status}" do + let(:object) { double(status: core_status) } + + it "fabricates a core status #{core_status}" do + expect(status).to be_a( + Gitlab::Ci::Status.const_get(core_status.capitalize)) + end + end + end + end +end From eb8a713caac13de61321970c4dc2715e1c0929f2 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 7 Dec 2016 09:19:08 +0000 Subject: [PATCH 082/140] Fixed timeago re-rendering every element Fixes a performance regression that was causing timeago to get re-rendered on every element & therefore adding a lot of new setTimeouts --- app/assets/javascripts/lib/utils/datetime_utility.js | 4 +++- changelogs/unreleased/timeago-perf-fix.yml | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/timeago-perf-fix.yml diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 963d2851e5f..e8e502694d6 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -29,7 +29,7 @@ setTimeago = true; } - $timeagoEls.each(function() { + $timeagoEls.filter(':not([data-timeago-rendered])').each(function() { var $el = $(this); $el.attr('title', gl.utils.formatDate($el.attr('datetime'))); @@ -39,6 +39,8 @@ template: '

' }); } + + $el.attr('data-timeago-rendered', true); gl.utils.renderTimeago($el); }); }; diff --git a/changelogs/unreleased/timeago-perf-fix.yml b/changelogs/unreleased/timeago-perf-fix.yml new file mode 100644 index 00000000000..265e7db29a9 --- /dev/null +++ b/changelogs/unreleased/timeago-perf-fix.yml @@ -0,0 +1,4 @@ +--- +title: Fixed timeago re-rendering every timeago +merge_request: +author: From 2fc1e643a7d8fb2fb4f9df49a2347d4f82776e99 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Wed, 7 Dec 2016 12:39:19 +0000 Subject: [PATCH 083/140] Fix Backup::Manager#remove_old --- lib/backup/manager.rb | 19 ++-- spec/lib/gitlab/backup/manager_spec.rb | 127 +++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 spec/lib/gitlab/backup/manager_spec.rb diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 96c20100541..7e6537e3d9e 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -82,16 +82,17 @@ module Backup removed = 0 Dir.chdir(Gitlab.config.backup.path) do - file_list = Dir.glob('*_gitlab_backup.tar') - file_list.map! do |path_string| - if path_string =~ /(\d+)(?:_\d{4}_\d{2}_\d{2})?_gitlab_backup\.tar/ - { timestamp: $1.to_i, path: path_string } - end - end - file_list.sort.each do |file| - if Time.at(file[:timestamp]) < (Time.now - keep_time) - if Kernel.system(*%W(rm #{file[:path]})) + Dir.glob('*_gitlab_backup.tar').each do |file| + next unless file =~ /(\d+)(?:_\d{4}_\d{2}_\d{2})?_gitlab_backup\.tar/ + + timestamp = $1.to_i + + if Time.at(timestamp) < (Time.now - keep_time) + begin + FileUtils.rm(file) removed += 1 + rescue => e + $progress.puts "Deleting #{file} failed: #{e.message}".color(:red) end end end diff --git a/spec/lib/gitlab/backup/manager_spec.rb b/spec/lib/gitlab/backup/manager_spec.rb new file mode 100644 index 00000000000..1b749d1bd39 --- /dev/null +++ b/spec/lib/gitlab/backup/manager_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +describe Backup::Manager, lib: true do + describe '#remove_old' do + let(:progress) { StringIO.new } + + let(:files) do + [ + '1451606400_2016_01_01_gitlab_backup.tar', + '1451520000_2015_12_31_gitlab_backup.tar', + '1450742400_2015_12_22_gitlab_backup.tar', + '1449878400_gitlab_backup.tar', + '1449014400_gitlab_backup.tar', + 'manual_gitlab_backup.tar' + ] + end + + before do + allow(Dir).to receive(:chdir).and_yield + allow(Dir).to receive(:glob).and_return(files) + allow(FileUtils).to receive(:rm) + allow(Time).to receive(:now).and_return(Time.utc(2016)) + + allow(progress).to receive(:puts) + allow(progress).to receive(:print) + + allow_any_instance_of(String).to receive(:color) do |string, _color| + string + end + + @old_progress = $progress # rubocop:disable Style/GlobalVars + $progress = progress # rubocop:disable Style/GlobalVars + end + + after do + $progress = @old_progress # rubocop:disable Style/GlobalVars + end + + context 'when keep_time is zero' do + before do + allow(Gitlab.config.backup).to receive(:keep_time).and_return(0) + + subject.remove_old + end + + it 'removes no files' do + expect(FileUtils).not_to have_received(:rm) + end + + it 'prints a skipped message' do + expect(progress).to have_received(:puts).with('skipping') + end + end + + context 'when there are no files older than keep_time' do + before do + allow(Gitlab.config.backup).to receive(:keep_time).and_return(2592000) + + subject.remove_old + end + + it 'removes no files' do + expect(FileUtils).not_to have_received(:rm) + end + + it 'prints a done message' do + expect(progress).to have_received(:puts).with('done. (0 removed)') + end + end + + context 'when keep_time is set to remove files' do + before do + allow(Gitlab.config.backup).to receive(:keep_time).and_return(1) + + subject.remove_old + end + + it 'removes matching files with a human-readable timestamp' do + expect(FileUtils).to have_received(:rm).with(files[1]) + expect(FileUtils).to have_received(:rm).with(files[2]) + end + + it 'removes matching files without a human-readable timestamp' do + expect(FileUtils).to have_received(:rm).with(files[3]) + expect(FileUtils).to have_received(:rm).with(files[4]) + end + + it 'does not remove files that are not old enough' do + expect(FileUtils).not_to have_received(:rm).with(files[0]) + end + + it 'does not remove non-matching files' do + expect(FileUtils).not_to have_received(:rm).with(files[5]) + end + + it 'prints a done message' do + expect(progress).to have_received(:puts).with('done. (4 removed)') + end + end + + context 'when removing a file fails' do + let(:file) { files[1] } + let(:message) { "Permission denied @ unlink_internal - #{file}" } + + before do + allow(Gitlab.config.backup).to receive(:keep_time).and_return(1) + allow(FileUtils).to receive(:rm).with(file).and_raise(Errno::EACCES, message) + + subject.remove_old + end + + it 'removes the remaining expected files' do + expect(FileUtils).to have_received(:rm).with(files[2]) + expect(FileUtils).to have_received(:rm).with(files[3]) + expect(FileUtils).to have_received(:rm).with(files[4]) + end + + it 'sets the correct removed count' do + expect(progress).to have_received(:puts).with('done. (3 removed)') + end + + it 'prints the error from file that could not be removed' do + expect(progress).to have_received(:puts).with(a_string_matching(message)) + end + end + end +end From 8b379465a5be48c8062379a3dea8e58110c52d87 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Mon, 21 Nov 2016 11:42:10 +0000 Subject: [PATCH 084/140] Reenables /user API request to return private-token if user is admin and requested with sudo --- lib/api/entities.rb | 1 + lib/api/users.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 899d68bc6c7..1dd191161ef 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -32,6 +32,7 @@ module API expose :can_create_project?, as: :can_create_project expose :two_factor_enabled?, as: :two_factor_enabled expose :external + expose :private_token, if: lambda { |user, options| user.is_admin? && options[:sudo_identifier] } end class UserLogin < UserFull diff --git a/lib/api/users.rb b/lib/api/users.rb index bc2362aa72e..b3870e0c7c8 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -353,7 +353,7 @@ module API success Entities::UserFull end get do - present current_user, with: Entities::UserFull + present current_user, with: Entities::UserFull, sudo_identifier: sudo_identifier end desc "Get the currently authenticated user's SSH keys" do From 3ed96afc47c481db4f8c0a6581602abaee920808 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Mon, 21 Nov 2016 12:59:37 +0000 Subject: [PATCH 085/140] adds impersonator variable and makes sudo usage overall more clear --- ...24537-reenable-private-token-with-sudo.yml | 5 + doc/api/users.md | 51 ++++- lib/api/entities.rb | 7 +- lib/api/helpers.rb | 13 +- lib/api/session.rb | 4 +- lib/api/users.rb | 16 +- spec/fixtures/api/schemas/user/login.json | 37 ++++ spec/fixtures/api/schemas/user/public.json | 79 +++++++ spec/requests/api/api_helpers_spec.rb | 197 ++++++++++++------ spec/requests/api/users_spec.rb | 79 +++++-- 10 files changed, 389 insertions(+), 99 deletions(-) create mode 100644 changelogs/unreleased/24537-reenable-private-token-with-sudo.yml create mode 100644 spec/fixtures/api/schemas/user/login.json create mode 100644 spec/fixtures/api/schemas/user/public.json diff --git a/changelogs/unreleased/24537-reenable-private-token-with-sudo.yml b/changelogs/unreleased/24537-reenable-private-token-with-sudo.yml new file mode 100644 index 00000000000..9fbbaeb914d --- /dev/null +++ b/changelogs/unreleased/24537-reenable-private-token-with-sudo.yml @@ -0,0 +1,5 @@ +--- +title: Reenables /user API request to return private-token if user is admin and request + is made with sudo +merge_request: 7615 +author: diff --git a/doc/api/users.md b/doc/api/users.md index 52a6b691610..28b6c7bd491 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -291,7 +291,9 @@ Parameters: - `id` (required) - The ID of the user -## Current user +## User + +### For normal users Gets currently authenticated user. @@ -335,6 +337,53 @@ GET /user } ``` +### For admins + +Parameters: + +- `sudo` (required) - the ID of a user + +``` +GET /user +``` + +```json +{ + "id": 1, + "username": "john_smith", + "email": "john@example.com", + "name": "John Smith", + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", + "web_url": "http://localhost:3000/john_smith", + "created_at": "2012-05-23T08:00:58Z", + "is_admin": false, + "bio": null, + "location": null, + "skype": "", + "linkedin": "", + "twitter": "", + "website_url": "", + "organization": "", + "last_sign_in_at": "2012-06-01T11:41:01Z", + "confirmed_at": "2012-05-23T09:05:22Z", + "theme_id": 1, + "color_scheme_id": 2, + "projects_limit": 100, + "current_sign_in_at": "2012-06-02T06:36:55Z", + "identities": [ + {"provider": "github", "extern_uid": "2435223452345"}, + {"provider": "bitbucket", "extern_uid": "john_smith"}, + {"provider": "google_oauth2", "extern_uid": "8776128412476123468721346"} + ], + "can_create_group": true, + "can_create_project": true, + "two_factor_enabled": true, + "external": false, + "private_token": "dd34asd13as" +} +``` + ## List SSH keys Get a list of currently authenticated user's SSH keys. diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 1dd191161ef..006d5f9f44e 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -22,7 +22,7 @@ module API expose :provider, :extern_uid end - class UserFull < User + class UserPublic < User expose :last_sign_in_at expose :confirmed_at expose :email @@ -32,10 +32,9 @@ module API expose :can_create_project?, as: :can_create_project expose :two_factor_enabled?, as: :two_factor_enabled expose :external - expose :private_token, if: lambda { |user, options| user.is_admin? && options[:sudo_identifier] } end - class UserLogin < UserFull + class UserWithPrivateToken < UserPublic expose :private_token end @@ -290,7 +289,7 @@ module API end class SSHKeyWithUser < SSHKey - expose :user, using: Entities::UserFull + expose :user, using: Entities::UserPublic end class Note < Grape::Entity diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7f94ede7940..6a47efc74f0 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -44,11 +44,14 @@ module API return nil end - identifier = sudo_identifier() + identifier = sudo_identifier - # If the sudo is the current user do nothing - if identifier && !(@current_user.id == identifier || @current_user.username == identifier) + if identifier + # We check for private_token because we cannot allow PAT to be used forbidden!('Must be admin to use sudo') unless @current_user.is_admin? + forbidden!('Private token must be specified in order to use sudo') unless private_token_used? + + @impersonator = @current_user @current_user = User.by_username_or_id(identifier) not_found!("No user id or username for: #{identifier}") if @current_user.nil? end @@ -399,6 +402,10 @@ module API links.join(', ') end + def private_token_used? + private_token == @current_user.private_token + end + def secret_token Gitlab::Shell.secret_token end diff --git a/lib/api/session.rb b/lib/api/session.rb index d09400b81f5..002ffd1d154 100644 --- a/lib/api/session.rb +++ b/lib/api/session.rb @@ -1,7 +1,7 @@ module API class Session < Grape::API desc 'Login to get token' do - success Entities::UserLogin + success Entities::UserWithPrivateToken end params do optional :login, type: String, desc: 'The username' @@ -14,7 +14,7 @@ module API return unauthorized! unless user return render_api_error!('401 Unauthorized. You have 2FA enabled. Please use a personal access token to access the API', 401) if user.two_factor_enabled? - present user, with: Entities::UserLogin + present user, with: Entities::UserWithPrivateToken end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index b3870e0c7c8..1dab799dd61 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -51,7 +51,7 @@ module API users = users.external if params[:external] && current_user.is_admin? end - entity = current_user.is_admin? ? Entities::UserFull : Entities::UserBasic + entity = current_user.is_admin? ? Entities::UserPublic : Entities::UserBasic present paginate(users), with: entity end @@ -66,7 +66,7 @@ module API not_found!('User') unless user if current_user && current_user.is_admin? - present user, with: Entities::UserFull + present user, with: Entities::UserPublic elsif can?(current_user, :read_user, user) present user, with: Entities::User else @@ -75,7 +75,7 @@ module API end desc 'Create a user. Available only for admins.' do - success Entities::UserFull + success Entities::UserPublic end params do requires :email, type: String, desc: 'The email of the user' @@ -99,7 +99,7 @@ module API end if user.save - present user, with: Entities::UserFull + present user, with: Entities::UserPublic else conflict!('Email has already been taken') if User. where(email: user.email). @@ -114,7 +114,7 @@ module API end desc 'Update a user. Available only for admins.' do - success Entities::UserFull + success Entities::UserPublic end params do requires :id, type: Integer, desc: 'The ID of the user' @@ -161,7 +161,7 @@ module API user_params.delete(:provider) if user.update_attributes(user_params) - present user, with: Entities::UserFull + present user, with: Entities::UserPublic else render_validation_error!(user) end @@ -350,10 +350,10 @@ module API resource :user do desc 'Get the currently authenticated user' do - success Entities::UserFull + success Entities::UserPublic end get do - present current_user, with: Entities::UserFull, sudo_identifier: sudo_identifier + present current_user, with: @impersonator ? Entities::UserWithPrivateToken : Entities::UserPublic end desc "Get the currently authenticated user's SSH keys" do diff --git a/spec/fixtures/api/schemas/user/login.json b/spec/fixtures/api/schemas/user/login.json new file mode 100644 index 00000000000..e6c1d9c9d84 --- /dev/null +++ b/spec/fixtures/api/schemas/user/login.json @@ -0,0 +1,37 @@ +{ + "type": "object", + "required": [ + "id", + "username", + "email", + "name", + "state", + "avatar_url", + "web_url", + "created_at", + "is_admin", + "bio", + "location", + "skype", + "linkedin", + "twitter", + "website_url", + "organization", + "last_sign_in_at", + "confirmed_at", + "theme_id", + "color_scheme_id", + "projects_limit", + "current_sign_in_at", + "identities", + "can_create_group", + "can_create_project", + "two_factor_enabled", + "external", + "private_token" + ], + "properties": { + "$ref": "full.json", + "private_token": { "type": "string" } + } +} diff --git a/spec/fixtures/api/schemas/user/public.json b/spec/fixtures/api/schemas/user/public.json new file mode 100644 index 00000000000..dbd5d32e89c --- /dev/null +++ b/spec/fixtures/api/schemas/user/public.json @@ -0,0 +1,79 @@ +{ + "type": "object", + "required": [ + "id", + "username", + "email", + "name", + "state", + "avatar_url", + "web_url", + "created_at", + "is_admin", + "bio", + "location", + "skype", + "linkedin", + "twitter", + "website_url", + "organization", + "last_sign_in_at", + "confirmed_at", + "theme_id", + "color_scheme_id", + "projects_limit", + "current_sign_in_at", + "identities", + "can_create_group", + "can_create_project", + "two_factor_enabled", + "external" + ], + "properties": { + "id": { "type": "integer" }, + "username": { "type": "string" }, + "email": { + "type": "string", + "pattern": "^[^@]+@[^@]+$" + }, + "name": { "type": "string" }, + "state": { + "type": "string", + "enum": ["active", "blocked"] + }, + "avatar_url": { "type": "string" }, + "web_url": { "type": "string" }, + "created_at": { "type": "date" }, + "is_admin": { "type": "boolean" }, + "bio": { "type": ["string", "null"] }, + "location": { "type": ["string", "null"] }, + "skype": { "type": "string" }, + "linkedin": { "type": "string" }, + "twitter": { "type": "string "}, + "website_url": { "type": "string" }, + "organization": { "type": ["string", "null"] }, + "last_sign_in_at": { "type": "date" }, + "confirmed_at": { "type": ["date", "null"] }, + "theme_id": { "type": "integer" }, + "color_scheme_id": { "type": "integer" }, + "projects_limit": { "type": "integer" }, + "current_sign_in_at": { "type": "date" }, + "identities": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": ["github", "bitbucket", "google_oauth2"] + }, + "extern_uid": { "type": ["number", "string"] } + } + } + }, + "can_create_group": { "type": "boolean" }, + "can_create_project": { "type": "boolean" }, + "two_factor_enabled": { "type": "boolean" }, + "external": { "type": "boolean" } + } +} diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb index 36517ad0f8c..3f34309f419 100644 --- a/spec/requests/api/api_helpers_spec.rb +++ b/spec/requests/api/api_helpers_spec.rb @@ -153,85 +153,144 @@ describe API::Helpers, api: true do end end - it "changes current user to sudo when admin" do - set_env(admin, user.id) - expect(current_user).to eq(user) - set_param(admin, user.id) - expect(current_user).to eq(user) - set_env(admin, user.username) - expect(current_user).to eq(user) - set_param(admin, user.username) - expect(current_user).to eq(user) - end + context 'sudo usage' do + context 'with admin' do + context 'with header' do + context 'with id' do + it 'changes current_user to sudo' do + set_env(admin, user.id) - it "throws an error when the current user is not an admin and attempting to sudo" do - set_env(user, admin.id) - expect { current_user }.to raise_error(Exception) - set_param(user, admin.id) - expect { current_user }.to raise_error(Exception) - set_env(user, admin.username) - expect { current_user }.to raise_error(Exception) - set_param(user, admin.username) - expect { current_user }.to raise_error(Exception) - end + expect(current_user).to eq(user) + end - it "throws an error when the user cannot be found for a given id" do - id = user.id + admin.id - expect(user.id).not_to eq(id) - expect(admin.id).not_to eq(id) - set_env(admin, id) - expect { current_user }.to raise_error(Exception) + it 'handles sudo to oneself' do + set_env(admin, admin.id) - set_param(admin, id) - expect { current_user }.to raise_error(Exception) - end + expect(current_user).to eq(admin) + end - it "throws an error when the user cannot be found for a given username" do - username = "#{user.username}#{admin.username}" - expect(user.username).not_to eq(username) - expect(admin.username).not_to eq(username) - set_env(admin, username) - expect { current_user }.to raise_error(Exception) + it 'throws an error when user cannot be found' do + id = user.id + admin.id + expect(user.id).not_to eq(id) + expect(admin.id).not_to eq(id) - set_param(admin, username) - expect { current_user }.to raise_error(Exception) - end + set_env(admin, id) - it "handles sudo's to oneself" do - set_env(admin, admin.id) - expect(current_user).to eq(admin) - set_param(admin, admin.id) - expect(current_user).to eq(admin) - set_env(admin, admin.username) - expect(current_user).to eq(admin) - set_param(admin, admin.username) - expect(current_user).to eq(admin) - end + expect { current_user }.to raise_error(Exception) + end + end - it "handles multiple sudo's to oneself" do - set_env(admin, user.id) - expect(current_user).to eq(user) - expect(current_user).to eq(user) - set_env(admin, user.username) - expect(current_user).to eq(user) - expect(current_user).to eq(user) + context 'with username' do + it 'changes current_user to sudo' do + set_env(admin, user.username) - set_param(admin, user.id) - expect(current_user).to eq(user) - expect(current_user).to eq(user) - set_param(admin, user.username) - expect(current_user).to eq(user) - expect(current_user).to eq(user) - end + expect(current_user).to eq(user) + end - it "handles multiple sudo's to oneself using string ids" do - set_env(admin, user.id.to_s) - expect(current_user).to eq(user) - expect(current_user).to eq(user) + it 'handles sudo to oneself' do + set_env(admin, admin.username) - set_param(admin, user.id.to_s) - expect(current_user).to eq(user) - expect(current_user).to eq(user) + expect(current_user).to eq(admin) + end + + it "throws an error when the user cannot be found for a given username" do + username = "#{user.username}#{admin.username}" + expect(user.username).not_to eq(username) + expect(admin.username).not_to eq(username) + + set_env(admin, username) + + expect { current_user }.to raise_error(Exception) + end + end + end + + context 'with param' do + context 'with id' do + it 'changes current_user to sudo' do + set_param(admin, user.id) + + expect(current_user).to eq(user) + end + + it 'handles sudo to oneself' do + set_param(admin, admin.id) + + expect(current_user).to eq(admin) + end + + it 'handles sudo to oneself using string' do + set_env(admin, user.id.to_s) + + expect(current_user).to eq(user) + end + + it 'throws an error when user cannot be found' do + id = user.id + admin.id + expect(user.id).not_to eq(id) + expect(admin.id).not_to eq(id) + + set_param(admin, id) + + expect { current_user }.to raise_error(Exception) + end + end + + context 'with username' do + it 'changes current_user to sudo' do + set_param(admin, user.username) + + expect(current_user).to eq(user) + end + + it 'handles sudo to oneself' do + set_param(admin, admin.username) + + expect(current_user).to eq(admin) + end + + it "throws an error when the user cannot be found for a given username" do + username = "#{user.username}#{admin.username}" + expect(user.username).not_to eq(username) + expect(admin.username).not_to eq(username) + + set_param(admin, username) + + expect { current_user }.to raise_error(Exception) + end + end + end + end + + context 'with regular user' do + context 'with env' do + it 'changes current_user to sudo when admin and user id' do + set_env(user, admin.id) + + expect { current_user }.to raise_error(Exception) + end + + it 'changes current_user to sudo when admin and user username' do + set_env(user, admin.username) + + expect { current_user }.to raise_error(Exception) + end + end + + context 'with params' do + it 'changes current_user to sudo when admin and user id' do + set_param(user, admin.id) + + expect { current_user }.to raise_error(Exception) + end + + it 'changes current_user to sudo when admin and user username' do + set_param(user, admin.username) + + expect { current_user }.to raise_error(Exception) + end + end + end end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index f82f52e7399..c37dbfa0a33 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -651,20 +651,75 @@ describe API::Users, api: true do end describe "GET /user" do - it "returns current user" do - get api("/user", user) - expect(response).to have_http_status(200) - expect(json_response['email']).to eq(user.email) - expect(json_response['is_admin']).to eq(user.is_admin?) - expect(json_response['can_create_project']).to eq(user.can_create_project?) - expect(json_response['can_create_group']).to eq(user.can_create_group?) - expect(json_response['projects_limit']).to eq(user.projects_limit) - expect(json_response['private_token']).to be_blank + let(:personal_access_token) { create(:personal_access_token, user: user) } + let(:private_token) { user.private_token } + + context 'with regular user' do + context 'with personal access token' do + it 'returns 403 without private token when sudo is defined' do + get api("/user?private_token=#{personal_access_token.token}&sudo=#{user.id}") + + expect(response).to have_http_status(403) + end + end + + context 'with private token' do + it 'returns 403 without private token when sudo defined' do + get api("/user?private_token=#{private_token}&sudo=#{user.id}") + + expect(response).to have_http_status(403) + end + end + + it 'returns current user without private token when sudo not defined' do + get api("/user", user) + + expect(response).to have_http_status(200) + expect(response).to match_response_schema('user/public') + end end - it "returns 401 error if user is unauthenticated" do - get api("/user") - expect(response).to have_http_status(401) + context 'with admin' do + let(:user) { create(:admin) } + + context 'with personal access token' do + it 'returns 403 without private token when sudo defined' do + get api("/user?private_token=#{personal_access_token.token}&sudo=#{user.id}") + + expect(response).to have_http_status(403) + end + + it 'returns current user without private token when sudo not defined' do + get api("/user?private_token=#{personal_access_token.token}") + + expect(response).to have_http_status(200) + expect(response).to match_response_schema('user/public') + end + end + + context 'with private token' do + it 'returns current user with private token when sudo defined' do + get api("/user?private_token=#{private_token}&sudo=#{user.id}") + + expect(response).to have_http_status(200) + expect(response).to match_response_schema('user/login') + end + + it 'returns current user without private token when sudo not defined' do + get api("/user?private_token=#{private_token}") + + expect(response).to have_http_status(200) + expect(response).to match_response_schema('user/public') + end + end + end + + context 'with unauthenticated user' do + it "returns 401 error if user is unauthenticated" do + get api("/user") + + expect(response).to have_http_status(401) + end end end From 6ab74b1cb3f6982c52c7124a6e9e451c83d33645 Mon Sep 17 00:00:00 2001 From: jnoortheen Date: Tue, 29 Nov 2016 22:01:01 +0530 Subject: [PATCH 086/140] fix: 24982- Remove'Signed in successfully' message After this change the sign-in-success flash message will not be shown refactor: set flash message to be nil while signing in test: changed tests to reflect removal of sign-in message refactor: adding signed_in message back See Merge Request !7837 issue#24982 --- app/controllers/sessions_controller.rb | 2 ++ .../24982-ux-improvement-sign-in-success-message.yml | 5 +++++ spec/controllers/sessions_controller_spec.rb | 1 - spec/features/u2f_spec.rb | 9 ++++----- 4 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/24982-ux-improvement-sign-in-success-message.yml diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 5d7ecfeacf4..38e7c6f4a48 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -31,6 +31,8 @@ class SessionsController < Devise::SessionsController resource.update_attributes(reset_password_token: nil, reset_password_sent_at: nil) end + # hide the signed-in notification + flash[:notice] = nil log_audit_event(current_user, with: authentication_method) end end diff --git a/changelogs/unreleased/24982-ux-improvement-sign-in-success-message.yml b/changelogs/unreleased/24982-ux-improvement-sign-in-success-message.yml new file mode 100644 index 00000000000..12ea08e3815 --- /dev/null +++ b/changelogs/unreleased/24982-ux-improvement-sign-in-success-message.yml @@ -0,0 +1,5 @@ +--- +title: 'fix: 24982- Remove''Signed in successfully'' message After this change the + sign-in-success flash message will not be shown' +merge_request: 7837 +author: jnoortheen diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 48d69377461..b56c7880b64 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -22,7 +22,6 @@ describe SessionsController do it 'authenticates user correctly' do post(:create, user: { login: user.username, password: user.password }) - expect(response).to set_flash.to /Signed in successfully/ expect(subject.current_user). to eq user end diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb index b750f27ea72..be21b403084 100644 --- a/spec/features/u2f_spec.rb +++ b/spec/features/u2f_spec.rb @@ -163,8 +163,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: click_on "Sign in via U2F device" expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') end end @@ -178,7 +177,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') end end @@ -234,7 +233,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') end end end @@ -275,7 +274,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: expect(page.body).to match('We heard back from your U2F device') click_on "Authenticate via U2F Device" - expect(page.body).to match('Signed in successfully') + expect(page.body).to match('href="/users/sign_out"') logout end From 19fb84e3a897f5b251fb3f437fe365c6ec342c34 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 7 Dec 2016 15:27:14 +0000 Subject: [PATCH 087/140] Updated members dropdowns This ports some code over from EE to reduce conflicts --- app/assets/javascripts/gl_dropdown.js | 5 ++ app/assets/javascripts/members.js.es6 | 71 ++++++++++++++++--- .../stylesheets/framework/dropdowns.scss | 5 ++ app/assets/stylesheets/pages/members.scss | 4 ++ app/views/groups/group_members/update.js.haml | 1 + .../projects/project_members/update.js.haml | 1 + app/views/shared/members/_member.html.haml | 20 +++++- changelogs/unreleased/members-dropdowns.yml | 4 ++ features/steps/group/members.rb | 7 +- features/steps/project/team_management.rb | 6 +- 10 files changed, 109 insertions(+), 15 deletions(-) create mode 100644 changelogs/unreleased/members-dropdowns.yml diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 969778dded7..9a91018a8e4 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -650,6 +650,11 @@ } else if(value) { field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']"); } + + if (this.options.isSelectable && !this.options.isSelectable(selectedObject, el)) { + return; + } + if (el.hasClass(ACTIVE_CLASS)) { el.removeClass(ACTIVE_CLASS); if (field && field.length) { diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 895bc10784f..64826894965 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -1,38 +1,87 @@ -/* eslint-disable */ -((w) => { - w.gl = w.gl || {}; +/* eslint-disable class-methods-use-this */ +(() => { + window.gl = window.gl || {}; class Members { constructor() { this.addListeners(); + this.initGLDropdown(); } addListeners() { $('.project_member, .group_member').off('ajax:success').on('ajax:success', this.removeRow); - $('.js-member-update-control').off('change').on('change', this.formSubmit); - $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess); + $('.js-member-update-control').off('change').on('change', this.formSubmit.bind(this)); + $('.js-edit-member-form').off('ajax:success').on('ajax:success', this.formSuccess.bind(this)); gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); } + initGLDropdown() { + $('.js-member-permissions-dropdown').each((i, btn) => { + const $btn = $(btn); + + $btn.glDropdown({ + selectable: true, + isSelectable(selected, $el) { + return !$el.hasClass('is-active'); + }, + fieldName: $btn.data('field-name'), + id(selected, $el) { + return $el.data('id'); + }, + toggleLabel(selected, $el) { + return $el.text(); + }, + clicked: (selected, $el) => { + const $link = $($el); + const { $toggle, $dateInput } = this.getMemberListItems($link); + + $toggle.attr('disabled', true); + $dateInput.attr('disabled', true); + + $btn.closest('form').trigger('submit.rails'); + }, + }); + }); + } + removeRow(e) { const $target = $(e.target); if ($target.hasClass('btn-remove')) { $target.closest('.member') - .fadeOut(function () { + .fadeOut(function fadeOutMemberRow() { $(this).remove(); }); } } - formSubmit() { - $(this).closest('form').trigger("submit.rails").end().disable(); + formSubmit(e) { + const $this = $(e.currentTarget); + const { $toggle, $dateInput } = this.getMemberListItems($this); + + $this.closest('form').trigger('submit.rails'); + + $toggle.attr('disabled', true); + $dateInput.attr('disabled', true); } - formSuccess() { - $(this).find('.js-member-update-control').enable(); + formSuccess(e) { + const { $toggle, $dateInput } = this.getMemberListItems($(e.currentTarget).closest('.member')); + + $toggle.removeAttr('disabled'); + $dateInput.removeAttr('disabled'); + } + + getMemberListItems($el) { + const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('el-id')}`); + + return { + $memberListItem, + $toggle: $memberListItem.find('.dropdown-menu-toggle'), + $dateInput: $memberListItem.find('.js-access-expiration-date'), + }; } } gl.Members = Members; -})(window); +})(); diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index 33de652c06f..d5914b900e2 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -42,6 +42,11 @@ border-radius: $border-radius-base; white-space: nowrap; + &[disabled] { + background-color: $input-bg-disabled; + cursor: not-allowed; + } + &.no-outline { outline: 0; } diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss index 756efa9c7fa..5f3bbb40ba0 100644 --- a/app/assets/stylesheets/pages/members.scss +++ b/app/assets/stylesheets/pages/members.scss @@ -54,6 +54,10 @@ @media (min-width: $screen-sm-min) { width: 50%; } + + .dropdown-menu-toggle { + width: 100%; + } } .member-access-text { diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml index de8f53b6b52..9d05bff6c4e 100644 --- a/app/views/groups/group_members/update.js.haml +++ b/app/views/groups/group_members/update.js.haml @@ -1,3 +1,4 @@ :plain var $listItem = $('#{escape_javascript(render('shared/members/member', member: @group_member))}'); $("##{dom_id(@group_member)} .list-item-name").replaceWith($listItem.find('.list-item-name')); + gl.utils.localTimeAgo($('.js-timeago'), $("##{dom_id(@group_member)}")); diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml index 91927181efb..d15f4310ff5 100644 --- a/app/views/projects/project_members/update.js.haml +++ b/app/views/projects/project_members/update.js.haml @@ -1,3 +1,4 @@ :plain var $listItem = $('#{escape_javascript(render('shared/members/member', member: @project_member))}'); $("##{dom_id(@project_member)} .list-item-name").replaceWith($listItem.find('.list-item-name')); + gl.utils.localTimeAgo($('.js-timeago'), $("##{dom_id(@project_member)}")); diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml index 432047a1c4e..ac3e4d9bac6 100644 --- a/app/views/shared/members/_member.html.haml +++ b/app/views/shared/members/_member.html.haml @@ -48,9 +48,25 @@ - if show_controls - if user != current_user = form_for member, remote: true, html: { class: 'form-horizontal js-edit-member-form' } do |f| - = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{member.id}", disabled: !can_admin_member + = f.hidden_field :access_level + .member-form-control.dropdown.append-right-5 + %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", + disabled: !can_admin_member, + data: { toggle: "dropdown", field_name: "#{f.object_name}[access_level]" } } + %span.dropdown-toggle-text + = member.human_access + = icon("chevron-down") + .dropdown-menu.dropdown-select.dropdown-menu-align-right.dropdown-menu-selectable + = dropdown_title("Change permissions") + .dropdown-content + %ul + - Gitlab::Access.options.each do |role, role_id| + %li + = link_to role, "javascript:void(0)", + class: ("is-active" if member.access_level == role_id), + data: { id: role_id, el_id: dom_id(member) } .prepend-left-5.clearable-input.member-form-control - = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member + = f.text_field :expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{member.id}", disabled: !can_admin_member, data: { el_id: dom_id(member) } %i.clear-icon.js-clear-input - else %span.member-access-text= member.human_access diff --git a/changelogs/unreleased/members-dropdowns.yml b/changelogs/unreleased/members-dropdowns.yml new file mode 100644 index 00000000000..b15403d6d62 --- /dev/null +++ b/changelogs/unreleased/members-dropdowns.yml @@ -0,0 +1,4 @@ +--- +title: Updated members dropdowns +merge_request: +author: diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb index cefc55d07ab..adaf375453c 100644 --- a/features/steps/group/members.rb +++ b/features/steps/group/members.rb @@ -117,7 +117,12 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps member = mary_jane_member page.within "#group_member_#{member.id}" do - select 'Developer', from: "member_access_level_#{member.id}" + click_button member.human_access + + page.within '.dropdown-menu' do + click_link 'Developer' + end + wait_for_ajax end end diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb index b21d0849ad1..99b6397ba74 100644 --- a/features/steps/project/team_management.rb +++ b/features/steps/project/team_management.rb @@ -65,7 +65,11 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps user = User.find_by(name: 'Dmitriy') project_member = project.project_members.find_by(user_id: user.id) page.within "#project_member_#{project_member.id}" do - select "Reporter", from: "member_access_level_#{project_member.id}" + click_button project_member.human_access + + page.within '.dropdown-menu' do + click_link 'Reporter' + end end end From faf8f421103194be9af09dd8d24c582437bb7d2f Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 7 Dec 2016 12:58:23 -0500 Subject: [PATCH 088/140] Do nothing if file is undefined --- app/assets/javascripts/blob/template_selector.js.es6 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/blob/template_selector.js.es6 b/app/assets/javascripts/blob/template_selector.js.es6 index 5434a19bcec..0ff5c0fab05 100644 --- a/app/assets/javascripts/blob/template_selector.js.es6 +++ b/app/assets/javascripts/blob/template_selector.js.es6 @@ -70,6 +70,8 @@ // e.g. // Api.gitignoreText item.name, @requestFileSuccess.bind(@) requestFileSuccess(file, { skipFocus } = {}) { + if (!file) return; + const oldValue = this.editor.getValue(); let newValue = file.content; From 8842f55201ae4725b307336686f8ab168fa0effb Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 7 Dec 2016 19:30:14 +0100 Subject: [PATCH 089/140] Code review --- app/models/ci/stage.rb | 4 +++- app/views/projects/commit/_pipeline.html.haml | 5 ++--- app/views/projects/pipelines/_graph.html.haml | 4 ++-- app/views/projects/pipelines/_with_tabs.html.haml | 5 ++--- app/views/projects/stage/_graph.html.haml | 11 ++++++----- spec/lib/gitlab/ci/status/factory_spec.rb | 6 +----- spec/lib/gitlab/ci/status/pipeline/factory_spec.rb | 4 ++-- 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index fe1c5c642e1..d2a37c0a827 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -9,7 +9,9 @@ module Ci delegate :project, to: :pipeline def initialize(pipeline, name:, status: nil) - @pipeline, @name, @status = pipeline, name, status + @pipeline = pipeline + @name = name + @status = status end def to_param diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index 4fc5e15592a..a677e859a15 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -25,7 +25,7 @@ = time_interval_in_words pipeline.duration .row-content-block.build-content.middle-block.pipeline-graph.hidden - = render "projects/pipelines/graph", subject: pipeline + = render "projects/pipelines/graph", subject: pipeline, as: :pipeline - if pipeline.yaml_errors.present? .bs-callout.bs-callout-danger @@ -50,5 +50,4 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - - pipeline.stages.each do |stage| - = render "projects/stage/stage", subject: stage + = render partial: "projects/stage/stage", collection: pipeline.stages, as: :stage diff --git a/app/views/projects/pipelines/_graph.html.haml b/app/views/projects/pipelines/_graph.html.haml index 3bb6426c156..16297b93d31 100644 --- a/app/views/projects/pipelines/_graph.html.haml +++ b/app/views/projects/pipelines/_graph.html.haml @@ -1,4 +1,4 @@ +- pipeline = local_assigns.fetch(:pipeline) .pipeline-visualization %ul.stage-column-list - - subject.stages.each do |stage| - = render "projects/stage/graph", subject: stage + = render partial: "projects/stage/graph", collection: pipeline.stages, as: :stage diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 2ace9339af3..a58bcb38b18 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -13,7 +13,7 @@ .tab-content #js-tab-pipeline.tab-pane .build-content.middle-block.pipeline-graph - = render "projects/pipelines/graph", subject: pipeline + = render "projects/pipelines/graph", subject: pipeline, as: :pipeline #js-tab-builds.tab-pane - if pipeline.yaml_errors.present? @@ -39,5 +39,4 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - - pipeline.stages.each do |stage| - = render "projects/stage/stage", subject: stage + = render partial: "projects/stage/stage", collection: pipeline.stages, as: :stage diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index f1d11db58ab..d8c87fae5a1 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -1,12 +1,13 @@ +- stage = local_assigns.fetch(:stage) +- statuses = stage.statuses.latest +- status_groups = statuses.sort_by(&:name).group_by(&:group_name) %li.stage-column .stage-name - %a{ name: subject.name } - - if subject.name - = subject.name.titleize + %a{ name: stage.name } + - if stage.name + = stage.name.titleize .builds-container %ul - - statuses = subject.statuses.latest - - status_groups = statuses.sort_by(&:name).group_by(&:group_name) - status_groups.each do |group_name, grouped_statuses| - if grouped_statuses.one? - status = grouped_statuses.first diff --git a/spec/lib/gitlab/ci/status/factory_spec.rb b/spec/lib/gitlab/ci/status/factory_spec.rb index 8ebdbddb001..d5bd7f7102b 100644 --- a/spec/lib/gitlab/ci/status/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/factory_spec.rb @@ -1,15 +1,11 @@ require 'spec_helper' describe Gitlab::Ci::Status::Factory do - let(:object) { double(status: :created) } - subject do described_class.new(object) end - let(:status) do - subject.fabricate! - end + let(:status) { subject.fabricate! } context 'when object has a core status' do HasStatus::AVAILABLE_STATUSES.each do |core_status| diff --git a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb index 939ad2edf04..d6243940f2e 100644 --- a/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb +++ b/spec/lib/gitlab/ci/status/pipeline/factory_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do Gitlab::Ci::Status.const_get(core_status.capitalize)) end - it 'extends core status with common stage methods' do + it 'extends core status with common pipeline methods' do expect(status).to have_details expect(status).not_to have_action expect(status.details_path) @@ -45,7 +45,7 @@ describe Gitlab::Ci::Status::Pipeline::Factory do .to be_a Gitlab::Ci::Status::Pipeline::SuccessWithWarnings end - it 'extends core status with common stage methods' do + it 'extends core status with common pipeline methods' do expect(status).to have_details end end From 6bb3293932a0ff249386f8c1f245ebc85f15df31 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 7 Dec 2016 19:32:13 +0100 Subject: [PATCH 090/140] Move .pipeline-graph to pipelines/graph --- app/views/projects/commit/_pipeline.html.haml | 2 +- app/views/projects/pipelines/_graph.html.haml | 2 +- app/views/projects/pipelines/_with_tabs.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index a677e859a15..6a45853e614 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -24,7 +24,7 @@ in = time_interval_in_words pipeline.duration - .row-content-block.build-content.middle-block.pipeline-graph.hidden + .row-content-block.build-content.middle-block.hidden = render "projects/pipelines/graph", subject: pipeline, as: :pipeline - if pipeline.yaml_errors.present? diff --git a/app/views/projects/pipelines/_graph.html.haml b/app/views/projects/pipelines/_graph.html.haml index 16297b93d31..0202833c0bf 100644 --- a/app/views/projects/pipelines/_graph.html.haml +++ b/app/views/projects/pipelines/_graph.html.haml @@ -1,4 +1,4 @@ - pipeline = local_assigns.fetch(:pipeline) -.pipeline-visualization +.pipeline-visualization.pipeline-graph %ul.stage-column-list = render partial: "projects/stage/graph", collection: pipeline.stages, as: :stage diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index a58bcb38b18..7ef498f890b 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -12,7 +12,7 @@ .tab-content #js-tab-pipeline.tab-pane - .build-content.middle-block.pipeline-graph + .build-content.middle-block = render "projects/pipelines/graph", subject: pipeline, as: :pipeline #js-tab-builds.tab-pane From ae1550bb5234aa5f6ce2611ca606ac49fc38c2d1 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 7 Dec 2016 12:37:41 -0600 Subject: [PATCH 091/140] Fade out should be white instead of gray --- app/assets/stylesheets/pages/notes.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 16b099c09eb..2e89d3f4e41 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -124,7 +124,7 @@ ul.notes { position: absolute; left: 0; bottom: 0; - background: linear-gradient(rgba($gray-light, 0.1) -100px, $white-light 100%); + background: linear-gradient(rgba($white-light, 0.1) -100px, $white-light 100%); } &.hide-shade { From df84444b30cddfc073f69b128f74a50ddbc5d87c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 7 Dec 2016 22:17:58 +0100 Subject: [PATCH 092/140] Fix specs --- app/views/projects/ci/builds/_build.html.haml | 8 ++++---- app/views/projects/commit/_pipeline.html.haml | 2 +- app/views/projects/pipelines/_with_tabs.html.haml | 4 ++-- app/views/projects/stage/_stage.html.haml | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index e75547c815f..18b3b04154f 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -104,9 +104,9 @@ = link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do = icon('remove', class: 'cred') - elsif allow_retry - - if build.retryable? - = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do - = icon('repeat') - - elsif build.playable? && !admin + - if build.playable? && !admin = link_to play_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do = custom_icon('icon_play') + - elsif build.retryable? + = link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do + = icon('repeat') diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index 6a45853e614..c7b5c1124b3 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -25,7 +25,7 @@ = time_interval_in_words pipeline.duration .row-content-block.build-content.middle-block.hidden - = render "projects/pipelines/graph", subject: pipeline, as: :pipeline + = render "projects/pipelines/graph", pipeline: pipeline - if pipeline.yaml_errors.present? .bs-callout.bs-callout-danger diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 7ef498f890b..739e5930822 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -13,7 +13,7 @@ .tab-content #js-tab-pipeline.tab-pane .build-content.middle-block - = render "projects/pipelines/graph", subject: pipeline, as: :pipeline + = render "projects/pipelines/graph", pipeline: pipeline #js-tab-builds.tab-pane - if pipeline.yaml_errors.present? @@ -39,4 +39,4 @@ - if pipeline.project.build_coverage_enabled? %th Coverage %th - = render partial: "projects/stage/stage", collection: pipeline.stages, as: :stage + = render partial: "projects/stage/stage", collection: pipeline.stages, as: :stage diff --git a/app/views/projects/stage/_stage.html.haml b/app/views/projects/stage/_stage.html.haml index 055d8fca38b..1eca375db3d 100644 --- a/app/views/projects/stage/_stage.html.haml +++ b/app/views/projects/stage/_stage.html.haml @@ -1,14 +1,14 @@ %tr %th{colspan: 10} %strong - %a{ name: subject.name } - %span{class: "ci-status-link ci-status-icon-#{subject.status}"} - = ci_icon_for_status(subject.status) - - if subject.name + %a{ name: stage.name } + %span{class: "ci-status-link ci-status-icon-#{stage.status}"} + = ci_icon_for_status(stage.status) + - if stage.name   - = subject.name.titleize - = render subject.statuses.latest_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true - = render subject.statuses.retried_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true + = stage.name.titleize += render stage.statuses.latest_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true += render stage.statuses.retried_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true %tr %td{colspan: 10}   From 0a8289cafd4a996452c0e21322178793be8120be Mon Sep 17 00:00:00 2001 From: Rydkin Maxim Date: Sun, 4 Dec 2016 00:45:48 +0300 Subject: [PATCH 093/140] add link_to_if helper on target_branch link on Merge Request show page for case of deleted target branch add spec on #24507 bug description add changelog entry fix changelog remove unnecessary js:true from specs change spec title add test for link to target branch before deletions renamed spec about state of target branch link before and after deletion some fixes into spec --- .../projects/merge_requests/_show.html.haml | 2 +- ...e_deleted_branch_link_in_merge_request.yml | 4 ++ .../merge_requests/target_branch_spec.rb | 41 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/24507_remove_deleted_branch_link_in_merge_request.yml create mode 100644 spec/features/merge_requests/target_branch_spec.rb diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 0e2975bd551..896f10104fa 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -31,7 +31,7 @@ %span.label-branch= source_branch_with_namespace(@merge_request) %span into %span.label-branch - = link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch) + = link_to_if @merge_request.target_branch_exists?, @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch) - if @merge_request.open? && @merge_request.diverged_from_target_branch? %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) diff --git a/changelogs/unreleased/24507_remove_deleted_branch_link_in_merge_request.yml b/changelogs/unreleased/24507_remove_deleted_branch_link_in_merge_request.yml new file mode 100644 index 00000000000..34999480d4a --- /dev/null +++ b/changelogs/unreleased/24507_remove_deleted_branch_link_in_merge_request.yml @@ -0,0 +1,4 @@ +--- +title: 'Remove unnecessary target branch link from MR page in case of deleted target branch' +merge_request: 7916 +author: Rydkin Maxim diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb new file mode 100644 index 00000000000..b6134540273 --- /dev/null +++ b/spec/features/merge_requests/target_branch_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe 'Target branch', feature: true do + let(:user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + + def path_to_merge_request + namespace_project_merge_request_path( + project.namespace, + project, merge_request + ) + end + + before do + login_as user + project.team << [user, :master] + end + + it 'shows link to target branch' do + visit path_to_merge_request + expect(page).to have_link('feature', href: namespace_project_commits_path(project.namespace, project, merge_request.target_branch)) + end + + context 'when branch was deleted' do + before do + DeleteBranchService.new(project, user).execute('feature') + visit path_to_merge_request + end + + it 'shows a message about missing target branch' do + expect(page).to have_content( + 'Target branch feature does not exist' + ) + end + + it 'does not show link to target branch' do + expect(page).not_to have_link('feature') + end + end +end From f4f9af06c9e6f468d9f696dbb5438dd8825fe773 Mon Sep 17 00:00:00 2001 From: Horacio Sanson Date: Tue, 29 Nov 2016 13:18:13 +0900 Subject: [PATCH 094/140] Enable display of admonition icons in Asciidoc. When rendering Asciidoc documents this merge request enables the diplay of admonition blocks using font icons. This improves the looks of Asciidoc redered files. This uses the font-awesome fonts already present in Gitlab so no large dependencies are required for this to work. --- app/assets/stylesheets/framework.scss | 1 + .../stylesheets/framework/asciidoctor.scss | 27 +++++++++++++++++++ .../enable-asciidoctor-admonition-icons.yml | 4 +++ lib/gitlab/asciidoc.rb | 2 +- 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/framework/asciidoctor.scss create mode 100644 changelogs/unreleased/enable-asciidoctor-admonition-icons.yml diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 4d4835568ed..c82a9a2b9e3 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -6,6 +6,7 @@ @import "framework/animations.scss"; @import "framework/avatar.scss"; +@import "framework/asciidoctor.scss"; @import "framework/blocks.scss"; @import "framework/buttons.scss"; @import "framework/calendar.scss"; diff --git a/app/assets/stylesheets/framework/asciidoctor.scss b/app/assets/stylesheets/framework/asciidoctor.scss new file mode 100644 index 00000000000..62493c32833 --- /dev/null +++ b/app/assets/stylesheets/framework/asciidoctor.scss @@ -0,0 +1,27 @@ +.admonitionblock td.icon { + width: 1%; + + [class^="fa icon-"] { + @extend .fa-2x; + } + + .icon-note { + @extend .fa-thumb-tack; + } + + .icon-tip { + @extend .fa-lightbulb-o; + } + + .icon-warning { + @extend .fa-exclamation-triangle; + } + + .icon-caution { + @extend .fa-fire; + } + + .icon-important { + @extend .fa-exclamation-circle; + } +} diff --git a/changelogs/unreleased/enable-asciidoctor-admonition-icons.yml b/changelogs/unreleased/enable-asciidoctor-admonition-icons.yml new file mode 100644 index 00000000000..9c52e53c3b4 --- /dev/null +++ b/changelogs/unreleased/enable-asciidoctor-admonition-icons.yml @@ -0,0 +1,4 @@ +--- +title: Enable AsciiDoctor admonition icons +merge_request: 7812 +author: Horacio Sanson diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index 1a22ad9acf5..9667df4ffb8 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -6,7 +6,7 @@ module Gitlab module Asciidoc DEFAULT_ADOC_ATTRS = [ 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', - 'env-gitlab', 'source-highlighter=html-pipeline' + 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font' ].freeze # Public: Converts the provided Asciidoc markup into HTML. From e41c3f698d82be51076bf83a3e7d4d90d9fc7efb Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 8 Dec 2016 11:07:45 +1100 Subject: [PATCH 095/140] Correct previous stable branch used in 8.14 to 8.15 update guide [ci skip] --- doc/update/8.14-to-8.15.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/update/8.14-to-8.15.md b/doc/update/8.14-to-8.15.md index 576b943b98c..3f58493fa63 100644 --- a/doc/update/8.14-to-8.15.md +++ b/doc/update/8.14-to-8.15.md @@ -113,7 +113,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: ```sh -git diff origin/8-13-stable:config/gitlab.yml.example origin/8-15-stable:config/gitlab.yml.example +git diff origin/8-14-stable:config/gitlab.yml.example origin/8-15-stable:config/gitlab.yml.example ``` #### Git configuration @@ -131,10 +131,10 @@ Ensure you're still up-to-date with the latest NGINX configuration changes: ```sh # For HTTPS configurations -git diff origin/8-13-stable:lib/support/nginx/gitlab-ssl origin/8-15-stable:lib/support/nginx/gitlab-ssl +git diff origin/8-14-stable:lib/support/nginx/gitlab-ssl origin/8-15-stable:lib/support/nginx/gitlab-ssl # For HTTP configurations -git diff origin/8-13-stable:lib/support/nginx/gitlab origin/8-15-stable:lib/support/nginx/gitlab +git diff origin/8-14-stable:lib/support/nginx/gitlab origin/8-15-stable:lib/support/nginx/gitlab ``` If you are using Apache instead of NGINX please see the updated [Apache templates]. From c6acc7ed437e4dfef3fed9346718e67cd5905547 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 29 Nov 2016 14:51:45 -0200 Subject: [PATCH 096/140] Render SVG as images in notes --- app/uploaders/uploader_helper.rb | 2 +- changelogs/unreleased/render-svg-in-diffs-and-notes.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/render-svg-in-diffs-and-notes.yml diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb index b10ad71d052..fbaea2744a3 100644 --- a/app/uploaders/uploader_helper.rb +++ b/app/uploaders/uploader_helper.rb @@ -1,6 +1,6 @@ # Extra methods for uploader module UploaderHelper - IMAGE_EXT = %w[png jpg jpeg gif bmp tiff] + IMAGE_EXT = %w[png jpg jpeg gif bmp tiff svg] # We recommend using the .mp4 format over .mov. Videos in .mov format can # still be used but you really need to make sure they are served with the # proper MIME type video/mp4 and not video/quicktime or your videos won't play diff --git a/changelogs/unreleased/render-svg-in-diffs-and-notes.yml b/changelogs/unreleased/render-svg-in-diffs-and-notes.yml new file mode 100644 index 00000000000..827b0dbb1d3 --- /dev/null +++ b/changelogs/unreleased/render-svg-in-diffs-and-notes.yml @@ -0,0 +1,4 @@ +--- +title: Render SVG images in diffs and notes +merge_request: 7747 +author: andrebsguedes From 9e136aa9973323413189498a26d06672ecee295d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 8 Dec 2016 17:30:07 +1100 Subject: [PATCH 097/140] Update factory_girl_rails to 4.7.0 --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index f49ef0386e7..f27d6363e3d 100644 --- a/Gemfile +++ b/Gemfile @@ -271,7 +271,7 @@ group :development, :test do gem 'fuubar', '~> 2.0.0' gem 'database_cleaner', '~> 1.5.0' - gem 'factory_girl_rails', '~> 4.6.0' + gem 'factory_girl_rails', '~> 4.7.0' gem 'rspec-rails', '~> 3.5.0' gem 'rspec-retry', '~> 0.4.5' gem 'spinach-rails', '~> 0.2.1' diff --git a/Gemfile.lock b/Gemfile.lock index 7a024e81ad2..c464ff70587 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -177,10 +177,10 @@ GEM excon (0.52.0) execjs (2.6.0) expression_parser (0.9.0) - factory_girl (4.5.0) + factory_girl (4.7.0) activesupport (>= 3.0.0) - factory_girl_rails (4.6.0) - factory_girl (~> 4.5.0) + factory_girl_rails (4.7.0) + factory_girl (~> 4.7.0) railties (>= 3.0.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) @@ -819,7 +819,7 @@ DEPENDENCIES dropzonejs-rails (~> 0.7.1) email_reply_parser (~> 0.5.8) email_spec (~> 1.6.0) - factory_girl_rails (~> 4.6.0) + factory_girl_rails (~> 4.7.0) ffaker (~> 2.0.0) flay (~> 2.6.1) fog-aws (~> 0.9) From 83232be0e14cc8b35bf74532203a6e4371c15e70 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 31 Oct 2016 13:00:53 +0200 Subject: [PATCH 098/140] Add nested groups support on data level * add parent_id field to namespaces table to store relation with nested groups * create routes table to keep information about full path of every group and project * project/group lookup by full path from routes table Signed-off-by: Dmitriy Zaporozhets --- app/controllers/admin/groups_controller.rb | 2 +- .../groups/application_controller.rb | 2 +- app/helpers/groups_helper.rb | 2 +- app/models/concerns/routable.rb | 70 +++++++++++++ app/models/namespace.rb | 18 +++- app/models/project.rb | 97 +++---------------- app/models/route.rb | 22 +++++ app/services/destroy_group_service.rb | 4 + app/views/projects/forks/error.html.haml | 6 +- changelogs/unreleased/dz-nested-groups.yml | 4 + ...161124111390_add_parent_id_to_namespace.rb | 12 +++ .../20161124111395_add_index_to_parent_id.rb | 14 +++ db/migrate/20161124111402_add_routes_table.rb | 18 ++++ .../20161130095245_fill_routes_table.rb | 21 ++++ ...161130101252_fill_projects_routes_table.rb | 22 +++++ ...202152031_remove_duplicates_from_routes.rb | 28 ++++++ .../20161202152035_add_index_to_routes.rb | 16 +++ db/schema.rb | 25 +++-- lib/api/helpers.rb | 2 +- lib/constraints/group_url_constrainer.rb | 2 +- .../constraints/group_url_constrainer_spec.rb | 7 ++ spec/lib/gitlab/import_export/all_models.yml | 1 + spec/models/concerns/routable_spec.rb | 67 +++++++++++++ spec/models/namespace_spec.rb | 8 ++ spec/models/project_spec.rb | 62 ------------ spec/models/route_spec.rb | 29 ++++++ 26 files changed, 402 insertions(+), 159 deletions(-) create mode 100644 app/models/concerns/routable.rb create mode 100644 app/models/route.rb create mode 100644 changelogs/unreleased/dz-nested-groups.yml create mode 100644 db/migrate/20161124111390_add_parent_id_to_namespace.rb create mode 100644 db/migrate/20161124111395_add_index_to_parent_id.rb create mode 100644 db/migrate/20161124111402_add_routes_table.rb create mode 100644 db/migrate/20161130095245_fill_routes_table.rb create mode 100644 db/migrate/20161130101252_fill_projects_routes_table.rb create mode 100644 db/migrate/20161202152031_remove_duplicates_from_routes.rb create mode 100644 db/migrate/20161202152035_add_index_to_routes.rb create mode 100644 spec/models/concerns/routable_spec.rb create mode 100644 spec/models/route_spec.rb diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index aa7570cd896..1e3d194e9f9 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -56,7 +56,7 @@ class Admin::GroupsController < Admin::ApplicationController private def group - @group ||= Group.find_by(path: params[:id]) + @group ||= Group.find_by_full_path(params[:id]) end def group_params diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index 949b4a6c25a..c411c21bb80 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -9,7 +9,7 @@ class Groups::ApplicationController < ApplicationController def group unless @group id = params[:group_id] || params[:id] - @group = Group.find_by(path: id) + @group = Group.find_by_full_path(id) unless @group && can?(current_user, :read_group, @group) @group = nil diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 19ab059aea6..f6d4ea4659a 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -5,7 +5,7 @@ module GroupsHelper def group_icon(group) if group.is_a?(String) - group = Group.find_by(path: group) + group = Group.find_by_full_path(group) end group.try(:avatar_url) || image_path('no_group_avatar.png') diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb new file mode 100644 index 00000000000..d36bb9da296 --- /dev/null +++ b/app/models/concerns/routable.rb @@ -0,0 +1,70 @@ +# Store object full path in separate table for easy lookup and uniq validation +# Object must have path db field and respond to full_path and full_path_changed? methods. +module Routable + extend ActiveSupport::Concern + + included do + has_one :route, as: :source, autosave: true, dependent: :destroy + + validates_associated :route + + before_validation :update_route_path, if: :full_path_changed? + end + + class_methods do + # Finds a single object by full path match in routes table. + # + # Usage: + # + # Klass.find_by_full_path('gitlab-org/gitlab-ce') + # + # Returns a single object, or nil. + def find_by_full_path(path) + # On MySQL we want to ensure the ORDER BY uses a case-sensitive match so + # any literal matches come first, for this we have to use "BINARY". + # Without this there's still no guarantee in what order MySQL will return + # rows. + binary = Gitlab::Database.mysql? ? 'BINARY' : '' + + order_sql = "(CASE WHEN #{binary} routes.path = #{connection.quote(path)} THEN 0 ELSE 1 END)" + + where_paths_in([path]).reorder(order_sql).take + end + + # Builds a relation to find multiple objects by their full paths. + # + # Usage: + # + # Klass.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee}) + # + # Returns an ActiveRecord::Relation. + def where_paths_in(paths) + wheres = [] + cast_lower = Gitlab::Database.postgresql? + + paths.each do |path| + path = connection.quote(path) + where = "(routes.path = #{path})" + + if cast_lower + where = "(#{where} OR (LOWER(routes.path) = LOWER(#{path})))" + end + + wheres << where + end + + if wheres.empty? + none + else + joins(:route).where(wheres.join(' OR ')) + end + end + end + + private + + def update_route_path + route || build_route(source: self) + route.path = full_path + end +end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 891dffac648..ff161e4c53f 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -4,12 +4,16 @@ class Namespace < ActiveRecord::Base include CacheMarkdownField include Sortable include Gitlab::ShellAdapter + include Routable cache_markdown_field :description, pipeline: :description has_many :projects, dependent: :destroy belongs_to :owner, class_name: "User" + belongs_to :parent, class_name: "Namespace" + has_many :children, class_name: "Namespace", foreign_key: :parent_id + validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, length: { within: 0..255 }, @@ -86,7 +90,7 @@ class Namespace < ActiveRecord::Base end def to_param - path + full_path end def human_name @@ -150,6 +154,14 @@ class Namespace < ActiveRecord::Base Gitlab.config.lfs.enabled end + def full_path + if parent + parent.full_path + '/' + path + else + path + end + end + private def repository_storage_paths @@ -185,4 +197,8 @@ class Namespace < ActiveRecord::Base where(projects: { namespace_id: id }). find_each(&:refresh_members_authorized_projects) end + + def full_path_changed? + path_changed? || parent_id_changed? + end end diff --git a/app/models/project.rb b/app/models/project.rb index 9d58aff4033..7659a7bf152 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -14,6 +14,7 @@ class Project < ActiveRecord::Base include TokenAuthenticatable include ProjectFeaturesCompatibility include SelectForProjectAuthorization + include Routable extend Gitlab::ConfigHelper @@ -324,87 +325,6 @@ class Project < ActiveRecord::Base non_archived.where(table[:name].matches(pattern)) end - # Finds a single project for the given path. - # - # path - The full project path (including namespace path). - # - # Returns a Project, or nil if no project could be found. - def find_with_namespace(path) - namespace_path, project_path = path.split('/', 2) - - return unless namespace_path && project_path - - namespace_path = connection.quote(namespace_path) - project_path = connection.quote(project_path) - - # On MySQL we want to ensure the ORDER BY uses a case-sensitive match so - # any literal matches come first, for this we have to use "BINARY". - # Without this there's still no guarantee in what order MySQL will return - # rows. - binary = Gitlab::Database.mysql? ? 'BINARY' : '' - - order_sql = "(CASE WHEN #{binary} namespaces.path = #{namespace_path} " \ - "AND #{binary} projects.path = #{project_path} THEN 0 ELSE 1 END)" - - where_paths_in([path]).reorder(order_sql).take - end - - # Builds a relation to find multiple projects by their full paths. - # - # Each path must be in the following format: - # - # namespace_path/project_path - # - # For example: - # - # gitlab-org/gitlab-ce - # - # Usage: - # - # Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee}) - # - # This would return the projects with the full paths matching the values - # given. - # - # paths - An Array of full paths (namespace path + project path) for which - # to find the projects. - # - # Returns an ActiveRecord::Relation. - def where_paths_in(paths) - wheres = [] - cast_lower = Gitlab::Database.postgresql? - - paths.each do |path| - namespace_path, project_path = path.split('/', 2) - - next unless namespace_path && project_path - - namespace_path = connection.quote(namespace_path) - project_path = connection.quote(project_path) - - where = "(namespaces.path = #{namespace_path} - AND projects.path = #{project_path})" - - if cast_lower - where = "( - #{where} - OR ( - LOWER(namespaces.path) = LOWER(#{namespace_path}) - AND LOWER(projects.path) = LOWER(#{project_path}) - ) - )" - end - - wheres << where - end - - if wheres.empty? - none - else - joins(:namespace).where(wheres.join(' OR ')) - end - end - def visibility_levels Gitlab::VisibilityLevel.options end @@ -440,6 +360,10 @@ class Project < ActiveRecord::Base def group_ids joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id) end + + # Add alias for Routable method for compatibility with old code. + # In future all calls `find_with_namespace` should be replaced with `find_by_full_path` + alias_method :find_with_namespace, :find_by_full_path end def lfs_enabled? @@ -879,13 +803,14 @@ class Project < ActiveRecord::Base end alias_method :human_name, :name_with_namespace - def path_with_namespace - if namespace - namespace.path + '/' + path + def full_path + if namespace && path + namespace.full_path + '/' + path else path end end + alias_method :path_with_namespace, :full_path def execute_hooks(data, hooks_scope = :push_hooks) hooks.send(hooks_scope).each do |hook| @@ -1373,4 +1298,8 @@ class Project < ActiveRecord::Base def validate_board_limit(board) raise BoardLimitExceeded, 'Number of permitted boards exceeded' if boards.size >= NUMBER_OF_PERMITTED_BOARDS end + + def full_path_changed? + path_changed? || namespace_id_changed? + end end diff --git a/app/models/route.rb b/app/models/route.rb new file mode 100644 index 00000000000..d40214b9da6 --- /dev/null +++ b/app/models/route.rb @@ -0,0 +1,22 @@ +class Route < ActiveRecord::Base + belongs_to :source, polymorphic: true + + validates :source, presence: true + + validates :path, + length: { within: 1..255 }, + presence: true, + uniqueness: { case_sensitive: false } + + after_update :rename_children, if: :path_changed? + + def rename_children + # We update each row separately because MySQL does not have regexp_replace. + # rubocop:disable Rails/FindEach + Route.where('path LIKE ?', "#{path_was}%").each do |route| + # Note that update column skips validation and callbacks. + # We need this to avoid recursive call of rename_children method + route.update_column(:path, route.path.sub(path_was, path)) + end + end +end diff --git a/app/services/destroy_group_service.rb b/app/services/destroy_group_service.rb index a880952e274..2316c57bf1e 100644 --- a/app/services/destroy_group_service.rb +++ b/app/services/destroy_group_service.rb @@ -20,6 +20,10 @@ class DestroyGroupService ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute end + group.children.each do |group| + DestroyGroupService.new(group, current_user).async_execute + end + group.really_destroy! end end diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml index 3d0ab5b85d6..98d81308407 100644 --- a/app/views/projects/forks/error.html.haml +++ b/app/views/projects/forks/error.html.haml @@ -13,7 +13,11 @@ - if @forked_project && @forked_project.errors.any? %p – - = @forked_project.errors.full_messages.first + - error = @forked_project.errors.full_messages.first + - if error.include?("already been taken") + Name has already been taken + - else + = error %p = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork", class: "btn" do diff --git a/changelogs/unreleased/dz-nested-groups.yml b/changelogs/unreleased/dz-nested-groups.yml new file mode 100644 index 00000000000..c227c5a8ea5 --- /dev/null +++ b/changelogs/unreleased/dz-nested-groups.yml @@ -0,0 +1,4 @@ +--- +title: Add nested groups support on data level +merge_request: +author: diff --git a/db/migrate/20161124111390_add_parent_id_to_namespace.rb b/db/migrate/20161124111390_add_parent_id_to_namespace.rb new file mode 100644 index 00000000000..a6fa1b70a9d --- /dev/null +++ b/db/migrate/20161124111390_add_parent_id_to_namespace.rb @@ -0,0 +1,12 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddParentIdToNamespace < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column(:namespaces, :parent_id, :integer) + end +end diff --git a/db/migrate/20161124111395_add_index_to_parent_id.rb b/db/migrate/20161124111395_add_index_to_parent_id.rb new file mode 100644 index 00000000000..eab74c01dfd --- /dev/null +++ b/db/migrate/20161124111395_add_index_to_parent_id.rb @@ -0,0 +1,14 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddIndexToParentId < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def change + add_concurrent_index(:namespaces, [:parent_id, :id], unique: true) + end +end diff --git a/db/migrate/20161124111402_add_routes_table.rb b/db/migrate/20161124111402_add_routes_table.rb new file mode 100644 index 00000000000..a02e046a18e --- /dev/null +++ b/db/migrate/20161124111402_add_routes_table.rb @@ -0,0 +1,18 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddRoutesTable < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + create_table :routes do |t| + t.integer :source_id, null: false + t.string :source_type, null: false + t.string :path, null: false + + t.timestamps + end + end +end diff --git a/db/migrate/20161130095245_fill_routes_table.rb b/db/migrate/20161130095245_fill_routes_table.rb new file mode 100644 index 00000000000..6754e583000 --- /dev/null +++ b/db/migrate/20161130095245_fill_routes_table.rb @@ -0,0 +1,21 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class FillRoutesTable < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + DOWNTIME_REASON = 'No new namespaces should be created during data copy' + + def up + execute <<-EOF + INSERT INTO routes + (source_id, source_type, path) + (SELECT id, 'Namespace', path FROM namespaces) + EOF + end + + def down + Route.delete_all(source_type: 'Namespace') + end +end diff --git a/db/migrate/20161130101252_fill_projects_routes_table.rb b/db/migrate/20161130101252_fill_projects_routes_table.rb new file mode 100644 index 00000000000..14700583be5 --- /dev/null +++ b/db/migrate/20161130101252_fill_projects_routes_table.rb @@ -0,0 +1,22 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class FillProjectsRoutesTable < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + DOWNTIME_REASON = 'No new projects should be created during data copy' + + def up + execute <<-EOF + INSERT INTO routes + (source_id, source_type, path) + (SELECT projects.id, 'Project', concat(namespaces.path, '/', projects.path) FROM projects + INNER JOIN namespaces ON projects.namespace_id = namespaces.id) + EOF + end + + def down + Route.delete_all(source_type: 'Project') + end +end diff --git a/db/migrate/20161202152031_remove_duplicates_from_routes.rb b/db/migrate/20161202152031_remove_duplicates_from_routes.rb new file mode 100644 index 00000000000..510796e05f2 --- /dev/null +++ b/db/migrate/20161202152031_remove_duplicates_from_routes.rb @@ -0,0 +1,28 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveDuplicatesFromRoutes < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + select_all("SELECT path FROM #{quote_table_name(:routes)} GROUP BY path HAVING COUNT(*) > 1").each do |row| + path = connection.quote(row['path']) + execute(%Q{ + DELETE FROM #{quote_table_name(:routes)} + WHERE path = #{path} + AND id != ( + SELECT id FROM ( + SELECT max(id) AS id + FROM #{quote_table_name(:routes)} + WHERE path = #{path} + ) max_ids + ) + }) + end + end + + def down + end +end diff --git a/db/migrate/20161202152035_add_index_to_routes.rb b/db/migrate/20161202152035_add_index_to_routes.rb new file mode 100644 index 00000000000..4a51337bda6 --- /dev/null +++ b/db/migrate/20161202152035_add_index_to_routes.rb @@ -0,0 +1,16 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddIndexToRoutes < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def change + add_concurrent_index(:routes, :path, unique: true) + add_concurrent_index(:routes, [:source_type, :source_id], unique: true) + end +end diff --git a/db/schema.rb b/db/schema.rb index 0d510c8a269..9c46f573719 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161128161412) do +ActiveRecord::Schema.define(version: 20161202152035) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -98,14 +98,14 @@ ActiveRecord::Schema.define(version: 20161128161412) do t.text "help_page_text_html" t.text "shared_runners_text_html" t.text "after_sign_up_text_html" - t.boolean "sidekiq_throttling_enabled", default: false - t.string "sidekiq_throttling_queues" - t.decimal "sidekiq_throttling_factor" t.boolean "housekeeping_enabled", default: true, null: false t.boolean "housekeeping_bitmaps_enabled", default: true, null: false t.integer "housekeeping_incremental_repack_period", default: 10, null: false t.integer "housekeeping_full_repack_period", default: 50, null: false t.integer "housekeeping_gc_period", default: 200, null: false + t.boolean "sidekiq_throttling_enabled", default: false + t.string "sidekiq_throttling_queues" + t.decimal "sidekiq_throttling_factor" t.boolean "html_emails_enabled", default: true end @@ -737,8 +737,9 @@ ActiveRecord::Schema.define(version: 20161128161412) do t.integer "visibility_level", default: 20, null: false t.boolean "request_access_enabled", default: false, null: false t.datetime "deleted_at" - t.boolean "lfs_enabled" t.text "description_html" + t.boolean "lfs_enabled" + t.integer "parent_id" end add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree @@ -746,6 +747,7 @@ ActiveRecord::Schema.define(version: 20161128161412) do add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree add_index "namespaces", ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree + add_index "namespaces", ["parent_id", "id"], name: "index_namespaces_on_parent_id_and_id", unique: true, using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"} add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree @@ -991,6 +993,17 @@ ActiveRecord::Schema.define(version: 20161128161412) do add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree + create_table "routes", force: :cascade do |t| + t.integer "source_id", null: false + t.string "source_type", null: false + t.string "path", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "routes", ["path"], name: "index_routes_on_path", unique: true, using: :btree + add_index "routes", ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree + create_table "sent_notifications", force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" @@ -1206,8 +1219,8 @@ ActiveRecord::Schema.define(version: 20161128161412) do t.datetime "otp_grace_period_started_at" t.boolean "ldap_email", default: false, null: false t.boolean "external", default: false - t.string "organization" t.string "incoming_email_token" + t.string "organization" t.boolean "authorized_projects_populated" end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 7f94ede7940..96ccde760db 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -108,7 +108,7 @@ module API if id =~ /^\d+$/ Group.find_by(id: id) else - Group.find_by(path: id) + Group.find_by_full_path(id) end end diff --git a/lib/constraints/group_url_constrainer.rb b/lib/constraints/group_url_constrainer.rb index 5711d96a586..bae4db1ca4d 100644 --- a/lib/constraints/group_url_constrainer.rb +++ b/lib/constraints/group_url_constrainer.rb @@ -4,7 +4,7 @@ class GroupUrlConstrainer return false unless valid?(id) - Group.find_by(path: id).present? + Group.find_by_full_path(id).present? end private diff --git a/spec/lib/constraints/group_url_constrainer_spec.rb b/spec/lib/constraints/group_url_constrainer_spec.rb index 892554f2870..96dacdc5cd2 100644 --- a/spec/lib/constraints/group_url_constrainer_spec.rb +++ b/spec/lib/constraints/group_url_constrainer_spec.rb @@ -10,6 +10,13 @@ describe GroupUrlConstrainer, lib: true do it { expect(subject.matches?(request)).to be_truthy } end + context 'valid request for nested group' do + let!(:nested_group) { create(:group, path: 'nested', parent: group) } + let!(:request) { build_request('gitlab/nested') } + + it { expect(subject.matches?(request)).to be_truthy } + end + context 'invalid request' do let(:request) { build_request('foo') } diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 7e00e214c6e..8e1a28f2723 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -188,6 +188,7 @@ project: - project_feature - authorized_users - project_authorizations +- route award_emoji: - awardable - user diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb new file mode 100644 index 00000000000..0acefc0c1d5 --- /dev/null +++ b/spec/models/concerns/routable_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe Group, 'Routable' do + let!(:group) { create(:group) } + + describe 'Associations' do + it { is_expected.to have_one(:route).dependent(:destroy) } + end + + describe 'Callbacks' do + it 'creates route record on create' do + expect(group.route.path).to eq(group.path) + end + + it 'updates route record on path change' do + group.update_attributes(path: 'wow') + + expect(group.route.path).to eq('wow') + end + + it 'ensure route path uniqueness across different objects' do + create(:group, parent: group, path: 'xyz') + duplicate = build(:project, namespace: group, path: 'xyz') + + expect { duplicate.save! }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Route path has already been taken, Route is invalid') + end + end + + describe '.find_by_full_path' do + let!(:nested_group) { create(:group, parent: group) } + + it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) } + it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) } + it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) } + it { expect(described_class.find_by_full_path('unknown')).to eq(nil) } + end + + describe '.where_paths_in' do + context 'without any paths' do + it 'returns an empty relation' do + expect(described_class.where_paths_in([])).to eq([]) + end + end + + context 'without any valid paths' do + it 'returns an empty relation' do + expect(described_class.where_paths_in(%w[unknown])).to eq([]) + end + end + + context 'with valid paths' do + let!(:nested_group) { create(:group, parent: group) } + + it 'returns the projects matching the paths' do + result = described_class.where_paths_in([group.to_param, nested_group.to_param]) + + expect(result).to contain_exactly(group, nested_group) + end + + it 'returns projects regardless of the casing of paths' do + result = described_class.where_paths_in([group.to_param.upcase, nested_group.to_param.upcase]) + + expect(result).to contain_exactly(group, nested_group) + end + end + end +end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 431b3e4435f..67f6a0bd4fe 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -117,4 +117,12 @@ describe Namespace, models: true do expect(Namespace.clean_path("--%+--valid_*&%name=.git.%.atom.atom.@email.com")).to eq("valid_name") end end + + describe '#full_path' do + let(:group) { create(:group) } + let(:nested_group) { create(:group, parent: group) } + + it { expect(group.full_path).to eq(group.path) } + it { expect(nested_group.full_path).to eq("#{group.path}/#{nested_group.path}") } + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 587ca1936a3..1b9e69933c1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -474,35 +474,6 @@ describe Project, models: true do end end - describe '.find_with_namespace' do - context 'with namespace' do - before do - @group = create :group, name: 'gitlab' - @project = create(:project, name: 'gitlabhq', namespace: @group) - end - - it { expect(Project.find_with_namespace('gitlab/gitlabhq')).to eq(@project) } - it { expect(Project.find_with_namespace('GitLab/GitlabHQ')).to eq(@project) } - it { expect(Project.find_with_namespace('gitlab-ci')).to be_nil } - end - - context 'when multiple projects using a similar name exist' do - let(:group) { create(:group, name: 'gitlab') } - - let!(:project1) do - create(:empty_project, name: 'gitlab1', path: 'gitlab', namespace: group) - end - - let!(:project2) do - create(:empty_project, name: 'gitlab2', path: 'GITLAB', namespace: group) - end - - it 'returns the row where the path matches literally' do - expect(Project.find_with_namespace('gitlab/GITLAB')).to eq(project2) - end - end - end - describe '#to_param' do context 'with namespace' do before do @@ -1544,39 +1515,6 @@ describe Project, models: true do end end - describe '.where_paths_in' do - context 'without any paths' do - it 'returns an empty relation' do - expect(Project.where_paths_in([])).to eq([]) - end - end - - context 'without any valid paths' do - it 'returns an empty relation' do - expect(Project.where_paths_in(%w[foo])).to eq([]) - end - end - - context 'with valid paths' do - let!(:project1) { create(:project) } - let!(:project2) { create(:project) } - - it 'returns the projects matching the paths' do - projects = Project.where_paths_in([project1.path_with_namespace, - project2.path_with_namespace]) - - expect(projects).to contain_exactly(project1, project2) - end - - it 'returns projects regardless of the casing of paths' do - projects = Project.where_paths_in([project1.path_with_namespace.upcase, - project2.path_with_namespace.upcase]) - - expect(projects).to contain_exactly(project1, project2) - end - end - end - describe 'change_head' do let(:project) { create(:project) } diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb new file mode 100644 index 00000000000..6f491fdf9a0 --- /dev/null +++ b/spec/models/route_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe Route, models: true do + let!(:group) { create(:group) } + let!(:route) { group.route } + + describe 'relationships' do + it { is_expected.to belong_to(:source) } + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:source) } + it { is_expected.to validate_presence_of(:path) } + it { is_expected.to validate_uniqueness_of(:path) } + end + + describe '#rename_children' do + let!(:nested_group) { create(:group, path: "test", parent: group) } + let!(:deep_nested_group) { create(:group, path: "foo", parent: nested_group) } + + it "updates children routes with new path" do + route.update_attributes(path: 'bar') + + expect(described_class.exists?(path: 'bar')).to be_truthy + expect(described_class.exists?(path: 'bar/test')).to be_truthy + expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy + end + end +end From fe447b43ac8a965b533c056f823d392f0c9db5ac Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 8 Dec 2016 11:53:21 +0100 Subject: [PATCH 099/140] Use gitlab-workhose 1.1.1 Confines API rate limiting feature to builds/register.json CI requests only. --- GITLAB_WORKHORSE_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 9084fa2f716..524cb55242b 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -1.1.0 +1.1.1 From fd87bf3c2c4d3064a1a4eb1867d9a093172fed6e Mon Sep 17 00:00:00 2001 From: Dimitrie Hoekstra Date: Thu, 8 Dec 2016 14:35:17 +0100 Subject: [PATCH 100/140] Various small emoji positioning adjustments --- app/assets/stylesheets/framework/awards.scss | 5 +++-- app/assets/stylesheets/framework/common.scss | 1 + app/assets/stylesheets/pages/notes.scss | 1 - changelogs/unreleased/small-emoji-adjustments.yml | 4 ++++ 4 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/small-emoji-adjustments.yml diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss index c13cb4a02b2..dece5c3202b 100644 --- a/app/assets/stylesheets/framework/awards.scss +++ b/app/assets/stylesheets/framework/awards.scss @@ -1,7 +1,7 @@ .awards { .emoji-icon { - width: 19px; - height: 19px; + width: 20px; + height: 20px; } } @@ -136,5 +136,6 @@ .award-control-icon { color: $award-emoji-new-btn-icon-color; + margin-top: 1px; } } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 600bf17259b..251e43d2edd 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -255,6 +255,7 @@ img.emoji { height: 20px; vertical-align: top; width: 20px; + margin-top: 1px; } .chart { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 16b099c09eb..30f41a37ab3 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -413,7 +413,6 @@ ul.notes { .fa { color: $notes-action-color; position: relative; - top: 1px; font-size: 17px; } diff --git a/changelogs/unreleased/small-emoji-adjustments.yml b/changelogs/unreleased/small-emoji-adjustments.yml new file mode 100644 index 00000000000..804bd05b613 --- /dev/null +++ b/changelogs/unreleased/small-emoji-adjustments.yml @@ -0,0 +1,4 @@ +--- +title: Various small emoji positioning adjustments +merge_request: +author: From aba894b94fd0f602781d2438c5419cfa2aa0cfaa Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 8 Dec 2016 15:48:46 +0100 Subject: [PATCH 101/140] Don't check if stage name doesn't exist --- app/views/projects/stage/_graph.html.haml | 3 +-- app/views/projects/stage/_stage.html.haml | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/views/projects/stage/_graph.html.haml b/app/views/projects/stage/_graph.html.haml index d8c87fae5a1..1d8fa10db0c 100644 --- a/app/views/projects/stage/_graph.html.haml +++ b/app/views/projects/stage/_graph.html.haml @@ -4,8 +4,7 @@ %li.stage-column .stage-name %a{ name: stage.name } - - if stage.name - = stage.name.titleize + = stage.name.titleize .builds-container %ul - status_groups.each do |group_name, grouped_statuses| diff --git a/app/views/projects/stage/_stage.html.haml b/app/views/projects/stage/_stage.html.haml index 1eca375db3d..1684e02fbad 100644 --- a/app/views/projects/stage/_stage.html.haml +++ b/app/views/projects/stage/_stage.html.haml @@ -4,9 +4,8 @@ %a{ name: stage.name } %span{class: "ci-status-link ci-status-icon-#{stage.status}"} = ci_icon_for_status(stage.status) - - if stage.name -   - = stage.name.titleize +   + = stage.name.titleize = render stage.statuses.latest_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true = render stage.statuses.retried_ordered, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true %tr From e374ab7b42967975f783098c6b825a6bca0eda56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Thu, 8 Dec 2016 13:21:21 -0300 Subject: [PATCH 102/140] Update CHANGELOG.md for 8.14.4 [ci skip] --- CHANGELOG.md | 13 +++++++++++++ .../unreleased/23696-fix-diff-view-highlighting.yml | 4 ---- .../24537-reenable-private-token-with-sudo.yml | 5 ----- changelogs/unreleased/24814-pipeline-tabs.yml | 4 ---- ...authorize-users-into-imported-gitlab-project.yml | 4 ---- ...x-compatibility-with-ie11-for-merge-requests.yml | 4 ---- changelogs/unreleased/fix-slack-pipeline-event.yml | 4 ---- .../remove-has-visible-content-caching.yml | 4 ---- 8 files changed, 13 insertions(+), 29 deletions(-) delete mode 100644 changelogs/unreleased/23696-fix-diff-view-highlighting.yml delete mode 100644 changelogs/unreleased/24537-reenable-private-token-with-sudo.yml delete mode 100644 changelogs/unreleased/24814-pipeline-tabs.yml delete mode 100644 changelogs/unreleased/fix-authorize-users-into-imported-gitlab-project.yml delete mode 100644 changelogs/unreleased/fix-compatibility-with-ie11-for-merge-requests.yml delete mode 100644 changelogs/unreleased/fix-slack-pipeline-event.yml delete mode 100644 changelogs/unreleased/remove-has-visible-content-caching.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index e03123111c3..82fbdf89a68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 8.14.4 (2016-12-08) + +- Fix diff view permalink highlighting. !7090 +- Fix pipeline author for Slack and use pipeline id for pipeline link. !7506 +- Fix compatibility with Internet Explorer 11 for merge requests. !7525 (Steffen Rauh) +- Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615 +- Fix Cicking on tabs on pipeline page should set URL. !7709 +- Authorize users into imported GitLab project. +- Destroy a user's session when they delete their own account. +- Don't accidentally mark unsafe diff lines as HTML safe. +- Replace MR access checks with use of MergeRequestsFinder. +- Remove visible content caching. + ## 8.14.3 (2016-12-02) - Pass commit data to ProcessCommitWorker to reduce Git overhead. !7744 diff --git a/changelogs/unreleased/23696-fix-diff-view-highlighting.yml b/changelogs/unreleased/23696-fix-diff-view-highlighting.yml deleted file mode 100644 index db523caffed..00000000000 --- a/changelogs/unreleased/23696-fix-diff-view-highlighting.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix diff view permalink highlighting -merge_request: 7090 -author: diff --git a/changelogs/unreleased/24537-reenable-private-token-with-sudo.yml b/changelogs/unreleased/24537-reenable-private-token-with-sudo.yml deleted file mode 100644 index 9fbbaeb914d..00000000000 --- a/changelogs/unreleased/24537-reenable-private-token-with-sudo.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Reenables /user API request to return private-token if user is admin and request - is made with sudo -merge_request: 7615 -author: diff --git a/changelogs/unreleased/24814-pipeline-tabs.yml b/changelogs/unreleased/24814-pipeline-tabs.yml deleted file mode 100644 index f85e7576905..00000000000 --- a/changelogs/unreleased/24814-pipeline-tabs.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Cicking on tabs on pipeline page should set URL -merge_request: 7709 -author: diff --git a/changelogs/unreleased/fix-authorize-users-into-imported-gitlab-project.yml b/changelogs/unreleased/fix-authorize-users-into-imported-gitlab-project.yml deleted file mode 100644 index 9f14463fdd1..00000000000 --- a/changelogs/unreleased/fix-authorize-users-into-imported-gitlab-project.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Authorize users into imported GitLab project -merge_request: -author: diff --git a/changelogs/unreleased/fix-compatibility-with-ie11-for-merge-requests.yml b/changelogs/unreleased/fix-compatibility-with-ie11-for-merge-requests.yml deleted file mode 100644 index db92e45d8f1..00000000000 --- a/changelogs/unreleased/fix-compatibility-with-ie11-for-merge-requests.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix compatibility with Internet Explorer 11 for merge requests -merge_request: 7525 -author: Steffen Rauh diff --git a/changelogs/unreleased/fix-slack-pipeline-event.yml b/changelogs/unreleased/fix-slack-pipeline-event.yml deleted file mode 100644 index fec864eeb3d..00000000000 --- a/changelogs/unreleased/fix-slack-pipeline-event.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix pipeline author for Slack and use pipeline id for pipeline link -merge_request: 7506 -author: diff --git a/changelogs/unreleased/remove-has-visible-content-caching.yml b/changelogs/unreleased/remove-has-visible-content-caching.yml deleted file mode 100644 index e2940c60443..00000000000 --- a/changelogs/unreleased/remove-has-visible-content-caching.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Remove visible content caching -merge_request: -author: From 55da922b86ed81f967b6fcac618171f61b4fb2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Thu, 8 Dec 2016 13:28:50 -0300 Subject: [PATCH 103/140] Update CHANGELOG.md for 8.13.9 [ci skip] --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82fbdf89a68..9f0e29a5d82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -264,6 +264,11 @@ entry. - Fix "Without projects" filter. !6611 (Ben Bodenmiller) - Fix 404 when visit /projects page +## 8.13.9 (2016-12-08) + +- Reenables /user API request to return private-token if user is admin and request is made with sudo. !7615 +- Replace MR access checks with use of MergeRequestsFinder. + ## 8.13.8 (2016-12-02) - Pass tag SHA to post-receive hook when tag is created via UI. !7700 From d74ad9263048549e4d90e6a22313768109eaf2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 8 Dec 2016 18:29:25 +0100 Subject: [PATCH 104/140] Use a single query in Projects::ProjectMembersController to fetch members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .../projects/project_members_controller.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 3fb8bba3cd0..53308948f62 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -35,13 +35,12 @@ class Projects::ProjectMembersController < Projects::ApplicationController @group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id)) end - member_ids = @project_members.pluck(:id) + wheres = ["id IN (#{@project_members.select(:id).to_sql})"] + wheres << "id IN (#{group_members.select(:id).to_sql})" if group_members - if group_members - member_ids += group_members.pluck(:id) - end - - @project_members = Member.where(id: member_ids).order(access_level: :desc).page(params[:page]) + @project_members = Member. + where(wheres.join(' OR ')). + order(access_level: :desc).page(params[:page]) @requesters = AccessRequestsFinder.new(@project).execute(current_user) From c1a71d9f58d76973c5ceae3605df1cd3c5f9f287 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Wed, 7 Dec 2016 16:00:07 -0200 Subject: [PATCH 105/140] Update JIRA troubleshoot documentation --- doc/project_services/jira.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/project_services/jira.md b/doc/project_services/jira.md index 366e4b2d306..390066c9989 100644 --- a/doc/project_services/jira.md +++ b/doc/project_services/jira.md @@ -197,6 +197,7 @@ incorrectly the JIRA-GitLab integration. Make sure that the user you set up for GitLab to communicate with JIRA has the correct access permission to post comments on a ticket and to also transition the ticket, if you'd like GitLab to also take care of closing them. +JIRA issue references and update comments will not work if the GitLab issue tracker is disabled. ### GitLab is unable to close a ticket From 4a0ca85834b6cf7decb58857986e535ba4e37c53 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Tue, 6 Dec 2016 18:56:59 -0200 Subject: [PATCH 106/140] Allow branch names with dots on API endpoint --- changelogs/unreleased/issue_25030.yml | 4 ++++ lib/api/branches.rb | 16 +++++++------- spec/requests/api/branches_spec.rb | 31 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/issue_25030.yml diff --git a/changelogs/unreleased/issue_25030.yml b/changelogs/unreleased/issue_25030.yml new file mode 100644 index 00000000000..e18b8d6a79b --- /dev/null +++ b/changelogs/unreleased/issue_25030.yml @@ -0,0 +1,4 @@ +--- +title: Allow branch names with dots on API endpoint +merge_request: +author: diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 73aed624ea7..0950c3d2e88 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -23,9 +23,9 @@ module API success Entities::RepoBranch end params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' end - get ':id/repository/branches/:branch' do + get ':id/repository/branches/:branch', requirements: { branch: /.+/ } do branch = user_project.repository.find_branch(params[:branch]) not_found!("Branch") unless branch @@ -39,11 +39,11 @@ module API success Entities::RepoBranch end params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' optional :developers_can_push, type: Boolean, desc: 'Flag if developers can push to that branch' optional :developers_can_merge, type: Boolean, desc: 'Flag if developers can merge to that branch' end - put ':id/repository/branches/:branch/protect' do + put ':id/repository/branches/:branch/protect', requirements: { branch: /.+/ } do authorize_admin_project branch = user_project.repository.find_branch(params[:branch]) @@ -76,9 +76,9 @@ module API success Entities::RepoBranch end params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' end - put ':id/repository/branches/:branch/unprotect' do + put ':id/repository/branches/:branch/unprotect', requirements: { branch: /.+/ } do authorize_admin_project branch = user_project.repository.find_branch(params[:branch]) @@ -112,9 +112,9 @@ module API desc 'Delete a branch' params do - requires :branch, type: String, regexp: /.+/, desc: 'The name of the branch' + requires :branch, type: String, desc: 'The name of the branch' end - delete ":id/repository/branches/:branch" do + delete ":id/repository/branches/:branch", requirements: { branch: /.+/ } do authorize_push_project result = DeleteBranchService.new(user_project, current_user). diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 55b8c8c0c69..2878e0cb59b 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -11,6 +11,7 @@ describe API::Branches, api: true do let!(:guest) { create(:project_member, :guest, user: user2, project: project) } let!(:branch_name) { 'feature' } let!(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } + let!(:branch_with_dot) { CreateBranchService.new(project, user).execute("with.1.2.3", "master") } describe "GET /projects/:id/repository/branches" do it "returns an array of project branches" do @@ -37,6 +38,13 @@ describe API::Branches, api: true do expect(json_response['developers_can_merge']).to eq(false) end + it "returns the branch information for a single branch with dots in the name" do + get api("/projects/#{project.id}/repository/branches/with.1.2.3", user) + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq("with.1.2.3") + end + context 'on a merged branch' do it "returns the branch information for a single branch" do get api("/projects/#{project.id}/repository/branches/merge-test", user) @@ -71,6 +79,14 @@ describe API::Branches, api: true do expect(json_response['developers_can_merge']).to eq(false) end + it "protects a single branch with dots in the name" do + put api("/projects/#{project.id}/repository/branches/with.1.2.3/protect", user) + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq("with.1.2.3") + expect(json_response['protected']).to eq(true) + end + it 'protects a single branch and developers can push' do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true @@ -220,6 +236,14 @@ describe API::Branches, api: true do expect(json_response['protected']).to eq(false) end + it "update branches with dots in branch name" do + put api("/projects/#{project.id}/repository/branches/with.1.2.3/unprotect", user) + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq("with.1.2.3") + expect(json_response['protected']).to eq(false) + end + it "returns success when unprotect branch" do put api("/projects/#{project.id}/repository/branches/unknown/unprotect", user) expect(response).to have_http_status(404) @@ -292,6 +316,13 @@ describe API::Branches, api: true do expect(json_response['branch_name']).to eq(branch_name) end + it "removes a branch with dots in the branch name" do + delete api("/projects/#{project.id}/repository/branches/with.1.2.3", user) + + expect(response).to have_http_status(200) + expect(json_response['branch_name']).to eq("with.1.2.3") + end + it 'returns 404 if branch not exists' do delete api("/projects/#{project.id}/repository/branches/foobar", user) expect(response).to have_http_status(404) From e948a466249be28655e794d63724e1c003f8621d Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 8 Dec 2016 17:09:06 -0200 Subject: [PATCH 107/140] Displays milestone remaining days only when it's present --- app/views/shared/milestones/_summary.html.haml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/shared/milestones/_summary.html.haml b/app/views/shared/milestones/_summary.html.haml index 0a237136959..d27fba805a3 100644 --- a/app/views/shared/milestones/_summary.html.haml +++ b/app/views/shared/milestones/_summary.html.haml @@ -25,8 +25,10 @@ %span.milestone-stat %strong== #{milestone.percent_complete(current_user)}% complete - %span.milestone-stat - %span.remaining-days= milestone_remaining_days(milestone) + - remaining_days = milestone_remaining_days(milestone) + - if remaining_days.present? + %span.milestone-stat + %span.remaining-days= remaining_days .milestone-progress-buttons %span.tab-issues-buttons From ef848c5ce2beaab7a102a3e8c9484f6aa3461a42 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 8 Dec 2016 17:09:26 -0200 Subject: [PATCH 108/140] Add CHANGELOG entry --- changelogs/unreleased/fix-milestone-summary.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix-milestone-summary.yml diff --git a/changelogs/unreleased/fix-milestone-summary.yml b/changelogs/unreleased/fix-milestone-summary.yml new file mode 100644 index 00000000000..3045a15054c --- /dev/null +++ b/changelogs/unreleased/fix-milestone-summary.yml @@ -0,0 +1,4 @@ +--- +title: Displays milestone remaining days only when it's present +merge_request: +author: From 44b082c7cce6d2a48c1511b5ffacd2d41671bad1 Mon Sep 17 00:00:00 2001 From: Ian Baum Date: Thu, 8 Dec 2016 13:47:16 -0600 Subject: [PATCH 109/140] Updating reference to database password postgresql['sql_password'] is not used, should be gitlab_rails['db_password'] --- doc/administration/high_availability/database.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md index 76f3a0fb387..b36cf18d459 100644 --- a/doc/administration/high_availability/database.md +++ b/doc/administration/high_availability/database.md @@ -41,7 +41,7 @@ If you use a cloud-managed service, or provide your own PostgreSQL: mailroom['enable'] = false # PostgreSQL configuration - postgresql['sql_password'] = 'DB password' + gitlab_rails['db_password'] = 'DB password' postgresql['md5_auth_cidr_addresses'] = ['0.0.0.0/0'] postgresql['listen_address'] = '0.0.0.0' ``` @@ -80,7 +80,7 @@ If you use a cloud-managed service, or provide your own PostgreSQL: 1. Similarly, set the password for the `gitlab` database user. Use the same password that you specified in the `/etc/gitlab/gitlab.rb` file for - `postgresql['sql_password']`. + `gitlab_rails['db_password']`. ``` \password gitlab From 4af62042bb7ddeff742a62048438673811e5344a Mon Sep 17 00:00:00 2001 From: Rydkin Maxim Date: Sat, 3 Dec 2016 23:26:02 +0300 Subject: [PATCH 110/140] remove unnecessary issues event filter on comments tab fix features_visibility_spec.rb remove strange part of spec fix conditions of comments tab disappearing and fix spec generate changelog rewrite spec for more coplex check step-by-step move conditional logic into helper and fix changelog fix indentation in helper --- app/helpers/events_helper.rb | 6 +++ app/views/shared/_event_filter.html.haml | 2 +- .../25272_fix_comments_tab_disappearing.yml | 4 ++ .../projects/features_visibility_spec.rb | 38 +++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/25272_fix_comments_tab_disappearing.yml diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index f1a0b929d82..362046c0270 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -45,6 +45,12 @@ module EventsHelper @project.feature_available?(feature_key, current_user) end + def comments_visible? + event_filter_visible(:repository) || + event_filter_visible(:merge_requests) || + event_filter_visible(:issues) + end + def event_preposition(event) if event.push? || event.commented? || event.target "at" diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml index 67c145cef17..e50ab5fea09 100644 --- a/app/views/shared/_event_filter.html.haml +++ b/app/views/shared/_event_filter.html.haml @@ -6,6 +6,6 @@ = event_filter_link EventFilter.merged, 'Merge events' - if event_filter_visible(:issues) = event_filter_link EventFilter.issue, 'Issue events' - - if event_filter_visible(:issues) + - if comments_visible? = event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.team, 'Team' diff --git a/changelogs/unreleased/25272_fix_comments_tab_disappearing.yml b/changelogs/unreleased/25272_fix_comments_tab_disappearing.yml new file mode 100644 index 00000000000..79cb2c6d843 --- /dev/null +++ b/changelogs/unreleased/25272_fix_comments_tab_disappearing.yml @@ -0,0 +1,4 @@ +--- +title: 'Fix comments activity tab visibility condition' +merge_request: 7913 +author: Rydkin Maxim diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb index 09aa6758b5c..3bb33394be7 100644 --- a/spec/features/projects/features_visibility_spec.rb +++ b/spec/features/projects/features_visibility_spec.rb @@ -182,6 +182,44 @@ describe 'Edit Project Settings', feature: true do expect(page).not_to have_content("Comments") end end + + # Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/25272 + it "hides comments activity tab only on disabled issues, merge requests and repository" do + select "Disabled", from: "project_project_feature_attributes_issues_access_level" + + save_changes_and_check_activity_tab do + expect(page).to have_content("Comments") + end + + visit edit_namespace_project_path(project.namespace, project) + + select "Disabled", from: "project_project_feature_attributes_merge_requests_access_level" + + save_changes_and_check_activity_tab do + expect(page).to have_content("Comments") + end + + visit edit_namespace_project_path(project.namespace, project) + + select "Disabled", from: "project_project_feature_attributes_repository_access_level" + + save_changes_and_check_activity_tab do + expect(page).not_to have_content("Comments") + end + + visit edit_namespace_project_path(project.namespace, project) + end + + def save_changes_and_check_activity_tab + click_button "Save changes" + wait_for_ajax + + visit activity_namespace_project_path(project.namespace, project) + + page.within(".event-filter") do + yield + end + end end # Regression spec for https://gitlab.com/gitlab-org/gitlab-ce/issues/24056 From 47bc0125beba2351c5d78323677a34d19b102047 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Dec 2016 00:51:33 +0000 Subject: [PATCH 111/140] Merge branch 'destroy-session' into 'security' Destroy a user session when they delete their own account via browser This patch destroys a user's session when they delete their own account using a browser. A new session is created as they are redirected to the sign_in page. Issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/25015 See merge request !2042 --- app/controllers/registrations_controller.rb | 5 ++++- changelogs/unreleased/destroy-session.yml | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/destroy-session.yml diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 3327f4f2b87..c45196cc3e9 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -27,7 +27,10 @@ class RegistrationsController < Devise::RegistrationsController DeleteUserService.new(current_user).execute(current_user) respond_to do |format| - format.html { redirect_to new_user_session_path, notice: "Account successfully removed." } + format.html do + session.try(:destroy) + redirect_to new_user_session_path, notice: "Account successfully removed." + end end end diff --git a/changelogs/unreleased/destroy-session.yml b/changelogs/unreleased/destroy-session.yml new file mode 100644 index 00000000000..e713e2dc424 --- /dev/null +++ b/changelogs/unreleased/destroy-session.yml @@ -0,0 +1,4 @@ +--- +title: Destroy a user's session when they delete their own account +merge_request: +author: From 6e1b52b8b9b83cb774a5f2f52d4b4355590f14f7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 6 Dec 2016 01:05:06 +0000 Subject: [PATCH 112/140] Merge branch 'rs-filter-authentication_token' into 'security' Add authentication_token to filter_parameters list See merge request !2041 --- config/application.rb | 3 ++- config/initializers/sentry.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config/application.rb b/config/application.rb index fb84870dfbd..0aa2873f94a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -45,7 +45,7 @@ module Gitlab # # Parameters filtered: # - Password (:password, :password_confirmation) - # - Private tokens (:private_token) + # - Private tokens (:private_token, :authentication_token) # - Two-factor tokens (:otp_attempt) # - Repo/Project Import URLs (:import_url) # - Build variables (:variables) @@ -55,6 +55,7 @@ module Gitlab # - Sentry DSN (:sentry_dsn) # - Deploy keys (:key) config.filter_parameters += %i( + authentication_token certificate encrypted_key hook diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index 4f30d1265c8..6b0cff75653 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -15,7 +15,7 @@ if Rails.env.production? Raven.configure do |config| config.dsn = current_application_settings.sentry_dsn config.release = Gitlab::REVISION - + # Sanitize fields based on those sanitized from Rails. config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s) # Sanitize authentication headers From edf7dbfacd5a6b884ae1af72204e3718e89f3c35 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 2 Dec 2016 08:48:32 +0000 Subject: [PATCH 113/140] Merge branch 'html-safe-diff-line-content' into 'security' Don't accidentally mark unsafe diff lines as HTML safe Fixes potential XSS issue when a legacy diff note is created on a merge request whose diff contained HTML See https://gitlab.com/gitlab-org/gitlab-ce/issues/25249 See merge request !2040 --- app/helpers/diff_helper.rb | 4 +- .../html-safe-diff-line-content.yml | 4 ++ spec/helpers/diff_helper_spec.rb | 57 ++++++++++++++++--- 3 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/html-safe-diff-line-content.yml diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index f489f9aa0d6..c35d6611ab0 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -55,7 +55,9 @@ module DiffHelper if line.blank? " ".html_safe else - line.sub(/^[\-+ ]/, '').html_safe + # We can't use `sub` because the HTML-safeness of `line` will not survive. + line[0] = '' if line.start_with?('+', '-', ' ') + line end end diff --git a/changelogs/unreleased/html-safe-diff-line-content.yml b/changelogs/unreleased/html-safe-diff-line-content.yml new file mode 100644 index 00000000000..8f8bbc51963 --- /dev/null +++ b/changelogs/unreleased/html-safe-diff-line-content.yml @@ -0,0 +1,4 @@ +--- +title: Don't accidentally mark unsafe diff lines as HTML safe +merge_request: +author: diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index 837e7afa7e8..468bcc7badc 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -60,15 +60,58 @@ describe DiffHelper do end describe '#diff_line_content' do - it 'returns non breaking space when line is empty' do - expect(diff_line_content(nil)).to eq(' ') + context 'when the line is empty' do + it 'returns a non breaking space' do + expect(diff_line_content(nil)).to eq(' ') + end + + it 'returns an HTML-safe string' do + expect(diff_line_content(nil)).to be_html_safe + end end - it 'returns the line itself' do - expect(diff_line_content(diff_file.diff_lines.first.text)). - to eq('@@ -6,12 +6,18 @@ module Popen') - expect(diff_line_content(diff_file.diff_lines.first.type)).to eq('match') - expect(diff_file.diff_lines.first.new_pos).to eq(6) + context 'when the line is not empty' do + context 'when the line starts with +, -, or a space' do + it 'strips the first character' do + expect(diff_line_content('+new line')).to eq('new line') + expect(diff_line_content('-new line')).to eq('new line') + expect(diff_line_content(' new line')).to eq('new line') + end + + context 'when the line is HTML-safe' do + it 'returns an HTML-safe string' do + expect(diff_line_content('+new line'.html_safe)).to be_html_safe + expect(diff_line_content('-new line'.html_safe)).to be_html_safe + expect(diff_line_content(' new line'.html_safe)).to be_html_safe + end + end + + context 'when the line is not HTML-safe' do + it 'returns a non-HTML-safe string' do + expect(diff_line_content('+new line')).not_to be_html_safe + expect(diff_line_content('-new line')).not_to be_html_safe + expect(diff_line_content(' new line')).not_to be_html_safe + end + end + end + + context 'when the line does not start with a +, -, or a space' do + it 'returns the string' do + expect(diff_line_content('@@ -6,12 +6,18 @@ module Popen')).to eq('@@ -6,12 +6,18 @@ module Popen') + end + + context 'when the line is HTML-safe' do + it 'returns an HTML-safe string' do + expect(diff_line_content('@@ -6,12 +6,18 @@ module Popen'.html_safe)).to be_html_safe + end + end + + context 'when the line is not HTML-safe' do + it 'returns a non-HTML-safe string' do + expect(diff_line_content('@@ -6,12 +6,18 @@ module Popen')).not_to be_html_safe + end + end + end end end From f23b1cb453deea2659c0cb9e9047c72d859bbf9d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 29 Nov 2016 13:47:43 +0000 Subject: [PATCH 114/140] Merge branch 'jej-23867-use-mr-finder-instead-of-access-check' into 'security' Replace MR access checks with use of MergeRequestsFinder Split from !2024 to partially solve https://gitlab.com/gitlab-org/gitlab-ce/issues/23867 :warning: - Potentially untested :bomb: - No test coverage :traffic_light: - Test coverage of some sort exists (a test failed when error raised) :vertical_traffic_light: - Test coverage of return value (a test failed when nil used) :white_check_mark: - Permissions check tested - [x] :bomb: app/finders/notes_finder.rb:17 - [x] :warning: app/views/layouts/nav/_project.html.haml:80 [`.count`] - [x] :bomb: app/controllers/concerns/creates_commit.rb:84 - [x] :traffic_light: app/controllers/projects/commits_controller.rb:24 - [x] :traffic_light: app/controllers/projects/compare_controller.rb:56 - [x] :vertical_traffic_light: app/controllers/projects/discussions_controller.rb:29 - [x] :white_check_mark: app/controllers/projects/todos_controller.rb:27 - [x] :vertical_traffic_light: app/models/commit.rb:268 - [x] :white_check_mark: lib/gitlab/search_results.rb:71 - [x] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#d1c10892daedb4d4dd3d4b12b6d071091eea83df_267_266 Memoize ` merged_merge_request(current_user)` - [x] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#d1c10892daedb4d4dd3d4b12b6d071091eea83df_248_247 Expected side effect for `merged_merge_request!`, consider `skip_authorization: true`. - [x] https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2024/diffs#d1c10892daedb4d4dd3d4b12b6d071091eea83df_269_269 Scary use of unchecked `merged_merge_request?` See merge request !2033 --- app/controllers/concerns/creates_commit.rb | 6 +-- app/controllers/projects/commit_controller.rb | 16 ++++---- .../projects/commits_controller.rb | 2 +- .../projects/compare_controller.rb | 2 +- .../projects/discussions_controller.rb | 2 +- app/controllers/projects/todos_controller.rb | 2 +- app/finders/issuable_finder.rb | 4 ++ app/finders/notes_finder.rb | 2 +- app/helpers/commits_helper.rb | 4 +- app/models/commit.rb | 41 ++++++++++++------- app/models/concerns/milestoneish.rb | 10 ++--- app/models/merge_request.rb | 2 +- app/models/repository.rb | 2 +- app/services/commits/change_service.rb | 2 +- app/views/layouts/nav/_project.html.haml | 2 +- app/views/projects/commit/_change.html.haml | 2 +- ...-use-mr-finder-instead-of-access-check.yml | 4 ++ lib/gitlab/search_results.rb | 2 +- .../projects/todo_controller_spec.rb | 15 ++++++- spec/lib/gitlab/search_results_spec.rb | 16 ++++++++ spec/models/commit_range_spec.rb | 15 ++++--- spec/models/commit_spec.rb | 11 ++--- 22 files changed, 104 insertions(+), 60 deletions(-) create mode 100644 changelogs/unreleased/jej-23867-use-mr-finder-instead-of-access-check.yml diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb index dacb5679dd3..936d9bab57e 100644 --- a/app/controllers/concerns/creates_commit.rb +++ b/app/controllers/concerns/creates_commit.rb @@ -81,10 +81,8 @@ module CreatesCommit def merge_request_exists? return @merge_request if defined?(@merge_request) - @merge_request = @mr_target_project.merge_requests.opened.find_by( - source_branch: @mr_source_branch, - target_branch: @mr_target_branch - ) + @merge_request = MergeRequestsFinder.new(current_user, project_id: @mr_target_project.id).execute.opened. + find_by(source_branch: @mr_source_branch, target_branch: @mr_target_branch) end def different_project? diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index cdfc1ba7b92..8197d9e4c99 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -65,7 +65,7 @@ class Projects::CommitController < Projects::ApplicationController return render_404 if @target_branch.blank? - create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title} has been successfully reverted.", + create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.", success_path: successful_change_path, failure_path: failed_change_path) end @@ -74,26 +74,24 @@ class Projects::CommitController < Projects::ApplicationController return render_404 if @target_branch.blank? - create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title} has been successfully cherry-picked.", + create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked.", success_path: successful_change_path, failure_path: failed_change_path) end private def successful_change_path - return referenced_merge_request_url if @commit.merged_merge_request - - namespace_project_commits_url(@project.namespace, @project, @target_branch) + referenced_merge_request_url || namespace_project_commits_url(@project.namespace, @project, @target_branch) end def failed_change_path - return referenced_merge_request_url if @commit.merged_merge_request - - namespace_project_commit_url(@project.namespace, @project, params[:id]) + referenced_merge_request_url || namespace_project_commit_url(@project.namespace, @project, params[:id]) end def referenced_merge_request_url - namespace_project_merge_request_url(@project.namespace, @project, @commit.merged_merge_request) + if merge_request = @commit.merged_merge_request(current_user) + namespace_project_merge_request_url(@project.namespace, @project, merge_request) + end end def commit diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index aba87b6144b..ad92f05a42d 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -21,7 +21,7 @@ class Projects::CommitsController < Projects::ApplicationController @note_counts = project.notes.where(commit_id: @commits.map(&:id)). group(:commit_id).count - @merge_request = @project.merge_requests.opened. + @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened. find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref) respond_to do |format| diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index bee3d56076c..ec02fc15d35 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -53,7 +53,7 @@ class Projects::CompareController < Projects::ApplicationController end def merge_request - @merge_request ||= @project.merge_requests.opened. + @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened. find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref) end end diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb index 148e39630e3..1349b015a63 100644 --- a/app/controllers/projects/discussions_controller.rb +++ b/app/controllers/projects/discussions_controller.rb @@ -24,7 +24,7 @@ class Projects::DiscussionsController < Projects::ApplicationController private def merge_request - @merge_request ||= @project.merge_requests.find_by!(iid: params[:merge_request_id]) + @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).find_by!(iid: params[:merge_request_id]) end def discussion diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb index 52517381c65..a41fcb85c40 100644 --- a/app/controllers/projects/todos_controller.rb +++ b/app/controllers/projects/todos_controller.rb @@ -18,7 +18,7 @@ class Projects::TodosController < Projects::ApplicationController when "issue" IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id]) when "merge_request" - @project.merge_requests.find(params[:issuable_id]) + MergeRequestsFinder.new(current_user, project_id: @project.id).find(params[:issuable_id]) end end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index c9bee01b9ad..b4c14d05eaf 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -77,6 +77,10 @@ class IssuableFinder counts end + def find_by!(*params) + execute.find_by!(*params) + end + def group return @group if defined?(@group) diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index a653a6d59c6..2484339e3a4 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -14,7 +14,7 @@ class NotesFinder when "issue" IssuesFinder.new(current_user, project_id: project.id).find(target_id).notes.inc_author when "merge_request" - project.merge_requests.find(target_id).mr_and_commit_notes.inc_author + MergeRequestsFinder.new(current_user, project_id: project.id).find(target_id).mr_and_commit_notes.inc_author when "snippet", "project_snippet" project.snippets.find(target_id).notes else diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index ed402b698fb..66a720a9426 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -130,7 +130,7 @@ module CommitsHelper def revert_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true) return unless current_user - tooltip = "Revert this #{commit.change_type_title} in a new merge request" if has_tooltip + tooltip = "Revert this #{commit.change_type_title(current_user)} in a new merge request" if has_tooltip if can_collaborate_with_project? btn_class = "btn btn-warning btn-#{btn_class}" unless btn_class.nil? @@ -154,7 +154,7 @@ module CommitsHelper def cherry_pick_commit_link(commit, continue_to_path, btn_class: nil, has_tooltip: true) return unless current_user - tooltip = "Cherry-pick this #{commit.change_type_title} in a new merge request" + tooltip = "Cherry-pick this #{commit.change_type_title(current_user)} in a new merge request" if can_collaborate_with_project? btn_class = "btn btn-default btn-#{btn_class}" unless btn_class.nil? diff --git a/app/models/commit.rb b/app/models/commit.rb index 248140f421b..1831cc7e175 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -245,44 +245,47 @@ class Commit project.repository.next_branch("cherry-pick-#{short_id}", mild: true) end - def revert_description - if merged_merge_request - "This reverts merge request #{merged_merge_request.to_reference}" + def revert_description(user) + if merged_merge_request?(user) + "This reverts merge request #{merged_merge_request(user).to_reference}" else "This reverts commit #{sha}" end end - def revert_message - %Q{Revert "#{title.strip}"\n\n#{revert_description}} + def revert_message(user) + %Q{Revert "#{title.strip}"\n\n#{revert_description(user)}} end - def reverts_commit?(commit) - description? && description.include?(commit.revert_description) + def reverts_commit?(commit, user) + description? && description.include?(commit.revert_description(user)) end def merge_commit? parents.size > 1 end - def merged_merge_request - return @merged_merge_request if defined?(@merged_merge_request) - - @merged_merge_request = project.merge_requests.find_by(merge_commit_sha: id) if merge_commit? + def merged_merge_request(current_user) + # Memoize with per-user access check + @merged_merge_request_hash ||= Hash.new do |hash, user| + hash[user] = merged_merge_request_no_cache(user) + end + + @merged_merge_request_hash[current_user] end - def has_been_reverted?(current_user = nil, noteable = self) + def has_been_reverted?(current_user, noteable = self) ext = all_references(current_user) noteable.notes_with_associations.system.each do |note| note.all_references(current_user, extractor: ext) end - ext.commits.any? { |commit_ref| commit_ref.reverts_commit?(self) } + ext.commits.any? { |commit_ref| commit_ref.reverts_commit?(self, current_user) } end - def change_type_title - merged_merge_request ? 'merge request' : 'commit' + def change_type_title(user) + merged_merge_request?(user) ? 'merge request' : 'commit' end # Get the URI type of the given path @@ -350,4 +353,12 @@ class Commit changes end + + def merged_merge_request?(user) + !!merged_merge_request(user) + end + + def merged_merge_request_no_cache(user) + MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit? + end end diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index e65fc9eaa09..875e9834487 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -1,17 +1,17 @@ module Milestoneish - def closed_items_count(user = nil) + def closed_items_count(user) issues_visible_to_user(user).closed.size + merge_requests.closed_and_merged.size end - def total_items_count(user = nil) + def total_items_count(user) issues_visible_to_user(user).size + merge_requests.size end - def complete?(user = nil) + def complete?(user) total_items_count(user) > 0 && total_items_count(user) == closed_items_count(user) end - def percent_complete(user = nil) + def percent_complete(user) ((closed_items_count(user) * 100) / total_items_count(user)).abs rescue ZeroDivisionError 0 @@ -29,7 +29,7 @@ module Milestoneish (Date.today - start_date).to_i end - def issues_visible_to_user(user = nil) + def issues_visible_to_user(user) issues.visible_to_user(user) end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 33b578e12c1..dd9f1a7c85b 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -805,7 +805,7 @@ class MergeRequest < ActiveRecord::Base @merge_commit ||= project.commit(merge_commit_sha) if merge_commit_sha end - def can_be_reverted?(current_user = nil) + def can_be_reverted?(current_user) merge_commit && !merge_commit.has_been_reverted?(current_user, self) end diff --git a/app/models/repository.rb b/app/models/repository.rb index 3c4b0212af7..1ccabdb7c1f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -950,7 +950,7 @@ class Repository update_branch_with_hooks(user, base_branch) do committer = user_to_committer(user) source_sha = Rugged::Commit.create(rugged, - message: commit.revert_message, + message: commit.revert_message(user), author: committer, committer: committer, tree: revert_tree_id, diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb index 1c82599c579..db5f2bf9b2e 100644 --- a/app/services/commits/change_service.rb +++ b/app/services/commits/change_service.rb @@ -34,7 +34,7 @@ module Commits repository.public_send(action, current_user, @commit, into, tree_id) success else - error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title} automatically. + error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically. It may have already been #{action.to_s.dasherize}, or a more recent commit may have updated some of its content." raise ChangeError, error_msg end diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 701bcd3ab71..7bd11f5727a 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -77,7 +77,7 @@ = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %span Merge Requests - %span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) + %span.badge.count.merge_counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) - if project_nav_tab? :wiki = nav_link(controller: :wikis) do diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml index e4cd55b9f7a..f6e3d5e76f5 100644 --- a/app/views/projects/commit/_change.html.haml +++ b/app/views/projects/commit/_change.html.haml @@ -11,7 +11,7 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3.page-title== #{label} this #{commit.change_type_title} + %h3.page-title== #{label} this #{commit.change_type_title(current_user)} .modal-body = form_tag send("#{type.underscore}_namespace_project_commit_path", @project.namespace, @project, commit.id), method: :post, remote: false, class: 'form-horizontal js-#{type}-form js-requires-input' do .form-group.branch diff --git a/changelogs/unreleased/jej-23867-use-mr-finder-instead-of-access-check.yml b/changelogs/unreleased/jej-23867-use-mr-finder-instead-of-access-check.yml new file mode 100644 index 00000000000..5a4a44b9562 --- /dev/null +++ b/changelogs/unreleased/jej-23867-use-mr-finder-instead-of-access-check.yml @@ -0,0 +1,4 @@ +--- +title: Replace MR access checks with use of MergeRequestsFinder +merge_request: +author: diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 47d8599e298..35212992698 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -68,7 +68,7 @@ module Gitlab end def merge_requests - merge_requests = MergeRequest.in_projects(project_ids_relation) + merge_requests = MergeRequestsFinder.new(current_user).execute.in_projects(project_ids_relation) if query =~ /[#!](\d+)\z/ merge_requests = merge_requests.where(iid: $1) else diff --git a/spec/controllers/projects/todo_controller_spec.rb b/spec/controllers/projects/todo_controller_spec.rb index 193a3f6b5a3..415c264e0dd 100644 --- a/spec/controllers/projects/todo_controller_spec.rb +++ b/spec/controllers/projects/todo_controller_spec.rb @@ -110,7 +110,7 @@ describe Projects::TodosController do end end - context 'when not authorized' do + context 'when not authorized for project' do it 'does not create todo for merge request user has no access to' do sign_in(user) expect do @@ -128,6 +128,19 @@ describe Projects::TodosController do expect(response).to have_http_status(302) end end + + context 'when not authorized for merge_request' do + before do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE) + sign_in(user) + end + + it "doesn't create todo" do + expect{ go }.not_to change { user.todos.count } + expect(response).to have_http_status(404) + end + end end end end diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index f23e3522625..9614aad3e73 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -40,6 +40,15 @@ describe Gitlab::SearchResults do expect(results.milestones_count).to eq(1) end end + + it 'includes merge requests from source and target projects' do + forked_project = create(:empty_project, forked_from_project: project) + merge_request_2 = create(:merge_request, target_project: project, source_project: forked_project, title: 'foo') + + results = described_class.new(user, Project.where(id: forked_project.id), 'foo') + + expect(results.objects('merge_requests')).to include merge_request_2 + end end it 'does not list issues on private projects' do @@ -152,4 +161,11 @@ describe Gitlab::SearchResults do expect(results.issues_count).to eq 5 end end + + it 'does not list merge requests on projects with limited access' do + project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) + project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE) + + expect(results.objects('merge_requests')).not_to include merge_request + end end diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index d89d4342dea..30782ca75a0 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -137,26 +137,25 @@ describe CommitRange, models: true do end describe '#has_been_reverted?' do - it 'returns true if the commit has been reverted' do - issue = create(:issue) + let(:issue) { create(:issue) } + let(:user) { issue.author } + it 'returns true if the commit has been reverted' do create(:note_on_issue, noteable: issue, system: true, - note: commit1.revert_description, + note: commit1.revert_description(user), project: issue.project) expect_any_instance_of(Commit).to receive(:reverts_commit?). - with(commit1). + with(commit1, user). and_return(true) - expect(commit1.has_been_reverted?(nil, issue)).to eq(true) + expect(commit1.has_been_reverted?(user, issue)).to eq(true) end it 'returns false a commit has not been reverted' do - issue = create(:issue) - - expect(commit1.has_been_reverted?(nil, issue)).to eq(false) + expect(commit1.has_been_reverted?(user, issue)).to eq(false) end end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index eb482c7f913..0935fc0561c 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -179,25 +179,26 @@ eos describe '#reverts_commit?' do let(:another_commit) { double(:commit, revert_description: "This reverts commit #{commit.sha}") } + let(:user) { commit.author } - it { expect(commit.reverts_commit?(another_commit)).to be_falsy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } context 'commit has no description' do before { allow(commit).to receive(:description?).and_return(false) } - it { expect(commit.reverts_commit?(another_commit)).to be_falsy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } end context "another_commit's description does not revert commit" do before { allow(commit).to receive(:description).and_return("Foo Bar") } - it { expect(commit.reverts_commit?(another_commit)).to be_falsy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_falsy } end context "another_commit's description reverts commit" do before { allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") } - it { expect(commit.reverts_commit?(another_commit)).to be_truthy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy } end context "another_commit's description reverts merged merge request" do @@ -207,7 +208,7 @@ eos allow(commit).to receive(:description).and_return("Foo #{another_commit.revert_description} Bar") end - it { expect(commit.reverts_commit?(another_commit)).to be_truthy } + it { expect(commit.reverts_commit?(another_commit, user)).to be_truthy } end end From b9b2ca47b3bcc7dfc56a3199994063988ff8218e Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Fri, 9 Dec 2016 09:49:38 +0100 Subject: [PATCH 115/140] Update docs to reflect new defaults on omnibus For mattermost chat commands, new defaults are set in the next release making configuring easier. This commit reflects that in the doc. [ci skip] --- .../img/mattermost_console_integrations.png | Bin 41186 -> 314642 bytes .../mattermost_slash_commands.md | 8 ++++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/project_services/img/mattermost_console_integrations.png b/doc/project_services/img/mattermost_console_integrations.png index b3b8c20d7bf14dee21cbb5bc335c9f83fe1e050c..92a30da5be0cb8e18d40dd39e2d5db1b9ba204e9 100644 GIT binary patch literal 314642 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 z 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 literal 41186 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%0Df^1S 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 n_@zjPMK}Tux!#t_ng$Rb diff --git a/doc/project_services/mattermost_slash_commands.md b/doc/project_services/mattermost_slash_commands.md index 1507dfa3abd..6fcbf6f1642 100644 --- a/doc/project_services/mattermost_slash_commands.md +++ b/doc/project_services/mattermost_slash_commands.md @@ -22,6 +22,9 @@ commands in Mattermost and then enable the service in GitLab. ### Step 1. Enable custom slash commands in Mattermost +This step is only required when using a source install, omnibus installs will be +preconfigured with the right settings. + The first thing to do in Mattermost is to enable custom slash commands from the administrator console. @@ -32,8 +35,9 @@ the administrator console. --- -1. Click **Custom integrations** and set **Enable Custom Slash Commands** to - true. +1. Click **Custom integrations** and set **Enable Custom Slash Commands**, + **Enable custom integrations to override usernames**, and **Override + custom integrations to override profile picture icons** to true ![Mattermost console](img/mattermost_console_integrations.png) From 1637ce4e6a64c154eca93444f6a62664f4e78e7a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 9 Dec 2016 10:06:02 +0000 Subject: [PATCH 116/140] Updated JS based on review Fixed group links dropdown to match --- app/assets/javascripts/members.js.es6 | 22 +++++++------------ app/views/projects/group_links/update.js.haml | 1 + app/views/shared/members/_group.html.haml | 21 ++++++++++++++++-- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index 64826894965..e3f367a11eb 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -31,14 +31,8 @@ toggleLabel(selected, $el) { return $el.text(); }, - clicked: (selected, $el) => { - const $link = $($el); - const { $toggle, $dateInput } = this.getMemberListItems($link); - - $toggle.attr('disabled', true); - $dateInput.attr('disabled', true); - - $btn.closest('form').trigger('submit.rails'); + clicked: (selected, $link) => { + this.formSubmit(null, $link); }, }); }); @@ -55,21 +49,21 @@ } } - formSubmit(e) { - const $this = $(e.currentTarget); + formSubmit(e, $el = null) { + const $this = e ? $(e.currentTarget) : $el; const { $toggle, $dateInput } = this.getMemberListItems($this); $this.closest('form').trigger('submit.rails'); - $toggle.attr('disabled', true); - $dateInput.attr('disabled', true); + $toggle.disable(); + $dateInput.disable(); } formSuccess(e) { const { $toggle, $dateInput } = this.getMemberListItems($(e.currentTarget).closest('.member')); - $toggle.removeAttr('disabled'); - $dateInput.removeAttr('disabled'); + $toggle.enable(); + $dateInput.enable(); } getMemberListItems($el) { diff --git a/app/views/projects/group_links/update.js.haml b/app/views/projects/group_links/update.js.haml index af9a5b19060..55520fda494 100644 --- a/app/views/projects/group_links/update.js.haml +++ b/app/views/projects/group_links/update.js.haml @@ -1,3 +1,4 @@ :plain var $listItem = $('#{escape_javascript(render('shared/members/group', group_link: @group_link))}'); $("#group_member_#{@group_link.id} .list-item-name").replaceWith($listItem.find('.list-item-name')); + gl.utils.localTimeAgo($('.js-timeago'), $("#group_member_#{@group_link.id}")); diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml index 1c0346bbc78..8928de9097b 100644 --- a/app/views/shared/members/_group.html.haml +++ b/app/views/shared/members/_group.html.haml @@ -1,7 +1,8 @@ - group_link = local_assigns[:group_link] - group = group_link.group - can_admin_member = can?(current_user, :admin_project_member, @project) -%li.member.group_member{ id: "group_member_#{group_link.id}" } +- dom_id = "group_member_#{group_link.id}" +%li.member.group_member{ id: dom_id } %span{ class: "list-item-name" } = image_tag group_icon(group), class: "avatar s40", alt: '' %strong @@ -14,7 +15,23 @@ Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)} .controls.member-controls = form_tag namespace_project_group_link_path(@project.namespace, @project, group_link), method: :put, remote: true, class: 'form-horizontal js-edit-member-form' do - = select_tag 'group_link[group_access]', options_for_select(ProjectGroupLink.access_options, group_link.group_access), class: 'form-control member-form-control append-right-5 js-member-update-control', id: "member_access_level_#{group.id}", disabled: !can_admin_member + = hidden_field_tag "group_link[group_access]", group_link.group_access + .member-form-control.dropdown.append-right-5 + %button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button", + disabled: !can_admin_member, + data: { toggle: "dropdown", field_name: "group_link[group_access]" } } + %span.dropdown-toggle-text + = group_link.human_access + = icon("chevron-down") + .dropdown-menu.dropdown-select.dropdown-menu-align-right.dropdown-menu-selectable + = dropdown_title("Change permissions") + .dropdown-content + %ul + - Gitlab::Access.options.each do |role, role_id| + %li + = link_to role, "javascript:void(0)", + class: ("is-active" if group_link.group_access == role_id), + data: { id: role_id, el_id: dom_id } .prepend-left-5.clearable-input.member-form-control = text_field_tag 'group_link[expires_at]', group_link.expires_at, class: 'form-control js-access-expiration-date js-member-update-control', placeholder: 'Expiration date', id: "member_expires_at_#{group.id}", disabled: !can_admin_member %i.clear-icon.js-clear-input From 70cb27926b225f71ccd4bba2342abbc3962da7e9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 8 Dec 2016 11:42:56 +0000 Subject: [PATCH 117/140] Replace play icon svg logic --- .../components/environment.js.es6 | 10 ++++-- .../components/environment_actions.js.es6 | 32 ++++--------------- .../components/environment_item.js.es6 | 11 +++++++ .../projects/environments/index.html.haml | 4 ++- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 84faabf938a..1db29dd47fb 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -74,6 +74,8 @@ projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath, newEnvironmentPath: environmentsData.newEnvironmentPath, helpPagePath: environmentsData.helpPagePath, + commitIconSvg: environmentsData.commitIconSvg, + playIconSvg: environmentsData.playIconSvg, }; }, @@ -227,7 +229,9 @@ :model="model" :toggleRow="toggleRow.bind(model)" :can-create-deployment="canCreateDeploymentParsed" - :can-read-environment="canReadEnvironmentParsed"> + :can-read-environment="canReadEnvironmentParsed" + :play-icon-svg="playIconSvg" + :commit-icon-svg="commitIconSvg"> + :can-read-environment="canReadEnvironmentParsed" + :play-icon-svg="playIconSvg" + :commit-icon-svg="commitIconSvg"> diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6 index d149a446e0b..02cdb305217 100644 --- a/app/assets/javascripts/environments/components/environment_actions.js.es6 +++ b/app/assets/javascripts/environments/components/environment_actions.js.es6 @@ -12,38 +12,18 @@ required: false, default: () => [], }, - }, - /** - * Appends the svg icon that were render in the index page. - * In order to reuse the svg instead of copy and paste in this template - * we need to render it outside this component using =custom_icon partial. - * - * TODO: Remove this when webpack is merged. - * - */ - mounted() { - const playIcon = document.querySelector('.play-icon-svg.hidden svg'); - - const dropdownContainer = this.$el.querySelector('.dropdown-play-icon-container'); - const actionContainers = this.$el.querySelectorAll('.action-play-icon-container'); - // Phantomjs does not have support to iterate a nodelist. - const actionsArray = [].slice.call(actionContainers); - - if (playIcon && actionsArray && dropdownContainer) { - dropdownContainer.appendChild(playIcon.cloneNode(true)); - - actionsArray.forEach((element) => { - element.appendChild(playIcon.cloneNode(true)); - }); - } + playIconSvg: { + type: String, + required: false, + }, }, template: `

diff --git a/app/assets/javascripts/vue_common_component/commit.js.es6 b/app/assets/javascripts/vue_common_component/commit.js.es6 index 2ef2959cbf4..2b67521d022 100644 --- a/app/assets/javascripts/vue_common_component/commit.js.es6 +++ b/app/assets/javascripts/vue_common_component/commit.js.es6 @@ -68,6 +68,11 @@ required: false, default: () => ({}), }, + + commitIconSvg: { + type: String, + required: false, + }, }, computed: { @@ -110,24 +115,6 @@ }, }, - /** - * In order to reuse the svg instead of copy and paste in this template - * we need to render it outside this component using =custom_icon partial. - * Make sure it has this structure: - * .commit-icon-svg.hidden - * svg - * - * TODO: Find a better way to include SVG - */ - mounted() { - const commitIconContainer = this.$el.querySelector('.commit-icon-container'); - const commitIcon = document.querySelector('.commit-icon-svg.hidden svg'); - - if (commitIconContainer && commitIcon) { - commitIconContainer.appendChild(commitIcon.cloneNode(true)); - } - }, - template: `

@@ -142,7 +129,7 @@ {{commit_ref.name}} -
+
From 3768de806566c10af980373a5f66a77306968ae9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 8 Dec 2016 11:54:08 +0000 Subject: [PATCH 119/140] Uniformize props name format --- .../components/environment_external_url.js.es6 | 4 ++-- .../components/environment_item.js.es6 | 16 ++++++++-------- .../components/environment_rollback.js.es6 | 9 +++++---- .../components/environment_stop.js.es6 | 4 ++-- .../vue_common_component/commit.js.es6 | 18 +++++++++--------- .../environment_external_url_spec.js.es6 | 2 +- .../environment_rollback_spec.js.es6 | 12 ++++++------ .../environments/environment_stop_spec.js.es6 | 2 +- 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_external_url.js.es6 b/app/assets/javascripts/environments/components/environment_external_url.js.es6 index 79cd5ded5bd..aed65b33c04 100644 --- a/app/assets/javascripts/environments/components/environment_external_url.js.es6 +++ b/app/assets/javascripts/environments/components/environment_external_url.js.es6 @@ -7,14 +7,14 @@ window.gl.environmentsList.ExternalUrlComponent = Vue.component('external-url-component', { props: { - external_url: { + externalUrl: { type: String, default: '', }, }, template: ` - + `, diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 177ffcb3785..2e046a60146 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -461,9 +461,9 @@
@@ -495,22 +495,22 @@
- + :external-url="model.external_url"> +
+ :stop-url="model.stop_path">
+ :is-last-deployment="isLastDeployment" + :retry-url="retryUrl">
diff --git a/app/assets/javascripts/environments/components/environment_rollback.js.es6 b/app/assets/javascripts/environments/components/environment_rollback.js.es6 index 55e5c826e07..6d4e8fad604 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.js.es6 +++ b/app/assets/javascripts/environments/components/environment_rollback.js.es6 @@ -7,19 +7,20 @@ window.gl.environmentsList.RollbackComponent = Vue.component('rollback-component', { props: { - retry_url: { + retryUrl: { type: String, default: '', }, - is_last_deployment: { + + isLastDeployment: { type: Boolean, default: true, }, }, template: ` - - + + Re-deploy diff --git a/app/assets/javascripts/environments/components/environment_stop.js.es6 b/app/assets/javascripts/environments/components/environment_stop.js.es6 index e6d66a0148c..7292f924e5c 100644 --- a/app/assets/javascripts/environments/components/environment_stop.js.es6 +++ b/app/assets/javascripts/environments/components/environment_stop.js.es6 @@ -7,7 +7,7 @@ window.gl.environmentsList.StopComponent = Vue.component('stop-component', { props: { - stop_url: { + stopUrl: { type: String, default: '', }, @@ -15,7 +15,7 @@ template: ` diff --git a/app/assets/javascripts/vue_common_component/commit.js.es6 b/app/assets/javascripts/vue_common_component/commit.js.es6 index 2b67521d022..1f6995a645a 100644 --- a/app/assets/javascripts/vue_common_component/commit.js.es6 +++ b/app/assets/javascripts/vue_common_component/commit.js.es6 @@ -23,7 +23,7 @@ * name * ref_url */ - commit_ref: { + commitRef: { type: Object, required: false, default: () => ({}), @@ -32,7 +32,7 @@ /** * Used to link to the commit sha. */ - commit_url: { + commitUrl: { type: String, required: false, default: '', @@ -41,7 +41,7 @@ /** * Used to show the commit short_sha that links to the commit url. */ - short_sha: { + shortSha: { type: String, required: false, default: '', @@ -85,7 +85,7 @@ * @returns {Boolean} */ hasCommitRef() { - return this.commit_ref && this.commit_ref.name && this.commit_ref.ref_url; + return this.commitRef && this.commitRef.name && this.commitRef.ref_url; }, /** @@ -125,15 +125,15 @@ - {{commit_ref.name}} + :href="commitRef.ref_url"> + {{commitRef.name}}
- {{short_sha}} + :href="commitUrl"> + {{shortSha}}

@@ -149,7 +149,7 @@ + :href="commitUrl"> {{title}} diff --git a/spec/javascripts/environments/environment_external_url_spec.js.es6 b/spec/javascripts/environments/environment_external_url_spec.js.es6 index 156506ef28f..35177d9b651 100644 --- a/spec/javascripts/environments/environment_external_url_spec.js.es6 +++ b/spec/javascripts/environments/environment_external_url_spec.js.es6 @@ -12,7 +12,7 @@ describe('External URL Component', () => { const component = new window.gl.environmentsList.ExternalUrlComponent({ el: document.querySelector('.test-dom-element'), propsData: { - external_url: externalURL, + externalUrl: externalURL, }, }); diff --git a/spec/javascripts/environments/environment_rollback_spec.js.es6 b/spec/javascripts/environments/environment_rollback_spec.js.es6 index 29449bbbd9e..d54ec9aa5da 100644 --- a/spec/javascripts/environments/environment_rollback_spec.js.es6 +++ b/spec/javascripts/environments/environment_rollback_spec.js.es6 @@ -13,8 +13,8 @@ describe('Rollback Component', () => { const component = new window.gl.environmentsList.RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { - retry_url: retryURL, - is_last_deployment: true, + retryUrl: retryURL, + isLastDeployment: true, }, }); @@ -25,8 +25,8 @@ describe('Rollback Component', () => { const component = new window.gl.environmentsList.RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { - retry_url: retryURL, - is_last_deployment: true, + retryUrl: retryURL, + isLastDeployment: true, }, }); @@ -38,8 +38,8 @@ describe('Rollback Component', () => { const component = new window.gl.environmentsList.RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { - retry_url: retryURL, - is_last_deployment: false, + retryUrl: retryURL, + isLastDeployment: false, }, }); diff --git a/spec/javascripts/environments/environment_stop_spec.js.es6 b/spec/javascripts/environments/environment_stop_spec.js.es6 index b842be4da61..84a41b2bf46 100644 --- a/spec/javascripts/environments/environment_stop_spec.js.es6 +++ b/spec/javascripts/environments/environment_stop_spec.js.es6 @@ -13,7 +13,7 @@ describe('Stop Component', () => { component = new window.gl.environmentsList.StopComponent({ el: document.querySelector('.test-dom-element'), propsData: { - stop_url: stopURL, + stopUrl: stopURL, }, }); }); From d98f03d3f5b66d84950f01926f85f8892d55a3d9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 8 Dec 2016 12:06:49 +0000 Subject: [PATCH 120/140] Adds tests --- .../components/environment_actions.js.es6 | 8 ++-- .../vue_common_component/commit.js.es6 | 2 +- .../environment_actions_spec.js.es6 | 32 +++++++++++++++- .../environment_external_url_spec.js.es6 | 2 +- .../environment_rollback_spec.js.es6 | 6 +-- .../vue_common_components/commit_spec.js.es6 | 37 +++++++++++-------- 6 files changed, 62 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6 index 02cdb305217..d35a0441961 100644 --- a/app/assets/javascripts/environments/components/environment_actions.js.es6 +++ b/app/assets/javascripts/environments/components/environment_actions.js.es6 @@ -23,8 +23,8 @@