From dab95689a3f267e6ee4e4fa9da304a01d37a5a4e Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Thu, 23 Jun 2016 13:07:51 +0000 Subject: [PATCH 001/176] Fix `ref` parameter name for `commits/statuses` The attribute to filter by branch or tag needs to be named `ref`, not `ref_name`. And indeed the attribute in the JSON response is `ref` (and not `ref_name`). Tested on Gitlab CE 8.9. --- doc/api/commits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/commits.md b/doc/api/commits.md index 57c2e1d9b87..d1317e5cc07 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -229,7 +229,7 @@ GET /projects/:id/repository/commits/:sha/statuses | --------- | ---- | -------- | ----------- | | `id` | integer | yes | The ID of a project | `sha` | string | yes | The commit SHA -| `ref_name`| string | no | The name of a repository branch or tag or, if not given, the default branch +| `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch | `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test` | `name` | string | no | Filter by [job name](../ci/yaml/README.md#jobs), e.g., `bundler:audit` | `all` | boolean | no | Return all statuses, not only the latest ones From 33540238ee7da03ef532385e7a52103276252c18 Mon Sep 17 00:00:00 2001 From: Christian Meter Date: Thu, 7 Jul 2016 11:47:13 +0000 Subject: [PATCH 002/176] Replace builds_emails_service.png to fit to new layout of GitLab --- .../img/builds_emails_service.png | Bin 33943 -> 30956 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/project_services/img/builds_emails_service.png b/doc/project_services/img/builds_emails_service.png index 88943dc410e8ad8a7edee5ff3dcc7d162ab00390..440728795beac492656846ccbe709cfef179e021 100644 GIT binary patch literal 30956 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 U`iU|F@E1rzR7NCUSoh=q0PkGbCjbBd literal 33943 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 Date: Sun, 10 Jul 2016 23:35:10 +0000 Subject: [PATCH 003/176] Fix docker.sock reference in config.toml Mapping was omitted so it would just create a temp volume. --- doc/ci/docker/using_docker_build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 7f83f846454..ee924323d96 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -188,7 +188,7 @@ In order to do that, follow the steps: image = "docker:latest" privileged = false disable_cache = false - volumes = ["/var/run/docker.sock", "/cache"] + volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] [runners.cache] Insecure = false ``` From 24538ec0902667d399b47c7715aa75035aacecf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Libor=20Klep=C3=A1=C4=8D?= Date: Mon, 8 Aug 2016 16:28:57 +1000 Subject: [PATCH 004/176] Fix error 500 on Runners page. ~~~~ ActionView::Template::Error (Missing partial kaminari/_paginator ~~~~ Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=819903 Signed-off-by: Dmitry Smirnov --- app/views/admin/runners/index.html.haml | 2 +- app/views/admin/runners/show.html.haml | 2 +- app/views/projects/runners/_specific_runners.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index a53876d6757..f68b6cba524 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -73,4 +73,4 @@ - @runners.each do |runner| = render "admin/runners/runner", runner: runner - = paginate @runners + = paginate @runners, theme: "gitlab" diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 61abfc6ecbe..8ca2c97481c 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -67,7 +67,7 @@ = form_for [:admin, project.namespace.becomes(Namespace), project, project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: @runner.id = f.submit 'Enable', class: 'btn btn-xs' - = paginate @projects + = paginate @projects, theme: "gitlab" .col-md-6 %h4 Recent builds served by this runner diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index d469dda5b81..4278980d1f4 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -26,4 +26,4 @@ %h4.underlined-title Available specific runners %ul.bordered-list.available-specific-runners = render partial: 'runner', collection: @assignable_runners, as: :runner - = paginate @assignable_runners + = paginate @assignable_runners, theme: "gitlab" From c920aad3ee82900c11f240f913abcd2caa0a3353 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 3 Oct 2016 01:11:50 +0000 Subject: [PATCH 005/176] Grammar fixes in docs --- doc/ci/docker/using_docker_images.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index a849905ac6b..1007db3df1a 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -37,7 +37,7 @@ The registered runner will use the `ruby:2.1` docker image and will run two services, `postgres:latest` and `mysql:latest`, both of which will be accessible during the build process. -## What is image +## What is an image The `image` keyword is the name of the docker image that is present in the local Docker Engine (list all images with `docker images`) or any image that @@ -47,7 +47,7 @@ Hub please read the [Docker Fundamentals][] documentation. In short, with `image` we refer to the docker image, which will be used to create a container on which your build will run. -## What is service +## What is a service The `services` keyword defines just another docker image that is run during your build and is linked to the docker image that the `image` keyword defines. @@ -61,7 +61,7 @@ time the project is built. You can see some widely used services examples in the relevant documentation of [CI services examples](../services/README.md). -### How is service linked to the build +### How services are linked to the build To better understand how the container linking works, read [Linking containers together][linking-containers]. From d2a9eefbfe86f1a152673d34f5803107c79c7d51 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 3 Oct 2016 23:18:35 +0800 Subject: [PATCH 006/176] Show commits from source project. Be consistent with: MergeRequest#pipeline Fixes #3596 --- app/views/projects/merge_requests/show/_commits.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml index 0b05785430b..61020516bcf 100644 --- a/app/views/projects/merge_requests/show/_commits.html.haml +++ b/app/views/projects/merge_requests/show/_commits.html.haml @@ -3,4 +3,4 @@ Most recent commits displayed first %ol#commits-list.list-unstyled - = render "projects/commits/commits", project: @merge_request.project + = render "projects/commits/commits", project: @merge_request.source_project From f11ebb9556612f663264868a24ee8b774babda32 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 5 Oct 2016 18:08:16 +0800 Subject: [PATCH 007/176] Add a view test for showing source commits --- .../merge_requests/_commits.html.haml_spec.rb | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 spec/views/projects/merge_requests/_commits.html.haml_spec.rb diff --git a/spec/views/projects/merge_requests/_commits.html.haml_spec.rb b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb new file mode 100644 index 00000000000..6f70b3daf8e --- /dev/null +++ b/spec/views/projects/merge_requests/_commits.html.haml_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe 'projects/merge_requests/show/_commits.html.haml' do + include Devise::Test::ControllerHelpers + + let(:user) { create(:user) } + let(:target_project) { create(:project) } + + let(:source_project) do + create(:project, forked_from_project: target_project) + end + + let(:merge_request) do + create(:merge_request, :simple, + source_project: source_project, + target_project: target_project, + author: user) + end + + before do + controller.prepend_view_path('app/views/projects') + + assign(:merge_request, merge_request) + assign(:commits, merge_request.commits) + end + + it 'shows commits from source project' do + render + + commit = source_project.commit(merge_request.source_branch) + href = namespace_project_commit_path( + source_project.namespace, + source_project, + commit) + + expect(rendered).to have_link(Commit.truncate_sha(commit.sha), href: href) + end +end From b5707a80ac1caa3aad809c0c9a0ad4fa91c9a834 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 5 Oct 2016 21:54:55 +0800 Subject: [PATCH 008/176] Add CHANGELOG entry [ci skip] --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 58f1e4a59c2..5bb9ff1ec0e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,7 @@ v 8.13.0 (unreleased) - Add word-wrap to issue title on issue and milestone boards (ClemMakesApps) - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) - Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska) + - Fix showing commits from source project for merge request !6658 - Add configurable email subject suffix (Fu Xu) - Use a ConnectionPool for Rails.cache on Sidekiq servers - Replace `alias_method_chain` with `Module#prepend` @@ -42,7 +43,7 @@ v 8.13.0 (unreleased) - Notify the Merger about merge after successful build (Dimitris Karakasilis) - Fix broken repository 500 errors in project list - Close todos when accepting merge requests via the API !6486 (tonygambone) - - Changed Slack service user referencing from full name to username (Sebastian Poxhofer) + - Changed Slack service user referencing from full name to username (Sebastian Poxhofer) v 8.12.4 (unreleased) From 43438c3695ac06e0d12e24824f2c191f9608a51b Mon Sep 17 00:00:00 2001 From: Ismael Arenzana Date: Fri, 7 Oct 2016 17:29:48 +0000 Subject: [PATCH 009/176] Changed gitlab-shell version to avoid warning when precompiling the assets. --- doc/update/8.11-to-8.12.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md index 07743d050f7..cddfa7e3e01 100644 --- a/doc/update/8.11-to-8.12.md +++ b/doc/update/8.11-to-8.12.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-12-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v3.6.0 +sudo -u git -H git checkout v3.6.1 ``` ### 6. Update gitlab-workhorse From a04e9f9b61d93ca872fe108d83f519ba6151631e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 11 Oct 2016 03:32:52 +0000 Subject: [PATCH 010/176] Remove bad stuffs from auto-merging --- CHANGELOG | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index eb9905a6f52..da4e66762e4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -83,8 +83,6 @@ v 8.13.0 (unreleased) - Fix Pipeline list commit column width should be adjusted - Close todos when accepting merge requests via the API !6486 (tonygambone) - Changed Slack service user referencing from full name to username (Sebastian Poxhofer) - -v 8.12.4 (unreleased) - Retouch environments list and deployments list - Add Container Registry on/off status to Admin Area !6638 (the-undefined) - Grouped pipeline dropdown is a scrollable container From 3ca064eeef874cbb258903abb8009e3ce257a4b4 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Thu, 13 Oct 2016 18:08:15 +0500 Subject: [PATCH 011/176] Add missing tests for download snippet ref: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6720 --- spec/controllers/snippets_controller_spec.rb | 190 ++++++++++--------- 1 file changed, 100 insertions(+), 90 deletions(-) diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index 41d263a46a4..2d762fdaa04 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -116,31 +116,100 @@ describe SnippetsController do end end - describe 'GET #raw' do - let(:user) { create(:user) } + %w(raw download).each do |action| + describe "GET #{action}" do + context 'when the personal snippet is private' do + let(:personal_snippet) { create(:personal_snippet, :private, author: user) } - context 'when the personal snippet is private' do - let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + context 'when signed in' do + before do + sign_in(user) + end - context 'when signed in' do - before do - sign_in(user) - end + context 'when signed in user is not the author' do + let(:other_author) { create(:author) } + let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } - context 'when signed in user is not the author' do - let(:other_author) { create(:author) } - let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } + it 'responds with status 404' do + get action, id: other_personal_snippet.to_param - it 'responds with status 404' do - get :raw, id: other_personal_snippet.to_param + expect(response).to have_http_status(404) + end + end - expect(response).to have_http_status(404) + context 'when signed in user is the author' do + before { get action, id: personal_snippet.to_param } + + it 'responds with status 200' do + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end + + it 'has expected headers' do + expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') + + if action == :download + expect(response.header['Content-Disposition']).to match(/attachment/) + elsif action == :raw + expect(response.header['Content-Disposition']).to match(/inline/) + end + end end end - context 'when signed in user is the author' do - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param + context 'when not signed in' do + it 'redirects to the sign in page' do + get action, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is internal' do + let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'responds with status 200' do + get action, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get action, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is public' do + let(:personal_snippet) { create(:personal_snippet, :public, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'responds with status 200' do + get action, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response).to have_http_status(200) + end + end + + context 'when not signed in' do + it 'responds with status 200' do + get action, id: personal_snippet.to_param expect(assigns(:snippet)).to eq(personal_snippet) expect(response).to have_http_status(200) @@ -148,84 +217,25 @@ describe SnippetsController do end end - context 'when not signed in' do - it 'redirects to the sign in page' do - get :raw, id: personal_snippet.to_param + context 'when the personal snippet does not exist' do + context 'when signed in' do + before do + sign_in(user) + end - expect(response).to redirect_to(new_user_session_path) - end - end - end + it 'responds with status 404' do + get action, id: 'doesntexist' - context 'when the personal snippet is internal' do - let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } - - context 'when signed in' do - before do - sign_in(user) + expect(response).to have_http_status(404) + end end - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param + context 'when not signed in' do + it 'responds with status 404' do + get action, id: 'doesntexist' - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) - end - end - - context 'when not signed in' do - it 'redirects to the sign in page' do - get :raw, id: personal_snippet.to_param - - expect(response).to redirect_to(new_user_session_path) - end - end - end - - context 'when the personal snippet is public' do - let(:personal_snippet) { create(:personal_snippet, :public, author: user) } - - context 'when signed in' do - before do - sign_in(user) - end - - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param - - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) - end - end - - context 'when not signed in' do - it 'renders the raw snippet' do - get :raw, id: personal_snippet.to_param - - expect(assigns(:snippet)).to eq(personal_snippet) - expect(response).to have_http_status(200) - end - end - end - - context 'when the personal snippet does not exist' do - context 'when signed in' do - before do - sign_in(user) - end - - it 'responds with status 404' do - get :raw, id: 'doesntexist' - - expect(response).to have_http_status(404) - end - end - - context 'when not signed in' do - it 'responds with status 404' do - get :raw, id: 'doesntexist' - - expect(response).to have_http_status(404) + expect(response).to have_http_status(404) + end end end end From b2c771f45261e1484158d1304cd898e866002f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 18:44:52 +0200 Subject: [PATCH 012/176] Add an API styleguide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/README.md | 2 ++ doc/development/api_styleguide.md | 58 +++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 doc/development/api_styleguide.md diff --git a/doc/development/README.md b/doc/development/README.md index 9706cb1de7f..630fe64cee6 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -8,6 +8,8 @@ ## Styleguides +- [API styleguide](api_styleguide.md) Use this styleguide if you are + contributing to the API. - [Documentation styleguide](doc_styleguide.md) Use this styleguide if you are contributing to documentation. - [SQL Migration Style Guide](migration_style_guide.md) for creating safe SQL migrations diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md new file mode 100644 index 00000000000..ee5e7ad3988 --- /dev/null +++ b/doc/development/api_styleguide.md @@ -0,0 +1,58 @@ +# API styleguide + +This styleguide recommends best practices for the API development. + +## Declared params + +> Grape allows you to access only the parameters that have been declared by your +`params` block. It filters out the params that have been passed, but are not +allowed. + +– https://github.com/ruby-grape/grape#declared + +### Exclude params from parent namespaces + +> By default `declared(params) `includes parameters that were defined in all +parent namespaces. + +– https://github.com/ruby-grape/grape#include-parent-namespaces + +In most cases you will want to exclude params from the parent namespaces: + +```ruby +declared(params, include_parent_namespaces: false) +``` + +### When to use `declared(params)`? + +You should always use `declared(params)` when you pass the params hash as +arguments to a method call. + +For instance: + +```ruby +# bad +User.create(params) # imagine the user submitted `admin=1`... :) + +# good +User.create(declared(params, include_parent_namespaces: false).to_h) +``` + +>**Note:** +`declared(params)` return a `Hashie::Mash` object, on which you will have to +call `.to_h`. + +But we can use directly `params[key]` when we access single elements. + +For instance: + +```ruby +# good +Model.create(foo: params[:foo]) +``` + +>**Note:** +Since you [should use Grape's DSL to declare params](doc_styleguide.md#method-description), [parameters validation and +coercion] comes for free! + +[parameters validation and coercion]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion From c1dd1795ed57c9481a1a9cab81daef39dd218346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 19:35:57 +0200 Subject: [PATCH 013/176] Move the Grape DSL part from Doc styleguide to API styleguide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also improve API styleguide Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 48 +++++++++++++++++++++++++++---- doc/development/doc_styleguide.md | 6 ---- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index ee5e7ad3988..be303fc6d39 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -2,6 +2,47 @@ This styleguide recommends best practices for the API development. +## Instance variables + +Please do not use instance variables, there is no need for them (we don't need +to access them as we do in Rails views), local variables are fine. + +## Entities + +Always use an [Entity] to present the endpoint's payload. + +## Methods and parameters description + +Every method must be described using the [Grape DSL](https://github.com/ruby-grape/grape#describing-methods) +(see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/environments.rb +for a good example): + +- `desc` for the method summary. You should pass it a block for additional + details such as: + - The GitLab version when the endpoint was added + - If the endpoint is deprecated, and if yes when will it be removed + +- `params` for the method params. This acts as description, validation, and + coercion of the parameters + +A good example is as follows: + +```ruby +desc 'Get all broadcast messages' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage +end +params do + optional :page, type: Integer, desc: 'Current page number' + optional :per_page, type: Integer, desc: 'Number of messages per page' +end +get do + messages = BroadcastMessage.all + + present paginate(messages), with: Entities::BroadcastMessage +end +``` + ## Declared params > Grape allows you to access only the parameters that have been declared by your @@ -10,7 +51,7 @@ allowed. – https://github.com/ruby-grape/grape#declared -### Exclude params from parent namespaces +### Exclude params from parent namespaces! > By default `declared(params) `includes parameters that were defined in all parent namespaces. @@ -51,8 +92,5 @@ For instance: Model.create(foo: params[:foo]) ``` ->**Note:** -Since you [should use Grape's DSL to declare params](doc_styleguide.md#method-description), [parameters validation and -coercion] comes for free! - +[Entity]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/entities.rb [parameters validation and coercion]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index 0b725cf200c..882da2a6562 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -342,12 +342,6 @@ You can use the following fake tokens as examples. Here is a list of must-have items. Use them in the exact order that appears on this document. Further explanation is given below. -- Every method must be described using [Grape's DSL](https://github.com/ruby-grape/grape/tree/v0.13.0#describing-methods) - (see https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/environments.rb - for a good example): - - `desc` for the method summary (you can pass it a block for additional details) - - `params` for the method params (this acts as description **and** validation - of the params) - Every method must have the REST API request. For example: ``` From 3a1d9bccd6c3ce8249e90153f4299db1c037eb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 19:38:38 +0200 Subject: [PATCH 014/176] Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index be303fc6d39..94047dfe173 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -1,6 +1,6 @@ # API styleguide -This styleguide recommends best practices for the API development. +This styleguide recommends best practices for API development. ## Instance variables From b4810fd2bce42d66cada56af3ef697219f176b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 13 Oct 2016 19:40:20 +0200 Subject: [PATCH 015/176] More improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index 94047dfe173..2a41314d2db 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -22,8 +22,8 @@ for a good example): - The GitLab version when the endpoint was added - If the endpoint is deprecated, and if yes when will it be removed -- `params` for the method params. This acts as description, validation, and - coercion of the parameters +- `params` for the method params. This acts as description, + [validation, and coercion of the parameters] A good example is as follows: @@ -93,4 +93,4 @@ Model.create(foo: params[:foo]) ``` [Entity]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/api/entities.rb -[parameters validation and coercion]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion +[validation, and coercion of the parameters]: https://github.com/ruby-grape/grape#parameter-validation-and-coercion From 11e40c0e26e07d153cd2712bf23b5d679b54615c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 17 Oct 2016 10:10:13 +0200 Subject: [PATCH 016/176] Improve copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- doc/development/api_styleguide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md index 2a41314d2db..ce444ebdde4 100644 --- a/doc/development/api_styleguide.md +++ b/doc/development/api_styleguide.md @@ -20,7 +20,7 @@ for a good example): - `desc` for the method summary. You should pass it a block for additional details such as: - The GitLab version when the endpoint was added - - If the endpoint is deprecated, and if yes when will it be removed + - If the endpoint is deprecated, and if so, when will it be removed - `params` for the method params. This acts as description, [validation, and coercion of the parameters] @@ -83,7 +83,7 @@ User.create(declared(params, include_parent_namespaces: false).to_h) `declared(params)` return a `Hashie::Mash` object, on which you will have to call `.to_h`. -But we can use directly `params[key]` when we access single elements. +But we can use `params[key]` directly when we access single elements. For instance: From baa3d0b3744b6ee7ef067ec39238257748fde71b Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 18 Sep 2016 17:20:40 +0100 Subject: [PATCH 017/176] Added tooltip with jobs full name to build items in graph Added status to build tooltip and removed status icon tooltip Added Pipelines class to force tooltips ontop of the dropdown, we cannot do this with data attributes because dropdown toggle is already set Corrected dispatcher invocation --- app/assets/javascripts/pipelines.js.es6 | 11 +++++++++++ app/helpers/ci_status_helper.rb | 4 ++-- app/views/projects/commit/_pipeline_stage.html.haml | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index a7624de6089..dd320c52e44 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -4,6 +4,17 @@ constructor() { $(document).off('click', '.toggle-pipeline-btn').on('click', '.toggle-pipeline-btn', this.toggleGraph); this.addMarginToBuildColumns(); + this.initGroupedPipelineTooltips(); + } + + initGroupedPipelineTooltips() { + $('.dropdown-menu-toggle', $('.grouped-pipeline-dropdown').parent()).each(function() { + const $this = $(this); + $this.tooltip({ + title: $this.data('tooltip-title'), + placement: 'bottom' + }); + }); } toggleGraph() { diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index b7f48630bd4..0e727f3dcf0 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -71,10 +71,10 @@ module CiStatusHelper Ci::Runner.shared.blank? end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body') + def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body', show_tooltip: true) klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" - data = { toggle: 'tooltip', placement: tooltip_placement, container: container } + data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if show_tooltip if path link_to ci_icon_for_status(status), path, diff --git a/app/views/projects/commit/_pipeline_stage.html.haml b/app/views/projects/commit/_pipeline_stage.html.haml index 289aa5178b1..993fc89d23d 100644 --- a/app/views/projects/commit/_pipeline_stage.html.haml +++ b/app/views/projects/commit/_pipeline_stage.html.haml @@ -5,7 +5,7 @@ - is_playable = status.playable? && can?(current_user, :update_build, @project) %li.build{ class: ("playable" if is_playable) } .curve - .build-content + .build-content{ { data: { toggle: 'tooltip', title: "#{group_name} - #{status.status}", placement: 'bottom' } } } = render "projects/#{status.to_partial_path}_pipeline", subject: status - else %li.build From a871032f4a5353d118e0f0dbd7373cfdefb89f57 Mon Sep 17 00:00:00 2001 From: Tobias Genberg Date: Wed, 19 Oct 2016 05:52:07 +0000 Subject: [PATCH 018/176] Changing gitlab-shell version to 3.6.6 instead of 3.6.3, later on the upgrade process it will complain about running 3.6.3 instead of 3.6.6. --- doc/update/8.12-to-8.13.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.12-to-8.13.md b/doc/update/8.12-to-8.13.md index 8940d14559b..c0084d9d59c 100644 --- a/doc/update/8.12-to-8.13.md +++ b/doc/update/8.12-to-8.13.md @@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-13-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch --all --tags -sudo -u git -H git checkout v3.6.3 +sudo -u git -H git checkout v3.6.6 ``` ### 6. Update gitlab-workhorse From f744a5c12f0c82573f5247b9e7580c3eeb7768cf Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 7 Oct 2016 19:30:34 +0100 Subject: [PATCH 019/176] Added dyanmic position adjustment Added tooltips for dropdown items Reverted pretty much everything in favour of a DOM approach Simplified JS --- app/assets/javascripts/pipelines.js.es6 | 11 ----------- app/assets/stylesheets/pages/pipelines.scss | 14 ++++++++++---- app/helpers/ci_status_helper.rb | 4 ++-- .../projects/ci/builds/_build_pipeline.html.haml | 4 ++-- .../projects/commit/_pipeline_stage.html.haml | 2 +- .../commit/_pipeline_status_group.html.haml | 2 +- .../_generic_commit_status_pipeline.html.haml | 13 +++++++------ 7 files changed, 23 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index dd320c52e44..a7624de6089 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -4,17 +4,6 @@ constructor() { $(document).off('click', '.toggle-pipeline-btn').on('click', '.toggle-pipeline-btn', this.toggleGraph); this.addMarginToBuildColumns(); - this.initGroupedPipelineTooltips(); - } - - initGroupedPipelineTooltips() { - $('.dropdown-menu-toggle', $('.grouped-pipeline-dropdown').parent()).each(function() { - const $this = $(this); - $this.tooltip({ - title: $this.data('tooltip-title'), - placement: 'bottom' - }); - }); } toggleGraph() { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 7b71876b822..46308742aaf 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -360,10 +360,6 @@ &:hover { background-color: $gray-lighter; - - .dropdown-menu-toggle { - background-color: transparent; - } } &.playable { @@ -393,6 +389,15 @@ } } + .tooltip { + white-space: nowrap; + + .tooltip-inner { + overflow: hidden; + text-overflow: ellipsis; + } + } + .ci-status-text { width: 135px; white-space: nowrap; @@ -410,6 +415,7 @@ } .dropdown-menu-toggle { + background-color: transparent; border: none; width: auto; padding: 0; diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 0e727f3dcf0..b7f48630bd4 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -71,10 +71,10 @@ module CiStatusHelper Ci::Runner.shared.blank? end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body', show_tooltip: true) + def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body') klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" - data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if show_tooltip + data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if path link_to ci_icon_for_status(status), path, diff --git a/app/views/projects/ci/builds/_build_pipeline.html.haml b/app/views/projects/ci/builds/_build_pipeline.html.haml index 017d3ff6af2..55965172d3f 100644 --- a/app/views/projects/ci/builds/_build_pipeline.html.haml +++ b/app/views/projects/ci/builds/_build_pipeline.html.haml @@ -1,10 +1,10 @@ - is_playable = subject.playable? && can?(current_user, :update_build, @project) - if is_playable - = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, title: 'Play' do + = link_to play_namespace_project_build_path(subject.project.namespace, subject.project, subject, return_to: request.original_url), method: :post, data: { toggle: 'tooltip', title: "#{subject.name} - play", container: '.pipeline-graph', placement: 'bottom' } do = render_status_with_link('build', 'play') .ci-status-text= subject.name - elsif can?(current_user, :read_build, @project) - = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject) do + = link_to namespace_project_build_path(subject.project.namespace, subject.project, subject), data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } do %span.ci-status-icon = render_status_with_link('build', subject.status) .ci-status-text= subject.name diff --git a/app/views/projects/commit/_pipeline_stage.html.haml b/app/views/projects/commit/_pipeline_stage.html.haml index 993fc89d23d..289aa5178b1 100644 --- a/app/views/projects/commit/_pipeline_stage.html.haml +++ b/app/views/projects/commit/_pipeline_stage.html.haml @@ -5,7 +5,7 @@ - is_playable = status.playable? && can?(current_user, :update_build, @project) %li.build{ class: ("playable" if is_playable) } .curve - .build-content{ { data: { toggle: 'tooltip', title: "#{group_name} - #{status.status}", placement: 'bottom' } } } + .build-content = render "projects/#{status.to_partial_path}_pipeline", subject: status - else %li.build diff --git a/app/views/projects/commit/_pipeline_status_group.html.haml b/app/views/projects/commit/_pipeline_status_group.html.haml index 5d0d5ba0262..f2d71fa6989 100644 --- a/app/views/projects/commit/_pipeline_status_group.html.haml +++ b/app/views/projects/commit/_pipeline_status_group.html.haml @@ -1,5 +1,5 @@ - group_status = CommitStatus.where(id: subject).status -%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } } +%button.dropdown-menu-toggle.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } } %span.ci-status-icon = render_status_with_link('build', group_status) %span.ci-status-text diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml index 0a66d60accc..c45b73e4225 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml @@ -1,9 +1,10 @@ -- if subject.target_url - = link_to subject.target_url do +%a{ data: { toggle: 'tooltip', title: "#{subject.name} - #{subject.status}", container: '.pipeline-graph', placement: 'bottom' } } + - if subject.target_url + = link_to subject.target_url do + %span.ci-status-icon + = render_status_with_link('commit status', subject.status) + %span.ci-status-text= subject.name + - else %span.ci-status-icon = render_status_with_link('commit status', subject.status) %span.ci-status-text= subject.name -- else - %span.ci-status-icon - = render_status_with_link('commit status', subject.status) - %span.ci-status-text= subject.name From ed9838cd292ce78ae98079f513d0d1648b7f49f0 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 14 Oct 2016 19:38:41 -0300 Subject: [PATCH 020/176] Create project feature when project is created --- CHANGELOG.md | 1 + app/models/project.rb | 7 +---- ...5_generate_project_feature_for_projects.rb | 28 +++++++++++++++++++ db/schema.rb | 2 +- spec/models/project_spec.rb | 14 ++++++++-- 5 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20161019213545_generate_project_feature_for_projects.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b018fc0d57..e9d07ed0927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun) - Cancelled pipelines could be retried. !6927 - Updating verbiage on git basics to be more intuitive + - Fix project_feature record not generated on project creation - Clarify documentation for Runners API (Gennady Trafimenkov) - The instrumentation for Banzai::Renderer has been restored - Change user & group landing page routing from /u/:username to /:username diff --git a/app/models/project.rb b/app/models/project.rb index 653c38322c5..6685baab699 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -32,8 +32,8 @@ class Project < ActiveRecord::Base default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } after_create :ensure_dir_exist + after_create :create_project_feature, unless: :project_feature after_save :ensure_dir_exist, if: :namespace_id_changed? - after_initialize :setup_project_feature # set last_activity_at to the same as created_at after_create :set_last_activity_at @@ -1310,11 +1310,6 @@ class Project < ActiveRecord::Base "projects/#{id}/pushes_since_gc" end - # Prevents the creation of project_feature record for every project - def setup_project_feature - build_project_feature unless project_feature - end - def default_branch_protected? current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL || current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE diff --git a/db/migrate/20161019213545_generate_project_feature_for_projects.rb b/db/migrate/20161019213545_generate_project_feature_for_projects.rb new file mode 100644 index 00000000000..4554e14b0df --- /dev/null +++ b/db/migrate/20161019213545_generate_project_feature_for_projects.rb @@ -0,0 +1,28 @@ +class GenerateProjectFeatureForProjects < ActiveRecord::Migration + DOWNTIME = true + + DOWNTIME_REASON = <<-HEREDOC + Application was eager loading project_feature for all projects generating an extra query + everytime a project was fetched. We removed that behavior to avoid the extra query, this migration + makes sure all projects have a project_feature record associated. + HEREDOC + + def up + # Generate enabled values for each project feature 20, 20, 20, 20, 20 + # All features are enabled by default + enabled_values = [ProjectFeature::ENABLED] * 5 + + execute <<-EOF.strip_heredoc + INSERT INTO project_features + (project_id, merge_requests_access_level, builds_access_level, + issues_access_level, snippets_access_level, wiki_access_level) + (SELECT projects.id, #{enabled_values.join(',')} FROM projects LEFT OUTER JOIN project_features + ON project_features.project_id = projects.id + WHERE project_features.id IS NULL) + EOF + end + + def down + "Not needed" + end +end diff --git a/db/schema.rb b/db/schema.rb index 65f55aa109b..a3c7fc2fd57 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: 20161018024550) do +ActiveRecord::Schema.define(version: 20161019213545) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e6d98e25d0b..f4dda1ee558 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -67,6 +67,14 @@ describe Project, models: true do it { is_expected.to have_many(:notification_settings).dependent(:destroy) } it { is_expected.to have_many(:forks).through(:forked_project_links) } + context 'after create' do + it "creates project feature" do + project = FactoryGirl.build(:project) + + expect { project.save }.to change{ project.project_feature.present? }.from(false).to(true) + end + end + describe '#members & #requesters' do let(:project) { create(:project, :public) } let(:requester) { create(:user) } @@ -531,9 +539,9 @@ describe Project, models: true do end describe '#has_wiki?' do - let(:no_wiki_project) { build(:project, wiki_enabled: false, has_external_wiki: false) } - let(:wiki_enabled_project) { build(:project) } - let(:external_wiki_project) { build(:project, has_external_wiki: true) } + let(:no_wiki_project) { create(:project, wiki_access_level: ProjectFeature::DISABLED, has_external_wiki: false) } + let(:wiki_enabled_project) { create(:project) } + let(:external_wiki_project) { create(:project, has_external_wiki: true) } it 'returns true if project is wiki enabled or has external wiki' do expect(wiki_enabled_project).to have_wiki From 373c232c05dce8b720017265763ed34f36f1aeaf Mon Sep 17 00:00:00 2001 From: evhoffmann Date: Wed, 19 Oct 2016 18:30:06 -0400 Subject: [PATCH 021/176] a round of terms and edits --- doc/university/glossary/README.md | 367 +++++++++++++++++++----------- 1 file changed, 230 insertions(+), 137 deletions(-) diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index a86ff165f2e..2cca5439a5a 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -6,83 +6,87 @@ Please add any terms that you discover that you think would be useful for others ### 2FA -User authentication by combination of 2 different steps during login. This allows for more security. +User authentication by combination of 2 different steps during login. This allows for [more security](https://about.gitlab.com/handbook/security/). ### Access Levels -Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. -See, [GitLab's Permission Guidelines](http://doc.gitlab.com/ce/permissions/permissions.html) +Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions. See [GitLab's Permission Guidelines](http://doc.gitlab.com/ce/permissions/permissions.html) ### Active Directory (AD) -A Microsoft based directory service for windows domain networks. It uses LDAP technology under the hood +A Microsoft-based [directory service](https://msdn.microsoft.com/en-us/library/bb742424.aspx) for windows domain networks. It uses LDAP technology under the hood. ### Agile -Building and delivering software in phases/parts rather than trying to build everything at once then delivering to the user/client. The later is known as a WaterFall model +Building and [delivering software](http://agilemethodology.org/) in phases/parts rather than trying to build everything at once then delivering to the user/client. The latter is known as the WaterFall model. ### Application Lifecycle Management (ALM) -Entire product lifecycle management process for an application. From requirements management, development and testing until deployment. +The entire product lifecycle management process for an application, from requirements management, development, and testing until deployment. GitLab has [advantages](https://docs.google.com/presentation/d/1vCU-NbZWz8NTNK8Vu3y4zGMAHb5DpC8PE5mHtw1PWfI/edit#slide=id.g72f2e4906_2_288) over both legacy and modern ALM tools. ### Artifactory -Version control for binaries. +A version control [system](https://www.jfrog.com/open-source/#os-arti) for non-text files. ### Artifacts -objects (usually binary and large) created by a build process +Objects (usually binary and large) created by a build process. These can include use cases, class diagrams, requirements and design documents. ### Atlassian -A company that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. See [Atlassian] (https://www.atlassian.com) +A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. ### Audit Log -*** Needs definition here +Also called an [audit trail](https://en.wikipedia.org/wiki/Audit_trail), an audit log is a document that records an event in an IT system. ### Auto Defined User Group -User groups are a way of centralizing control over important management tasks, particularly access control and password policies. -A simple example of such groups are the users and the admins groups. -In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc... +User groups are a way of centralizing control over important management tasks, particularly access control and password policies. A simple example of such groups are the users and the admins groups. +In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc. ### Bamboo -Atlassian's CI tool similar to GitLab CI and Jenkins +Atlassian's CI tool similar to GitLab CI and Jenkins. ### Basic Subscription -Entry level subscription for GitLab EE currently available in packs of 10 see [Basic subscription](https://about.gitlab.com/pricing/) +Entry level [subscription](https://about.gitlab.com/pricing/) for GitLab EE currently available in packs of 10. ### Bitbucket -Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor +Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor. ### Branch -A branch is a parallel version of a repository. Allows you to work on the repository without you affecting the "master" branch. Allows you to make changes without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master and to make the changes fo "live". +A branch is a parallel version of a repository. This allows you to work on the repository without affecting the "master" branch, and without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master. When your merge request is accepted your changes will be "live." ### Branded Login -Having your own logo on your GitLab instance login page instead of the GitLab logo. +Having your own logo on [your GitLab instance login page](https://docs.gitlab.com/ee/customization/branded_login_page.html) instead of the GitLab logo. + +### Build triggers +These protect your code base against breaks, for instance when a team is working on the same project. Learn about [setting up](https://docs.gitlab.com/ce/ci/triggers/README.html) build triggers. ### CEPH -is a distributed object store and file system designed to provide excellent performance, reliability and scalability. + A distributed object store and file [system](http://ceph.com/) designed to provide excellent performance, reliability and scalability. + +### ChatOps + +The ability to [initiate an action](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/1412) from chat. ChatBots run in your chat application and give you the ability to do "anything" from chat. ### Clone -A copy of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely. +A [copy](https://git-scm.com/docs/git-clone) of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely. ### Code Review -Examination of a progam's code. The main aim is to maintain high standards quality of code that is being shipped. +Examination of a progam's code. The main aim is to maintain high quality standards of code that is being shipped. Merge requests [serve as a code review tool](https://about.gitlab.com/2014/09/29/gitlab-flow/) in GitLab. ### Code Snippet -A small amount of code. Usually for the purpose of showing other developers how -to do something specific or reproduce a problem. +A small amount of code, usually selected for the purpose of showing other developers how to do something specific or reproduce a problem. ### Collaborator @@ -90,31 +94,39 @@ Person with read and write access to a repository who has been invited by reposi ### Commit -Is a change (revision) to a file, and also creates an ID that allows you to see revision history and who made the changes. +A [change](https://git-scm.com/docs/git-commit) (revision) to a file that also creates an ID, allowing you to see revision history and the author of the changes. ### Community -Everyone who is using GitLab +[Everyone](https://about.gitlab.com/community/) who uses GitLab. ### Confluence -Atlassian's product for collaboration of documents and projects. +Atlassian's product for collaboration on documents and projects. -### Continuous Deivery +### Continuous Delivery -Continuous delivery is a series of practices designed to ensure that code can be rapidly and safely deployed to production by delivering every change to a production-like environment and ensuring business applications and services function as expected through rigorous automated testing. +A [software engineering approach](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which continuous integration, automated testing, and automated deployment capabilities allow software to be developed and deployed rapidly, reliably and repeatedly with minimal human intervention. Still, the deployment to production is defined strategically and triggered manually. ### Continuous Deployment -Continuous deployment is the next step of continuous delivery: Every change that passes the automated tests is deployed to production automatically. +A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which every code change goes through the entire pipeline and is put into production automatically, resulting in many production deployments every day. It does everything that Continuous Delivery does, but the process is fully automated, there's no human intervention at all. ### Continuous Integration -A process that involves adding new code commits to source code with the combined code being run on an automated test to ensure that the changes do not break the software. +A [software development practice](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) in which you build and test software every time a developer pushes code to the application, and it happens several times a day. ### Contributor -Term used to a person contributing to an Open Source Project. +Term used for a person contributing to an open source project. + +### Conversational Development (ConvDev) + +A [natural evolution](https://about.gitlab.com/2016/09/14/gitlab-live-event-recap/) of software development that carries a conversation across functional groups throughout the development process, enabling developers to track the full path of development in a cohesive and intuitive way. ConvDev accelerates the development lifecycle by fostering collaboration and knowledge sharing from idea to production. + +### Cycle Time + +The time it takes to move from [idea to production](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#from-idea-to-production-with-gitlab). ### Data Centre @@ -122,37 +134,47 @@ Atlassian product for High Availability. ### Deploy Keys -An SSH key stored on the your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code. +A [SSH key](https://docs.gitlab.com/ce/gitlab-basics/create-your-ssh-keys.html)stored on your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code. ### Developer -For us (GitLab) this means a software developer, i.e. someone who makes software. It is also one of the levels of access in our multi level approval system. +For us at GitLab, this means a software developer, or someone who makes software. It is also one of the levels of access in our multi-level approval system. + +### DevOps + +The intersection of software engineering, quality assurance, and technology operations. Explore more DevOps topics in the [glossary by XebiaLabs](https://xebialabs.com/glossary/) ### Diff -Is the difference between two commits, or saved changes. This will also be shown visually after the changes. +The difference between two commits, or saved changes. This will also be shown visually after the changes. + +#### Directory + +A folder used for storing multiple files. ### Docker -Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. -This guarantees that it will always run the same, regardless of the environment it is running in. +Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in. + +### Dynamic Environment + +### Emacs ### Fork -Your own copy of a repository that allows you to make changes to the repository without affecting the original. +Your [own copy](https://docs.gitlab.com/ce/workflow/forking_workflow.html) of a repository that allows you to make changes to the repository without affecting the original. ### Gerrit -A code review tool built on top of Git. +A code review [tool](https://www.gerritcodereview.com/) built on top of Git. ### Git Hooks -Are scripts you can use to trigger actions at certain points. +[Scripts](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you can use to trigger actions at certain points. ### GitHost.io -Is a single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for -installing, updating, hosting, and backing up customers own private and secure GitLab instance. +A single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for installing, updating, hosting, and backing up customers' own private and secure GitLab instance. ### GitHub @@ -164,51 +186,78 @@ Our free on Premise solution with >100,000 users ### GitLab CI -Our own Continuos Integration feature that is shipped with each instance +Our own Continuos Integration [feature](https://about.gitlab.com/gitlab-ci/) that is shipped with each instance ### GitLab EE -Our premium on premise solution that currently has Basic, Standard and Plus subscription packages with additional features and support. +Our premium on premise [solution](https://about.gitlab.com/features/#enterprise) that currently has Basic, Standard and Plus subscription packages with additional features and support. ### GitLab.com Our free SaaS for public and private repositories. +### GitLab Geo + +Allows you to replicate your GitLab instance to other geographical locations as a read-only fully operational version. It [can be used](https://docs.gitlab.com/ee/gitlab-geo/README.html) for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster. + +### GitLab Pages +These allow you to [create websites](https://pages.gitlab.io/) for your GitLab projects, groups, or user account. + ### Gitolite -Is basically an access layer that sits on top of Git. Users are granted access to repos via a simple config file and you as an admin only needs the users public SSH key and a username from the user. +An [access layer](https://git-scm.com/book/en/v1/Git-on-the-Server-Gitolite) that sits on top of Git. Users are granted access to repos via a simple config file. As an admin, you only need the users' public SSH key and a username. ### Gitorious -A web based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. [Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/) +A web-based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. Read the[Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/). -### HADR +### Go -Sometimes written HA/DR. High Availability for Disaster Recovery. Usually refers to a strategy having a failover server in place in case the main server fails. +An open source programming [language](https://golang.org/). + +### GUI/ Git GUI + +A portable [graphical interface](https://git-scm.com/docs/git-gui) to Git that allows users to make changes to their repository by making new commits, amending existing ones, creating branches, performing local merges, and fetching/pushing to remote repositories. + +### High Availability for Disaster Recovery (HADR) + +Sometimes written HA/DR, this usually refers to a strategy for having a failover server in place in case the main server fails. ### Hip Chat -Atlassian's real time chat application for teams. Competitor to Slack, RocketChat and MatterMost. +Atlassian's real time chat application for teams, Hip Chat is a competitor to Slack, RocketChat and MatterMost. ### High Availability -Refers to a system or component that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing." +Refers to a [system or component](https://about.gitlab.com/high-availability/) that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing." + +### Inner-sourcing + +The [use of](https://about.gitlab.com/2014/09/05/innersourcing-using-the-open-source-workflow-to-improve-collaboration-within-an-organization/) open source development techniques within the corporation. + +### Internet Relay Chat (IRC) + +An [application layer protocol](http://www.irchelp.org/) that facilitates communication in the form of text. ### Issue Tracker -A tool used to manage, organize, and maintain a list of issues, making it easier for an organization to manage. +A [tool](https://docs.gitlab.com/ee/integration/external-issue-tracker.html) used to manage, organize, and maintain a list of issues, making it easier for an organization to manage. ### Jenkins -An Open Source CI tool written using the Java programming language. Does the same job as GitLab CI, Bamboo, Travis CI. It is extremely popular. see [Jenkins](https://jenkins-ci.org/) +An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins-ci.org/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular. ### Jira -Atlassian's project management software. i.e. a complex issue tracker. See[Jira](https://www.atlassian.com/software/jira) +Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](https://docs.gitlab.com/ee/project_services/jira.html) to interact with JIRA Core either using an on-premises instance or the SaaS solution that Atlassian offers. + +### JUnit + +A testing framework for the Java programming language, [JUnit](http://junit.org/junit4/) has been important in the evolution of test-driven development. ### Kerberos -A network authentication protocol that uses secret-key cryptography for security. +A network authentication [protocol](http://web.mit.edu/kerberos/) that uses secret-key cryptography for security. ### Kubernetes @@ -216,23 +265,27 @@ An open source container cluster manager originally designed by Google. It's bas ### Labels -An identifier to describe a group of one or more specific file revisions +An [identifier](https://docs.gitlab.com/ce/user/project/labels.html) to describe a group of one or more specific file revisions. -### LDAP +### Lightweight Directory Access Protocol (LDAP) -Lightweight Directory Access Protocol - basically its a directory (electronic address book) with user information e.g. name, phone_number etc + A directory (electronic address book) with user information (e.g. name, phone_number etc.) ### LDAP User Authentication -Allowing GitLab to sign in people from an LDAP server i.e. Allow people whose names are on the electronic user directory server) to be able to use their LDAP accounts to login. +GitLab [integrates](https://docs.gitlab.com/ce/administration/auth/ldap.html) with LDAP to support user authentication. This enables GitLab to sign in people from an LDAP server (i.e., allowing people whose names are on the electronic user directory server to be able to use their LDAP accounts to login.) ### LDAP Group Sync Allows you to synchronize the members of a GitLab group with one or more LDAP groups. -### Git LFS +### Load Balancer -Git Large File Storage. A way to enable git to handle large binary files by using reference pointers within small text files to point to the large files. +A [device](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that distributes network or application traffic across multiple servers. + +### Git Large File Storage (LFS) + +A way [to enable](https://about.gitlab.com/2015/11/23/announcing-git-lfs-support-in-gitlab/) git to handle large binary files by using reference pointers within small text files to point to the large files. Large files such as high resolution images and videos, audio files, and assets can be called from a remote server. ### Linux @@ -240,8 +293,7 @@ An operating system like Windows or OS X. It is mostly used by software develope ### Markdown -Is a lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. -Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. +A lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. Checkout GitLab's [Markdown guide](https://gitlab.com/help/user/markdown.md). ### Maria DB @@ -249,193 +301,211 @@ A community developed fork/variation of MySQL. MySQL is owned by Oracle. ### Master -Name of the default branch in every git repository. +Name of the [default branch](https://git-scm.com/book/en/v1/Git-Branching-What-a-Branch-Is) in every git repository. + +### Mattermost + +An open source, self-hosted messaging alternative to Slack. View GitLab's Mattermost [feature](https://gitlab.com/gitlab-org/gitlab-mattermost). ### Mercurial -A free distributed version control system like Git. Think of it as a competitor to Git. +A free distributed version control system similar to and a competitor with Git. ### Merge -Takes changes from one branch, and applies them into another branch. +Takes changes from one branch, and [applies them](https://git-scm.com/docs/git-merge) into another branch. + +### Merge Conflict + +[Arises](https://about.gitlab.com/2016/09/06/resolving-merge-conflicts-from-the-gitlab-ui/) when a merge can't be performed cleanly between two versions of the same file. ### Meteor -A hip platform for building javascript apps.[Meteor] (https://www.meteor.com) +A [platform](https://www.meteor.com) for building javascript apps. ### Milestones -Allows you to track the progress on issues, and merge requests, which allows you to get a snapshot of the progress made. +Allow you to [organize issues](https://docs.gitlab.com/ce/workflow/milestones.html) and merge requests in GitLab into a cohesive group, optionally setting a due date. A common use is keeping track of an upcoming software version. Milestones are created per-project. ### Mirror Repositories -You can set up a project to automatically have its branches, tags, and commits updated from an upstream repository. This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and its activity using the familiar GitLab interface. +A project that is setup to automatically have its branches, tags, and commits [updated from an upstream repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html). This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and activity using the familiar GitLab interface. ### MIT License -A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this license. -This means, you can download the code, modify it as you want even build a new commercial product using the underlying code and its not illegal. The only condition is that there is no form of waranty provided by GitLab so whatever happens if you use the code is your own problem. +A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this [license](https://docs.gitlab.com/ce/development/licensing.html). This means you can download the code, modify it as you want, and even build a new commercial product using the underlying code and it's not illegal. The only condition is that there is no form of warranty provided by GitLab so whatever happens when you use the code is your own problem. -### Mondo +### Mondo Rescue -*** Needs definition here +A free disaster recovery [software](https://help.ubuntu.com/community/MondoMindi). -### Multi LDAP Server +### MySQL -*** Needs definition here - -### My SQL - -A relational database. Currently only supported if you are using EE. It is owned by Oracle. +A relational [database](http://www.mysql.com/) owned by Oracle. Currently only supported if you are using EE. ### Namespace -In computing, a namespace is a set of symbols that are used to organize objects of various kinds, so that these objects may be referred to by name. - -Prominent examples include: -- file systems are namespaces that assign names to files; -- programming languages organize their variables and subroutines in namespaces; -- computer networks and distributed systems assign names to resources, such as computers, printers, websites, (remote) files, etc. +A set of symbols that are used to organize objects of various kinds so that these objects may be referred to by name. Examples of namespaces in action include file systems that assign names to files; programming languages that organize their variables and subroutines in namespaces; and computer networks and distributed systems that assign names to resources, such as computers, printers, websites, (remote) files, etc. ### Nginx -(pronounced "engine x") is a web server. It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache. +A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache. -### oAuth +### OAuth -Is an open standard for authorization, commonly used as a way for Internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. +An open [standard](https://oauth.net/) for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. ### Omnibus Packages -Omnibus is a way to package the different services and tools required to run GitLab, so that users can install it without as much work. +A way to [package different services and tools](https://docs.gitlab.com/omnibus/) required to run GitLab, so that most developers can install it without laborious configuration. ### On Premise -On your own server. In GitLab, this refers to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com which is hosted by GitLab Inc's servers. +On your own server. In GitLab, this [refers](https://about.gitlab.com/2015/02/12/why-ship-on-premises-in-the-saas-era/) to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com, which is hosted by GitLab Inc's servers. + +### Open Core + +GitLab's [business model](https://about.gitlab.com/2016/07/20/gitlab-is-open-core-github-is-closed-source/). Coined by Andrew Lampitt in 2008, the [open core model](https://en.wikipedia.org/wiki/Open_core) primarily involves offering a "core" or feature-limited version of a software product as free and open-source software, while offering "commercial" versions or add-ons as proprietary software. ### Open Source Software -Software for which the original source code is freely available and may be redistributed and modified. +Software for which the original source code is freely [available](https://opensource.org/docs/osd) and may be redistributed and modified. GitLab prioritizes open source [stewardship](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/). ### Owner -This is the most powerful person on a GitLab project. He has the permissions of all the other users plus the additional permission of being able to destroy i.e. delete the project +The most powerful person on a GitLab project. They have the permissions of all the other users plus the additional permission of being able to destroy (i.e. delete) the project. -### PaaS +### Platform as a Service (PaaS) -Typically referred to in regards to application development, it is a model in which a cloud provider delivers hardware and software tools to its users as a service +Typically referred to in regards to application development, PaaS is a model in which a cloud provider delivers hardware and software tools to its users as a service. ### Perforce -The company that produces Helix. A commercial, proprietary, centralised VCS well known for it's ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo +The company that produces Helix. A commercial, proprietary, centralised VCS well known for its ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo. ### Phabricator -Is a suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion. +A suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion. ### Piwik Analytics -An open source analytics software to help you analyze web traffic. It is similar to google analytics only that google analytics is not open source and information is stored by google while in Piwik the information is stored in your own server hence fully private. +An open source analytics software to help you analyze web traffic. It is similar to Google Analytics, except that the latter is not open source and information is stored by Google. In Piwik, the information is stored on your own server and hence is fully private. ### Plus Subscription -GitLab Premium EE subscription that includes training and dedicated Account Management and Service Engineer and complete support package [Plus subscription](https://about.gitlab.com/pricing/) +GitLab Premium EE [subscription](https://about.gitlab.com/pricing/) that includes training and dedicated Account Management and Service Engineer and complete support package. ### PostgreSQL -A relational database. Touted as the most advanced open source database. +An [object-relational](https://en.wikipedia.org/wiki/PostgreSQL) database. Touted as the most advanced open source database, it is one of two database management systems [supported by](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/database.md) GitLab, the other being MySQL. ### Protected Branches -A feature that protects branches from unauthorized pushes, force pushing or deletion. +A [feature](https://docs.gitlab.com/ce/user/project/protected_branches.html) that protects branches from unauthorized pushes, force pushing or deletion. ### Pull -Git command to synchronize the local repository with the remote repository, by fetching all remote changes and merging them into the local repository. +Git command to [synchronize](https://git-scm.com/docs/git-pull) the local repository with the remote repository, by fetching all remote changes and merging them into the local repository. ### Puppet -A popular devops automation tool +A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works). ### Push -Git command to send commits from the local repository to the remote repository. +Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. ### RE Read Only -Permissions to see a file and it's contents, but not change it +Permissions to see a file and its contents, but not change it. ### Rebase -Moves a branch from one commit to another. This allows you to re-write your project's history. +In addition to the merge, the [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) is a main way to integrate changes from one branch into another. -### Git Repository +### (Git) Repository -Storage location of all files which are tracked by git. +A directory where Git [has been initiatlized](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository) to start version controlling your files. The history of your work is stored here. A remote repository is not on your machine, but usually online (like on GitLab.com, for instance). The main remote repository is usually called "Origin." ### Requirements management -*** Needs definition here - -### Revision - -*** Needs definition here +Gives your distributed teams a single shared repository to collaborate and share requirements, understand their relationship to tests, and evaluate linked defects. It includes multiple, preconfigured requirement types. ### Revision Control -Also known as version control or source control, is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number", "revision level", or simply "revision". +Also known as version control or source control, this is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number," "revision level," or simply "revision." ### RocketChat -An open source chat application for teams. Very similar to Slack only that is is open-source. +An open source chat application for teams, RocketChat is very similar to Slack but it is also open-source. + +### Route Table + +A route table contains rules (called routes) that determine where network traffic is directed. Each [subnet in a VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Route_Tables.html) must be associated with a route table. ### Runners -Actual build machines/containers that run/execute tests you have specified to be run on GitLab CI +Actual build machines/containers that [run and execute tests](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner) you have specified to be run on GitLab CI. -### SaaS +### Software as a service (SaaS) -Software as a service. Software is hosted centrally and accessed on-demand i.e. when you want to. This refers to GitLab.com in our scenario +Software that is hosted centrally and accessed on-demand (i.e. whenever you want to). This applies to GitLab.com. -### SCM +### Software Configuration Management (SCM) -Software Configuration Management. Often used by people when they mean Version Control +This term is often used by people when they mean "Version Control." ## Scrum -An Agile framework designed to help complete complex (typically) software projects. It's made up of several parts: product requirments backlog, sprint plannnig, sprint (development), sprint review, retrospec (analyzing the sprint). The goal is to end up with potentially shippable products. +An Agile [framework](https://www.scrum.org/Resources/What-is-Scrum) designed to typically help complete complex software projects. It's made up of several parts: product requirements backlog, sprint planning, sprint (development), sprint review, and retrospec (analyzing the sprint). The goal is to end up with potentially shippable products. ### Scrum Board The board used to track the status and progress of each of the sprint backlog items. +### Shell + +[Terminal](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. + +### Single-tenant + +The tenant purchases their own copy of the software and the software can be customized to meet the specific and needs of that customer. [GitHost.io](https://about.gitlab.com/handbook/positioning-faq/) is our provider of single-tenant 'managed cloud' GitLab instances. + ### Slack -Real time messaging app for teams. Used internally by GitLab +Real time messaging app for teams that is used internally by GitLab team members. GitLab users can enable [Slack integration](https://docs.gitlab.com/ce/project_services/slack.html) to trigger push, issue, and merge request events among others. ### Slave Servers -Also known as secondary servers. They help to spread the load over multiple machines, they also provide backups when the master/primary server crashes. +Also known as secondary servers, these help to spread the load over multiple machines. They also provide backups when the master/primary server crashes. ### Source Code -Program code as typed by a computer programmer. i.e. it has not yet been compiled/translated by the computer to machine language. +Program code as typed by a computer programmer (i.e. it has not yet been compiled/translated by the computer to machine language). ### SSH Key -A unique identifier of a computer. It is used to identify computers without the need for a password. e.g. On GitLab I have added the ssh key of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added. +A unique identifier of a computer. It is used to identify computers without the need for a password (e.g., On GitLab I have [added the ssh key](https://docs.gitlab.com/ce/gitlab-basics/create-your-ssh-keys.html) of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added.) -### SSO +### Single Sign On (SSO) -Single Sign On. An authentication process that allows you enter one username and password to access multiple applications. +An authentication process that allows you enter one username and password to access multiple applications. + +### Staging Area + +[Staging occurs](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics) before the commit process in git. The staging area is a file, generally contained in your Git directory, that stores information about what will go into your next commit. It’s sometimes referred to as the “index."" ### Standard Subscription -Our mid range EE subscription that includes 24/7 support, support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/) +Our mid range EE subscription that includes 24/7 support and support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/). ### Stash -Atlassian's Git On-Premises solution. Think of it as Atlassian's GitLab EE. It is now known as BitBucket Server. +Atlassian's Git on-premise solution. Think of it as Atlassian's GitLab EE, now known as BitBucket Server. + +### Static Site Generators (SSGs) + +A [software](https://wiki.python.org/moin/StaticSiteGenerator) that takes some text and templates as input and produces html files on the output. ### Subversion @@ -443,40 +513,63 @@ Non-proprietary, centralized version control system. ### Sudo -A program that allows you to perform superuser/administrator actions on Unix Operating Systems e.g. Linux, OS X. It actually stands for 'superuser do' +A program that allows you to perform superuser/administrator actions on Unix Operating Systems (e.g., Linux, OS X.) It actually stands for 'superuser do.' -### SVN +### Subversion (SVN) -Abbreviation for Subversion. +An open source version control system. ### Tag -Represents a version of a particular branch at a moment in time. +[Represents](https://docs.gitlab.com/ce/api/tags.html) a version of a particular branch at a moment in time. ### Tool Stack -Set of tools used in a process to achieve a common outcome. E.g. set of tools used in Application Lifecycle Management. +The set of tools used in a process to achieve a common outcome (e.g. set of tools used in Application Lifecycle Management). ### Trac -An Open Source project management and bug tracking web application. +An open source project management and bug tracking web [application](https://trac.edgewall.org/). + +### Untracked files + +New files that Git has not [been told](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) to track previously. ### User Anyone interacting with the software. -### VCS +### Version Control Software (VCS) -Version Control Software +Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. VCS [has evolved](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.gd69537a19_0_32) from local version control systems, to centralized version control systems, to the present distributed version control systems like Git, Mercurial, Bazaar, and Darcs. + +### Virtual Private Cloud (VPC) + +An on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [setup High Availability](https://docs.gitlab.com/ce/university/high-availability/aws/). + +### Virtual private server (VPS) + +A [virtual machine](https://en.wikipedia.org/wiki/Virtual_private_server) sold as a service by an Internet hosting service. A VPS runs its own copy of an operating system, and customers have superuser-level access to that operating system instance, so they can install almost any software that runs on that OS. + +### VM Instance ### Waterfall -A model of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the COMPLETE software to the customer that meets all the requirements specified by the customer +A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified. ### Webhooks -A way for for an app to provide other applications with real-time information. e.g. send a message to a slack channel when a commit is pushed +A way for for an app to [provide](https://docs.gitlab.com/ce/web_hooks/web_hooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) ### Wiki -A website/system that allows for collaborative editing of its content by the users. In programming, they usually contain documentation of how to use the software +A [website/system](http://www.wiki.com/) that allows for collaborative editing of its content by the users. In programming, wikis usually contain documentation of how to use the software. + +### Working Tree + +[Consists of files](http://stackoverflow.com/questions/3689838/difference-between-head-working-tree-index-in-git) that you are currently working on. + +### YAML + +A human-readable data serialization [language](http://www.yaml.org/about.html) that takes concepts from programming languages such as C, Perl, and Python, and ideas from XML and the data format of electronic mail. + From e6f515ecbed85b204e8f7bc9934b0bf208a0ea4c Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 16:53:12 +0100 Subject: [PATCH 022/176] Revert "Add #closed_without_source_project?" This reverts commit 31c37c6c38258684fc92e0d91119c33872e39034. See #23341 --- app/models/merge_request.rb | 10 ++---- .../projects/merge_requests/_show.html.haml | 31 ++++++++---------- spec/models/merge_request_spec.rb | 32 ------------------- 3 files changed, 16 insertions(+), 57 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 0cc0b3c2a0e..d32bc9f882f 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -333,11 +333,7 @@ class MergeRequest < ActiveRecord::Base end def closed_without_fork? - closed? && forked_source_project_missing? - end - - def closed_without_source_project? - closed? && !source_project + closed? && (forked_source_project_missing? || !source_project) end def forked_source_project_missing? @@ -348,9 +344,7 @@ class MergeRequest < ActiveRecord::Base end def reopenable? - return false if closed_without_fork? || closed_without_source_project? || merged? - - closed? + source_branch_exists? && closed? end def ensure_merge_request_diff diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index cd98aaf8d75..fe90383b00f 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -26,19 +26,17 @@ %ul.dropdown-menu.dropdown-menu-align-right %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) - - unless @merge_request.closed_without_fork? - .normal - %span Request to merge - %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) - - if @merge_request.open? && @merge_request.diverged_from_target_branch? - %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) + .normal + %span Request to merge + %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) + - if @merge_request.open? && @merge_request.diverged_from_target_branch? + %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) - - unless @merge_request.closed_without_source_project? - = render "projects/merge_requests/show/how_to_merge" - = render "projects/merge_requests/widget/show.html.haml" + = render "projects/merge_requests/show/how_to_merge" + = render "projects/merge_requests/widget/show.html.haml" - if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user) .light.prepend-top-default.append-bottom-default @@ -52,11 +50,10 @@ = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do Discussion %span.badge= @merge_request.mr_and_commit_notes.user.count - - unless @merge_request.closed_without_source_project? - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do - Commits - %span.badge= @commits_count + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do + Commits + %span.badge= @commits_count - if @pipeline %li.pipelines-tab = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 6db5e7f7d80..ee003a9d18f 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1274,38 +1274,6 @@ describe MergeRequest, models: true do end end - describe '#closed_without_source_project?' do - let(:project) { create(:project) } - let(:user) { create(:user) } - let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) } - let(:destroy_service) { Projects::DestroyService.new(fork_project, user) } - - context 'when the merge request is closed' do - let(:closed_merge_request) do - create(:closed_merge_request, - source_project: fork_project, - target_project: project) - end - - it 'returns false if the source project exists' do - expect(closed_merge_request.closed_without_source_project?).to be_falsey - end - - it 'returns true if the source project does not exist' do - destroy_service.execute - closed_merge_request.reload - - expect(closed_merge_request.closed_without_source_project?).to be_truthy - end - end - - context 'when the merge request is open' do - it 'returns false' do - expect(subject.closed_without_source_project?).to be_falsey - end - end - end - describe '#reopenable?' do context 'when the merge request is closed' do it 'returns true' do From a4ef4244d4b9b54246f53bba5c02746542034da5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 20 Oct 2016 16:16:57 +0800 Subject: [PATCH 023/176] Preserve note_type and position for notes from emails Closes #23208 --- .../email/handler/create_note_handler.rb | 4 +++- spec/fixtures/emails/commands_in_reply.eml | 2 -- spec/fixtures/emails/commands_only_reply.eml | 2 -- .../email/handler/create_note_handler_spec.rb | 24 ++++++++++--------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb index 06dae31cc27..447c7a6a6b9 100644 --- a/lib/gitlab/email/handler/create_note_handler.rb +++ b/lib/gitlab/email/handler/create_note_handler.rb @@ -46,7 +46,9 @@ module Gitlab noteable_type: sent_notification.noteable_type, noteable_id: sent_notification.noteable_id, commit_id: sent_notification.commit_id, - line_code: sent_notification.line_code + line_code: sent_notification.line_code, + position: sent_notification.position, + type: sent_notification.note_type ).execute end end diff --git a/spec/fixtures/emails/commands_in_reply.eml b/spec/fixtures/emails/commands_in_reply.eml index 06bf60ab734..712f6f797b4 100644 --- a/spec/fixtures/emails/commands_in_reply.eml +++ b/spec/fixtures/emails/commands_in_reply.eml @@ -23,8 +23,6 @@ Cool! /close /todo -/due tomorrow - On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta wrote: diff --git a/spec/fixtures/emails/commands_only_reply.eml b/spec/fixtures/emails/commands_only_reply.eml index aed64224b06..2d2e2f94290 100644 --- a/spec/fixtures/emails/commands_only_reply.eml +++ b/spec/fixtures/emails/commands_only_reply.eml @@ -21,8 +21,6 @@ X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1 /close /todo -/due tomorrow - On Sun, Jun 9, 2013 at 1:39 PM, eviltrout via Discourse Meta wrote: diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb index 4909fed6b77..48660d1dd1b 100644 --- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb @@ -12,10 +12,13 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do let(:email_raw) { fixture_file('emails/valid_reply.eml') } let(:project) { create(:project, :public) } - let(:noteable) { create(:issue, project: project) } let(:user) { create(:user) } + let(:note) { create(:diff_note_on_merge_request, project: project) } + let(:noteable) { note.noteable } - let!(:sent_notification) { SentNotification.record(noteable, user.id, mail_key) } + let!(:sent_notification) do + SentNotification.record_note(note, user.id, mail_key) + end context "when the recipient address doesn't include a mail key" do let(:email_raw) { fixture_file('emails/valid_reply.eml').gsub(mail_key, "") } @@ -82,7 +85,6 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do expect { receiver.execute }.to change { noteable.notes.count }.by(1) expect(noteable.reload).to be_closed - expect(noteable.due_date).to eq(Date.tomorrow) expect(TodoService.new.todo_exist?(noteable, user)).to be_truthy end end @@ -100,7 +102,6 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do expect { receiver.execute }.to change { noteable.notes.count }.by(1) expect(noteable.reload).to be_open - expect(noteable.due_date).to be_nil expect(TodoService.new.todo_exist?(noteable, user)).to be_falsy end end @@ -117,7 +118,6 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do expect { receiver.execute }.to change { noteable.notes.count }.by(2) expect(noteable.reload).to be_closed - expect(noteable.due_date).to eq(Date.tomorrow) expect(TodoService.new.todo_exist?(noteable, user)).to be_truthy end end @@ -138,10 +138,11 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do it "creates a comment" do expect { receiver.execute }.to change { noteable.notes.count }.by(1) - note = noteable.notes.last + new_note = noteable.notes.last - expect(note.author).to eq(sent_notification.recipient) - expect(note.note).to include("I could not disagree more.") + expect(new_note.author).to eq(sent_notification.recipient) + expect(new_note.position).to eq(note.position) + expect(new_note.note).to include("I could not disagree more.") end it "adds all attachments" do @@ -160,10 +161,11 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do shared_examples 'an email that contains a mail key' do |header| it "fetches the mail key from the #{header} header and creates a comment" do expect { receiver.execute }.to change { noteable.notes.count }.by(1) - note = noteable.notes.last + new_note = noteable.notes.last - expect(note.author).to eq(sent_notification.recipient) - expect(note.note).to include('I could not disagree more.') + expect(new_note.author).to eq(sent_notification.recipient) + expect(new_note.position).to eq(note.position) + expect(new_note.note).to include('I could not disagree more.') end end From 80ca78fa47068d71e866b58d546d0cd5059d96d9 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 20 Oct 2016 16:24:37 +0800 Subject: [PATCH 024/176] Add CHANGELOG entry [ci skip] --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b018fc0d57..d6e6690ced1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) + - Fix discussion thread from emails for merge requests. !7010 + ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) From 5f0b7fe429d75de2dbcfef142d2389bf99d199ec Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Mon, 17 Oct 2016 11:44:25 +0200 Subject: [PATCH 025/176] Stop injecting field errors where they won't be used. --- app/assets/javascripts/gl_field_errors.js.es6 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/gl_field_errors.js.es6 b/app/assets/javascripts/gl_field_errors.js.es6 index 8657e7b4abf..8e8f9f29ab3 100644 --- a/app/assets/javascripts/gl_field_errors.js.es6 +++ b/app/assets/javascripts/gl_field_errors.js.es6 @@ -137,8 +137,11 @@ } initValidators () { - // select all non-hidden inputs in form - this.state.inputs = this.form.find(':input:not([type=hidden])').toArray() + // register selectors here as needed + const validateSelectors = [':text', ':password', '[type=email]'] + .map((selector) => `input${selector}`).join(','); + + this.state.inputs = this.form.find(validateSelectors).toArray() .filter((input) => !input.classList.contains(customValidationFlag)) .map((input) => new GlFieldError({ input, formErrors: this })); From fd2c3a3da0302a474d7c1adbd409aedea2a41053 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 19 Oct 2016 19:43:04 +0300 Subject: [PATCH 026/176] Refactoring find_commits functionality --- app/controllers/projects/commits_controller.rb | 2 +- app/models/repository.rb | 9 ++++++--- lib/gitlab/project_search_results.rb | 6 +----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index a52c614b259..c2e7bf1ffec 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -13,7 +13,7 @@ class Projects::CommitsController < Projects::ApplicationController @commits = if search.present? - @repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact + @repository.find_commits_by_message(search, @ref, @path, @limit, @offset) else @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) end diff --git a/app/models/repository.rb b/app/models/repository.rb index 72e473871fa..1b7f20a2134 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -109,6 +109,10 @@ class Repository end def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0) + unless exists? && has_visible_content? && query.present? + return [] + end + ref ||= root_ref args = %W( @@ -117,9 +121,8 @@ class Repository ) args = args.concat(%W(-- #{path})) if path.present? - git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) - commits = git_log_results.map { |c| commit(c) } - commits + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines + git_log_results.map { |c| commit(c.chomp) }.compact end def find_branch(name, fresh_repo: true) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 5b9cfaeb2f8..24733435a5a 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -73,11 +73,7 @@ module Gitlab end def commits - if project.empty_repo? || query.blank? - [] - else - project.repository.find_commits_by_message(query).compact - end + project.repository.find_commits_by_message(query) end def project_ids_relation From c9108442b5ac33514801c38412bed5e7b13677b8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 19 Oct 2016 14:48:37 +0200 Subject: [PATCH 027/176] Update duration at the end of pipeline --- CHANGELOG.md | 1 + app/models/ci/pipeline.rb | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfde2cc81e5..cb43cb4b307 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Add `/projects/visible` API endpoint (Ben Boeckel) - Fix centering of custom header logos (Ashley Dumaine) - Keep around commits only pipeline creation as pipeline data doesn't change over time + - Update duration at the end of pipeline - ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup - Add group level labels. (!6425) - Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index e84c91b417d..d5c1e03b461 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -59,9 +59,6 @@ module Ci before_transition any => [:success, :failed, :canceled] do |pipeline| pipeline.finished_at = Time.now - end - - before_transition do |pipeline| pipeline.update_duration end From d9d0b81bc6117f9c01c28c1fab597f556b56f369 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 20 Oct 2016 12:10:27 +0200 Subject: [PATCH 028/176] Make label API spec independent of order --- spec/requests/api/labels_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 1da9988978b..867bc615b97 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -22,8 +22,7 @@ describe API::API, api: true do expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.size).to eq(2) - expect(json_response.first['name']).to eq(group_label.name) - expect(json_response.second['name']).to eq(label1.name) + expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, label1.name]) end end From 50288a6ace0e75aa3e6c09c534f0be9800c95836 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 20 Oct 2016 10:27:30 +0100 Subject: [PATCH 029/176] Removed code from project members controller This code was meant to be added to another branch as an expirement, but instead was commited to wrong branch --- .../projects/project_members_controller.rb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 37a86ed0523..2a07d154853 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -32,21 +32,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController current_user: current_user ) - if params[:group_ids].present? - group_ids = params[:group_ids].split(',') - groups = Group.where(id: group_ids) - - groups.each do |group| - next unless can?(current_user, :read_group, group) - - project.project_group_links.create( - group: group, - group_access: params[:access_level], - expires_at: params[:expires_at] - ) - end - end - redirect_to namespace_project_project_members_path(@project.namespace, @project) end From 06564f9e049417087fa53cf8ec15c22ec65724d5 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Thu, 20 Oct 2016 12:47:32 +0200 Subject: [PATCH 030/176] Update gl_field_error tests for better input filtering. --- spec/javascripts/gl_field_errors_spec.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/gl_field_errors_spec.js.es6 b/spec/javascripts/gl_field_errors_spec.js.es6 index 36feb2b2aa5..da9259edd78 100644 --- a/spec/javascripts/gl_field_errors_spec.js.es6 +++ b/spec/javascripts/gl_field_errors_spec.js.es6 @@ -11,12 +11,12 @@ this.fieldErrors = new global.GlFieldErrors($form); }); - it('should properly initialize the form', function() { + it('should select the correct input elements', function() { expect(this.$form).toBeDefined(); expect(this.$form.length).toBe(1); expect(this.fieldErrors).toBeDefined(); const inputs = this.fieldErrors.state.inputs; - expect(inputs.length).toBe(5); + expect(inputs.length).toBe(4); }); it('should ignore elements with custom error handling', function() { From 493367108eef14c8517c6d023ec46267c1e706cf Mon Sep 17 00:00:00 2001 From: Paco Guzman Date: Wed, 19 Oct 2016 14:30:17 +0200 Subject: [PATCH 031/176] Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method --- CHANGELOG.md | 2 ++ app/models/issue.rb | 8 +++++++- spec/models/issue_spec.rb | 8 +++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b018fc0d57..6c0fd97b070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) + - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method + ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) diff --git a/app/models/issue.rb b/app/models/issue.rb index 133a5993815..89158a50353 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -211,7 +211,13 @@ class Issue < ActiveRecord::Base note.all_references(current_user, extractor: ext) end - ext.merge_requests.select { |mr| mr.open? && mr.closes_issue?(self) } + merge_requests = ext.merge_requests.select(&:open?) + if merge_requests.any? + ids = MergeRequestsClosingIssues.where(merge_request_id: merge_requests.map(&:id), issue_id: id).pluck(:merge_request_id) + merge_requests.select { |mr| mr.id.in?(ids) } + else + [] + end end def moved? diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 3b8b743af2d..60d30eb7418 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -100,11 +100,17 @@ describe Issue, models: true do end it 'returns the merge request to close this issue' do - allow(mr).to receive(:closes_issue?).with(issue).and_return(true) + mr expect(issue.closed_by_merge_requests).to eq([mr]) end + it "returns an empty array when the merge request is closed already" do + closed_mr + + expect(issue.closed_by_merge_requests).to eq([]) + end + it "returns an empty array when the current issue is closed already" do expect(closed_issue.closed_by_merge_requests).to eq([]) end From bc31a489dd8c329cf011ac898498735809c319dd Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 20 Oct 2016 12:59:39 +0200 Subject: [PATCH 032/176] Restrict ProjectCacheWorker jobs to one per 15 min This ensures ProjectCacheWorker jobs for a given project are performed at most once per 15 minutes. This should reduce disk load a bit in cases where there are multiple pushes happening (which should schedule multiple ProjectCacheWorker jobs). --- CHANGELOG.md | 3 +- app/workers/project_cache_worker.rb | 27 ++++++++++++++++ spec/workers/project_cache_worker_spec.rb | 38 +++++++++++++++++------ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 399a7f65267..ba5d72d3ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - - Simpler arguments passed to named_route on toggle_award_url helper method + - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) @@ -35,6 +35,7 @@ Please view this file on the master branch, on stable branches it's out of date. - AbstractReferenceFilter caches project_refs on RequestStore when active - Replaced the check sign to arrow in the show build view. !6501 - Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar) + - ProjectCacheWorker updates caches at most once per 15 minutes per project - Fix Error 500 when viewing old merge requests with bad diff data - Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar) - Speed-up group milestones show page diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb index ccefd0f71a0..0d524e88dc3 100644 --- a/app/workers/project_cache_worker.rb +++ b/app/workers/project_cache_worker.rb @@ -1,9 +1,30 @@ +# Worker for updating any project specific caches. +# +# This worker runs at most once every 15 minutes per project. This is to ensure +# that multiple instances of jobs for this worker don't hammer the underlying +# storage engine as much. class ProjectCacheWorker include Sidekiq::Worker sidekiq_options queue: :default + LEASE_TIMEOUT = 15.minutes.to_i + def perform(project_id) + if try_obtain_lease_for(project_id) + Rails.logger. + info("Obtained ProjectCacheWorker lease for project #{project_id}") + else + Rails.logger. + info("Could not obtain ProjectCacheWorker lease for project #{project_id}") + + return + end + + update_caches(project_id) + end + + def update_caches(project_id) project = Project.find(project_id) return unless project.repository.exists? @@ -15,4 +36,10 @@ class ProjectCacheWorker project.repository.build_cache end end + + def try_obtain_lease_for(project_id) + Gitlab::ExclusiveLease. + new("project_cache_worker:#{project_id}", timeout: LEASE_TIMEOUT). + try_obtain + end end diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index 5785a6a06ff..f5b60b90d11 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -6,21 +6,39 @@ describe ProjectCacheWorker do subject { described_class.new } describe '#perform' do - it 'updates project cache data' do - expect_any_instance_of(Repository).to receive(:size) - expect_any_instance_of(Repository).to receive(:commit_count) + context 'when an exclusive lease can be obtained' do + before do + allow(subject).to receive(:try_obtain_lease_for).with(project.id). + and_return(true) + end - expect_any_instance_of(Project).to receive(:update_repository_size) - expect_any_instance_of(Project).to receive(:update_commit_count) + it 'updates project cache data' do + expect_any_instance_of(Repository).to receive(:size) + expect_any_instance_of(Repository).to receive(:commit_count) - subject.perform(project.id) + expect_any_instance_of(Project).to receive(:update_repository_size) + expect_any_instance_of(Project).to receive(:update_commit_count) + + subject.perform(project.id) + end + + it 'handles missing repository data' do + expect_any_instance_of(Repository).to receive(:exists?).and_return(false) + expect_any_instance_of(Repository).not_to receive(:size) + + subject.perform(project.id) + end end - it 'handles missing repository data' do - expect_any_instance_of(Repository).to receive(:exists?).and_return(false) - expect_any_instance_of(Repository).not_to receive(:size) + context 'when an exclusive lease can not be obtained' do + it 'does nothing' do + allow(subject).to receive(:try_obtain_lease_for).with(project.id). + and_return(false) - subject.perform(project.id) + expect(subject).not_to receive(:update_caches) + + subject.perform(project.id) + end end end end From 374071321d0cfb7a161ec38e85e27e1d46ae7c9a Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 17:13:04 +0100 Subject: [PATCH 033/176] Fix the merge request view when source projects or branches are removed --- .../projects/merge_requests_controller.rb | 2 +- app/helpers/merge_requests_helper.rb | 10 +++++++--- app/models/merge_request.rb | 15 ++++++--------- app/views/projects/merge_requests/_show.html.haml | 13 ++++++++----- .../merge_requests/created_from_fork_spec.rb | 14 ++++++++++++++ 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 0f593d1a936..55ea31e48a0 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -554,7 +554,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController def define_pipelines_vars @pipelines = @merge_request.all_pipelines - if @pipelines.any? + if @pipelines.present? @pipeline = @pipelines.first @statuses = @pipeline.statuses.relevant end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 249cb44e9d5..a6659ea2fd1 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -86,11 +86,15 @@ module MergeRequestsHelper end def source_branch_with_namespace(merge_request) - branch = link_to(merge_request.source_branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch)) + namespace = merge_request.source_project_namespace + branch = merge_request.source_branch + + if merge_request.source_branch_exists? + namespace = link_to(namespace, project_path(merge_request.source_project)) + branch = link_to(branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch)) + end if merge_request.for_fork? - namespace = link_to(merge_request.source_project_namespace, - project_path(merge_request.source_project)) namespace + ":" + branch else branch diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d32bc9f882f..45fa8af60e5 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -333,7 +333,7 @@ class MergeRequest < ActiveRecord::Base end def closed_without_fork? - closed? && (forked_source_project_missing? || !source_project) + closed? && forked_source_project_missing? end def forked_source_project_missing? @@ -344,7 +344,7 @@ class MergeRequest < ActiveRecord::Base end def reopenable? - source_branch_exists? && closed? + closed? && !source_project_missing? && source_branch_exists? end def ensure_merge_request_diff @@ -656,7 +656,7 @@ class MergeRequest < ActiveRecord::Base end def has_ci? - source_project.ci_service && commits.any? + source_project.try(:ci_service) && commits.any? end def branch_missing? @@ -688,12 +688,9 @@ class MergeRequest < ActiveRecord::Base @environments ||= begin - environments = source_project.environments_for( - source_branch, diff_head_commit) - environments += target_project.environments_for( - target_branch, diff_head_commit, with_tags: true) - - environments.uniq + envs = target_project.environments_for(target_branch, diff_head_commit, with_tags: true) + envs.concat(source_project.environments_for(source_branch, diff_head_commit)) if source_project + envs.uniq end end diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index fe90383b00f..0e19d224fcd 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -35,7 +35,9 @@ - if @merge_request.open? && @merge_request.diverged_from_target_branch? %span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind) - = render "projects/merge_requests/show/how_to_merge" + - if @merge_request.source_branch_exists? + = render "projects/merge_requests/show/how_to_merge" + = render "projects/merge_requests/widget/show.html.haml" - if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user) @@ -50,10 +52,11 @@ = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do Discussion %span.badge= @merge_request.mr_and_commit_notes.user.count - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do - Commits - %span.badge= @commits_count + - if @merge_request.source_project + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do + Commits + %span.badge= @commits_count - if @pipeline %li.pipelines-tab = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb index a506624b30d..cfc1244429f 100644 --- a/spec/features/merge_requests/created_from_fork_spec.rb +++ b/spec/features/merge_requests/created_from_fork_spec.rb @@ -25,6 +25,20 @@ feature 'Merge request created from fork' do expect(page).to have_content 'Test merge request' end + context 'source project is deleted' do + background do + MergeRequests::MergeService.new(project, user).execute(merge_request) + fork_project.destroy! + end + + scenario 'user can access merge request' do + visit_merge_request(merge_request) + + expect(page).to have_content 'Test merge request' + expect(page).to have_content "(removed):#{merge_request.source_branch}" + end + end + context 'pipeline present in source project' do include WaitForAjax From 6f846fcbe8fa129e240ae58c8d4bbb1e1a82bbef Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 17:48:30 +0100 Subject: [PATCH 034/176] Fix two CI endpoints for MRs where the source project is deleted --- app/controllers/projects/merge_requests_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 55ea31e48a0..2ee53f7ceda 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -398,7 +398,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController status ||= "preparing" else - ci_service = @merge_request.source_project.ci_service + ci_service = @merge_request.source_project.try(:ci_service) status = ci_service.commit_status(merge_request.diff_head_sha, merge_request.source_branch) if ci_service if ci_service.respond_to?(:commit_coverage) From 61536ed2cff454bb2e3db8b7ca9ee09cc407461f Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 19:33:51 +0100 Subject: [PATCH 035/176] Rename forked_source_project_missing? to source_project_missing? --- app/models/merge_request.rb | 6 +++--- spec/models/merge_request_spec.rb | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 45fa8af60e5..c476a3bb14e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -326,17 +326,17 @@ class MergeRequest < ActiveRecord::Base def validate_fork return true unless target_project && source_project return true if target_project == source_project - return true unless forked_source_project_missing? + return true unless source_project_missing? errors.add :validate_fork, 'Source project is not a fork of the target project' end def closed_without_fork? - closed? && forked_source_project_missing? + closed? && source_project_missing? end - def forked_source_project_missing? + def source_project_missing? return false unless for_fork? return true unless source_project diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index ee003a9d18f..6e5137602aa 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1198,7 +1198,7 @@ describe MergeRequest, models: true do end end - describe "#forked_source_project_missing?" do + describe "#source_project_missing?" do let(:project) { create(:project) } let(:fork_project) { create(:project, forked_from_project: project) } let(:user) { create(:user) } @@ -1211,13 +1211,13 @@ describe MergeRequest, models: true do target_project: project) end - it { expect(merge_request.forked_source_project_missing?).to be_falsey } + it { expect(merge_request.source_project_missing?).to be_falsey } end context "when the source project is the same as the target project" do let(:merge_request) { create(:merge_request, source_project: project) } - it { expect(merge_request.forked_source_project_missing?).to be_falsey } + it { expect(merge_request.source_project_missing?).to be_falsey } end context "when the fork does not exist" do @@ -1231,7 +1231,7 @@ describe MergeRequest, models: true do unlink_project.execute merge_request.reload - expect(merge_request.forked_source_project_missing?).to be_truthy + expect(merge_request.source_project_missing?).to be_truthy end end end From 0bbfdde64d3de61df1f87e2394c0f459512c7f51 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 19 Oct 2016 18:09:33 +0100 Subject: [PATCH 036/176] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b018fc0d57..edaa1432757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Add a /wip slash command to toggle the Work In Progress status of a merge request. !6259 (tbalthazar) - Fix Error 500 when viewing old merge requests with bad diff data - Create a new /templates namespace for the /licenses, /gitignores and /gitlab_ci_ymls API endpoints. !5717 (tbalthazar) + - Fix viewing merged MRs when the source project has been removed !6991 - Speed-up group milestones show page - Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps) - Extract project#update_merge_requests and SystemHooks to its own worker from GitPushService From 8815748feb0c9bbef9b04f07f09645ba94ea4d63 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 13 Oct 2016 13:58:31 -0500 Subject: [PATCH 037/176] Change input order on Sign In form for better tabbing. This *unreverts* 8751491b, which was mistakenly reverted in !6328. It also changes the implementation of the original commit to work with the new login styling and markup. cc: @ClemMakesApps --- app/assets/stylesheets/pages/login.scss | 17 +++++++++++++++++ app/views/devise/sessions/_new_base.html.haml | 4 ++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index e6d9be5185d..bdb13bee178 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -53,6 +53,7 @@ margin: 0 0 10px; } + .login-footer { margin-top: 10px; @@ -246,3 +247,19 @@ padding: 65px; // height of footer + bottom padding of email confirmation link } } + +// For sign in pane only, to improve tab order, the following removes the submit button from +// normal document flow and pins it to the bottom of the form. For context, see !6867 & !6928 + +.login-box { + .new_user { + position: relative; + padding-bottom: 35px; + } + + .move-submit-down { + position: absolute; + width: 100%; + bottom: 0; + } +} diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index a96b579c593..525e7d99d71 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -5,6 +5,8 @@ %div.form-group = f.label :password = f.password_field :password, class: "form-control bottom", required: true, title: "This field is required." + %div.submit-container.move-submit-down + = f.submit "Sign in", class: "btn btn-save" - if devise_mapping.rememberable? .remember-me.checkbox %label{for: "user_remember_me"} @@ -12,5 +14,3 @@ %span Remember me .pull-right = link_to "Forgot your password?", new_password_path(resource_name) - %div.submit-container - = f.submit "Sign in", class: "btn btn-save" From c1212beaa495b5b0c3b805bb1b2a7bdc6d1a941b Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 19 Oct 2016 14:49:09 +0200 Subject: [PATCH 038/176] Use deployment IID when saving refs --- app/models/deployment.rb | 2 +- app/models/environment.rb | 4 ++-- spec/controllers/projects/merge_requests_controller_spec.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 1f8c5fb3d85..c843903877b 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -102,6 +102,6 @@ class Deployment < ActiveRecord::Base private def ref_path - File.join(environment.ref_path, 'deployments', id.to_s) + File.join(environment.ref_path, 'deployments', iid.to_s) end end diff --git a/app/models/environment.rb b/app/models/environment.rb index d575f1dc73a..73f415c0ef0 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -71,8 +71,8 @@ class Environment < ActiveRecord::Base return nil unless ref - deployment_id = ref.split('/').last - deployments.find(deployment_id) + deployment_iid = ref.split('/').last + deployments.find_by(iid: deployment_iid) end def ref_path diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index d6980471ea4..940d54f8686 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -913,7 +913,7 @@ describe Projects::MergeRequestsController do end describe 'GET ci_environments_status' do - context 'when the environment is from a forked project' do + context 'the environment is from a forked project' do let!(:forked) { create(:project) } let!(:environment) { create(:environment, project: forked) } let!(:deployment) { create(:deployment, environment: environment, sha: forked.commit.id, ref: 'master') } From 55365d368a069d6598f58d71d8891d6c8f06ea66 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 20 Oct 2016 14:21:40 +0200 Subject: [PATCH 039/176] Only create refs for new deployments This patch makes sure GitLab does not save the refs to the filesystem each time the deployment is updated. This will save some IO although I expect the impact to be minimal. --- app/models/deployment.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/deployment.rb b/app/models/deployment.rb index c843903877b..91d85c2279b 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -11,7 +11,7 @@ class Deployment < ActiveRecord::Base delegate :name, to: :environment, prefix: true - after_save :create_ref + after_create :create_ref def commit project.commit(sha) From b5d210c9d6d44fc664b006567b8f488e198ad04f Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Thu, 15 Sep 2016 18:45:22 +0300 Subject: [PATCH 040/176] Render hipchat notification descriptions as HTML instead of raw markdown --- .../project_services/hipchat_service.rb | 9 +++------ .../project_services/hipchat_service_spec.rb | 20 +++++++++---------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index afebd3b6a12..98f0312d84e 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -144,8 +144,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" if description - description = format_body(description) - message << description + message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) end message @@ -167,8 +166,7 @@ class HipchatService < Service "#{project_link}: #{title}" if description - description = format_body(description) - message << description + message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) end message @@ -219,8 +217,7 @@ class HipchatService < Service message << title if note - note = format_body(note) - message << note + message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(note)) end message diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 26dd95bdfec..90066f01f6f 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -117,7 +117,7 @@ describe HipchatService, models: true do end context 'issue events' do - let(:issue) { create(:issue, title: 'Awesome issue', description: 'please fix') } + let(:issue) { create(:issue, title: 'Awesome issue', description: '**please** fix') } let(:issue_service) { Issues::CreateService.new(project, user) } let(:issues_sample_data) { issue_service.hook_data(issue, 'open') } @@ -135,12 +135,12 @@ describe HipchatService, models: true do "issue ##{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome issue" \ - "
please fix
") + "

please fix

\n
") end end context 'merge request events' do - let(:merge_request) { create(:merge_request, description: 'please fix', title: 'Awesome merge request', target_project: project, source_project: project) } + let(:merge_request) { create(:merge_request, description: '**please** fix', title: 'Awesome merge request', target_project: project, source_project: project) } let(:merge_service) { MergeRequests::CreateService.new(project, user) } let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') } @@ -159,7 +159,7 @@ describe HipchatService, models: true do "merge request !#{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome merge request" \ - "
please fix
") + "

please fix

\n
") end end @@ -190,7 +190,7 @@ describe HipchatService, models: true do "commit #{commit_id} in " \ "#{project_name}: " \ "#{title}" \ - "
a comment on a commit
") + "

a comment on a commit

\n
") end end @@ -203,7 +203,7 @@ describe HipchatService, models: true do let(:merge_request_note) do create(:note_on_merge_request, noteable: merge_request, project: project, - note: "merge request note") + note: "merge request **note**") end it "calls Hipchat API for merge request comment events" do @@ -222,7 +222,7 @@ describe HipchatService, models: true do "merge request !#{merge_id} in " \ "#{project_name}: " \ "#{title}" \ - "
merge request note
") + "

merge request note

\n
") end end @@ -230,7 +230,7 @@ describe HipchatService, models: true do let(:issue) { create(:issue, project: project) } let(:issue_note) do create(:note_on_issue, noteable: issue, project: project, - note: "issue note") + note: "issue **note**") end it "calls Hipchat API for issue comment events" do @@ -247,7 +247,7 @@ describe HipchatService, models: true do "issue ##{issue_id} in " \ "#{project_name}: " \ "#{title}" \ - "
issue note
") + "

issue note

\n
") end end @@ -275,7 +275,7 @@ describe HipchatService, models: true do "snippet ##{snippet_id} in " \ "#{project_name}: " \ "#{title}" \ - "
snippet note
") + "

snippet note

\n
") end end end From 3d034c15017b45611cb3e52744f09cc45c1c7bbe Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 12:28:42 +0100 Subject: [PATCH 041/176] Full Banzai rendering for HipChat notifications --- app/models/project_services/hipchat_service.rb | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 98f0312d84e..f4edbbbceb2 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -121,14 +121,6 @@ class HipchatService < Service message end - def format_body(body) - if body - body = body.truncate(200, separator: ' ', omission: '...') - end - - "
#{body}
" - end - def create_issue_message(data) user_name = data[:user][:name] @@ -144,7 +136,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" if description - message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) + message << Banzai.render(note, project: project) end message @@ -166,7 +158,7 @@ class HipchatService < Service "#{project_link}: #{title}" if description - message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(description)) + message << Banzai.render(note, project: project) end message @@ -217,7 +209,7 @@ class HipchatService < Service message << title if note - message << format_body(Banzai::Filter::MarkdownFilter.renderer.render(note)) + message << Banzai.render(note, project: project) end message From 8e0c868436db61be8020356f4792cc79c2964363 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 12:38:31 +0100 Subject: [PATCH 042/176] Also render commit titles in HipChat notifications --- app/models/project_services/hipchat_service.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index f4edbbbceb2..7d70452a70b 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -88,6 +88,10 @@ class HipchatService < Service end end + def render_line(text) + Banzai.render(text.lines.first.chomp, project: project, pipeline: :single_line) if text + end + def create_push_message(push) ref_type = Gitlab::Git.tag_ref?(push[:ref]) ? 'tag' : 'branch' ref = Gitlab::Git.ref_name(push[:ref]) @@ -110,7 +114,7 @@ class HipchatService < Service message << "(Compare changes)" push[:commits].take(MAX_COMMITS).each do |commit| - message << "
- #{commit[:message].lines.first} (#{commit[:id][0..5]})" + message << "
- #{render_line(commit[:message])} (#{commit[:id][0..5]})" end if push[:commits].count > MAX_COMMITS @@ -126,7 +130,7 @@ class HipchatService < Service obj_attr = data[:object_attributes] obj_attr = HashWithIndifferentAccess.new(obj_attr) - title = obj_attr[:title] + title = render_line(obj_attr[:title]) state = obj_attr[:state] issue_iid = obj_attr[:iid] issue_url = obj_attr[:url] @@ -150,7 +154,7 @@ class HipchatService < Service merge_request_id = obj_attr[:iid] state = obj_attr[:state] description = obj_attr[:description] - title = obj_attr[:title] + title = render_line(obj_attr[:title]) merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}" merge_request_link = "merge request !#{merge_request_id}" @@ -165,7 +169,7 @@ class HipchatService < Service end def format_title(title) - "" + title.lines.first.chomp + "" + "#{render_line(title)}" end def create_note_message(data) From eb074021b0bfeed139e098d06d45b562b98f6214 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 16:23:16 +0100 Subject: [PATCH 043/176] Absolute URLs for Banzai HTML for HipChat Using "pipeline: :email" gets "only_path: false" into the context to produce full URLs instead of /namespace/project/... --- .../project_services/hipchat_service.rb | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 7d70452a70b..9d52a1423b7 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -89,7 +89,7 @@ class HipchatService < Service end def render_line(text) - Banzai.render(text.lines.first.chomp, project: project, pipeline: :single_line) if text + markdown(text.lines.first.chomp, pipeline: :single_line) if text end def create_push_message(push) @@ -125,6 +125,20 @@ class HipchatService < Service message end + def markdown(text, context = {}) + if text + context = ({ + project: project, + pipeline: :email + }).merge(context) + + html = Banzai.render(text, context) + html = Banzai.post_process(html, context) + else + "" + end + end + def create_issue_message(data) user_name = data[:user][:name] @@ -139,9 +153,7 @@ class HipchatService < Service issue_link = "issue ##{issue_iid}" message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" - if description - message << Banzai.render(note, project: project) - end + message << markdown(description) message end @@ -161,9 +173,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{merge_request_link} in " \ "#{project_link}: #{title}" - if description - message << Banzai.render(note, project: project) - end + message << markdown(description) message end @@ -180,11 +190,13 @@ class HipchatService < Service note = obj_attr[:note] note_url = obj_attr[:url] noteable_type = obj_attr[:noteable_type] + commit_id = nil case noteable_type when "Commit" commit_attr = HashWithIndifferentAccess.new(data[:commit]) - subject_desc = commit_attr[:id] + commit_id = commit_attr[:id] + subject_desc = commit_id subject_desc = Commit.truncate_sha(subject_desc) subject_type = "commit" title = format_title(commit_attr[:message]) @@ -212,9 +224,7 @@ class HipchatService < Service message = "#{user_name} commented on #{subject_html} in #{project_link}: " message << title - if note - message << Banzai.render(note, project: project) - end + message << markdown(note, ref: commit_id) message end From b434b75fd0a5486325dabcf0a2edf652c959675b Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 16:25:45 +0100 Subject: [PATCH 044/176] Ensure absolute URLs for single lines from Banzai for HipChat "pipeline: :single_line" leaves the protocol/host part out of the URLs and caches them that way. To avoid giving those out to HipChat, markdown is always rendered with "pipeline: :email" first. There must be a better way to do this, but I can't see how to avoid the link caching. --- app/models/project_services/hipchat_service.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 9d52a1423b7..ce4a2a96015 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -125,12 +125,16 @@ class HipchatService < Service message end - def markdown(text, context = {}) + def markdown(text, options = {}) if text - context = ({ + context = { project: project, pipeline: :email - }).merge(context) + } + + Banzai.render(text, context) + + context.merge!(options) html = Banzai.render(text, context) html = Banzai.post_process(html, context) From aa2406e0f821e217ed5e0c59a212cecd73227509 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 4 Oct 2016 16:27:40 +0100 Subject: [PATCH 045/176] Clean up Banzai HTML for HipChat The `class` and `data-*` attributes are meaningless in HipChat, and it would probably be better to limit the tags, too. For example, we could avoid block-level elements in `render_line`. --- app/models/project_services/hipchat_service.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index ce4a2a96015..8988a7b905e 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -1,4 +1,6 @@ class HipchatService < Service + include ActionView::Helpers::SanitizeHelper + MAX_COMMITS = 3 prop_accessor :token, :room, :server, :notify, :color, :api_version @@ -138,6 +140,7 @@ class HipchatService < Service html = Banzai.render(text, context) html = Banzai.post_process(html, context) + sanitize html, attributes: %w(href title alt) else "" end From 32cf2e5f77c24c27782adc37d4635b06dfada060 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Wed, 5 Oct 2016 13:38:08 +0100 Subject: [PATCH 046/176] Tests for markdown HipChat notifications --- spec/models/project_services/hipchat_service_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 90066f01f6f..1029b6d2459 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -135,7 +135,7 @@ describe HipchatService, models: true do "issue ##{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome issue" \ - "

please fix

\n
") + "

please fix

") end end @@ -159,7 +159,7 @@ describe HipchatService, models: true do "merge request !#{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome merge request" \ - "

please fix

\n
") + "

please fix

") end end @@ -190,7 +190,7 @@ describe HipchatService, models: true do "commit #{commit_id} in " \ "#{project_name}: " \ "#{title}" \ - "

a comment on a commit

\n
") + "

a comment on a commit

") end end @@ -222,7 +222,7 @@ describe HipchatService, models: true do "merge request !#{merge_id} in " \ "#{project_name}: " \ "#{title}" \ - "

merge request note

\n
") + "

merge request note

") end end @@ -247,7 +247,7 @@ describe HipchatService, models: true do "issue ##{issue_id} in " \ "#{project_name}: " \ "#{title}" \ - "

issue note

\n
") + "

issue note

") end end @@ -275,7 +275,7 @@ describe HipchatService, models: true do "snippet ##{snippet_id} in " \ "#{project_name}: " \ "#{title}" \ - "

snippet note

\n
") + "

snippet note

") end end end From 7b90680c2dd78d88d747fff545d69b1908ced7ae Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Wed, 12 Oct 2016 09:51:02 +0300 Subject: [PATCH 047/176] Use guard clause instead of if-else statement --- .../project_services/hipchat_service.rb | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 8988a7b905e..e7a77070b9f 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -128,22 +128,21 @@ class HipchatService < Service end def markdown(text, options = {}) - if text - context = { - project: project, - pipeline: :email - } + return "" unless text - Banzai.render(text, context) + context = { + project: project, + pipeline: :email + } - context.merge!(options) + Banzai.render(text, context) - html = Banzai.render(text, context) - html = Banzai.post_process(html, context) - sanitize html, attributes: %w(href title alt) - else - "" - end + context.merge!(options) + + html = Banzai.render(text, context) + html = Banzai.post_process(html, context) + + sanitize html, attributes: %w(href title alt) end def create_issue_message(data) From 257d15a67007f7c8750ca6d00a7c13f8e8a9f974 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Wed, 19 Oct 2016 22:51:15 +0300 Subject: [PATCH 048/176] Return truncation for notification descriptions, fix minor bugs with rendering --- app/models/project_services/hipchat_service.rb | 17 +++++++++++------ .../project_services/hipchat_service_spec.rb | 12 ++++++------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index e7a77070b9f..660a8ae3421 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -2,6 +2,11 @@ class HipchatService < Service include ActionView::Helpers::SanitizeHelper MAX_COMMITS = 3 + HIPCHAT_ALLOWED_TAGS = %w[ + a b i strong em br img pre code + table th tr td caption colgroup col thead tbody tfoot + ul ol li dl dt dd + ] prop_accessor :token, :room, :server, :notify, :color, :api_version boolean_accessor :notify_only_broken_builds @@ -139,10 +144,10 @@ class HipchatService < Service context.merge!(options) - html = Banzai.render(text, context) - html = Banzai.post_process(html, context) + html = Banzai.post_process(Banzai.render(text, context), context) + sanitized_html = sanitize(html, tags: HIPCHAT_ALLOWED_TAGS, attributes: %w[href title alt]) - sanitize html, attributes: %w(href title alt) + sanitized_html.truncate(200, separator: ' ', omission: '...') end def create_issue_message(data) @@ -159,7 +164,7 @@ class HipchatService < Service issue_link = "issue ##{issue_iid}" message = "#{user_name} #{state} #{issue_link} in #{project_link}: #{title}" - message << markdown(description) + message << "
#{markdown(description)}
" message end @@ -179,7 +184,7 @@ class HipchatService < Service message = "#{user_name} #{state} #{merge_request_link} in " \ "#{project_link}: #{title}" - message << markdown(description) + message << "
#{markdown(description)}
" message end @@ -230,7 +235,7 @@ class HipchatService < Service message = "#{user_name} commented on #{subject_html} in #{project_link}: " message << title - message << markdown(note, ref: commit_id) + message << "
#{markdown(note, ref: commit_id)}
" message end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 1029b6d2459..2da3a9cb09f 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -135,7 +135,7 @@ describe HipchatService, models: true do "issue ##{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome issue" \ - "

please fix

") + "
please fix
") end end @@ -159,7 +159,7 @@ describe HipchatService, models: true do "merge request !#{obj_attr["iid"]} in " \ "#{project_name}: " \ "Awesome merge request" \ - "

please fix

") + "
please fix
") end end @@ -190,7 +190,7 @@ describe HipchatService, models: true do "commit #{commit_id} in " \ "#{project_name}: " \ "#{title}" \ - "

a comment on a commit

") + "
a comment on a commit
") end end @@ -222,7 +222,7 @@ describe HipchatService, models: true do "merge request !#{merge_id} in " \ "#{project_name}: " \ "#{title}" \ - "

merge request note

") + "
merge request note
") end end @@ -247,7 +247,7 @@ describe HipchatService, models: true do "issue ##{issue_id} in " \ "#{project_name}: " \ "#{title}" \ - "

issue note

") + "
issue note
") end end @@ -275,7 +275,7 @@ describe HipchatService, models: true do "snippet ##{snippet_id} in " \ "#{project_name}: " \ "#{title}" \ - "

snippet note

") + "
snippet note
") end end end From db7c227fc64de0221d061d58387a93c6925cded7 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Thu, 20 Oct 2016 15:12:39 +0300 Subject: [PATCH 049/176] Add CHANGELOG.md entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98cffab8c03..9135b851f96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) + - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) From d1f6dfc863f1560f7e6e1a30e846304979679a61 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 20 Oct 2016 20:47:21 +0800 Subject: [PATCH 050/176] We want to release this in 8.13.0 --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6e6690ced1..4fd0b763e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,6 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - - Fix discussion thread from emails for merge requests. !7010 - ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) @@ -40,6 +38,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Speed-up group milestones show page - Fix inconsistent options dropdown caret on mobile viewports (ClemMakesApps) - Extract project#update_merge_requests and SystemHooks to its own worker from GitPushService + - Fix discussion thread from emails for merge requests. !7010 - Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs) - Add tag shortcut from the Commit page. !6543 - Keep refs for each deployment From 2e411b5ea0faf733aa457ecc28064bb48f07deed Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 19 Oct 2016 15:35:38 +0200 Subject: [PATCH 051/176] Test GitLab project import for a user with only their default namespace. Refactor the spec file: - remove hardcoded record IDs - avoid top-level let if not used in all scenarios - prefer expect { ... }.to change { ... }.from(0).to(1) over checking that there are no records at the beginning of the test --- .../import_export/import_file_spec.rb | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb index f32834801a0..3015576f6f8 100644 --- a/spec/features/projects/import_export/import_file_spec.rb +++ b/spec/features/projects/import_export/import_file_spec.rb @@ -3,13 +3,8 @@ require 'spec_helper' feature 'Import/Export - project import integration test', feature: true, js: true do include Select2Helper - let(:admin) { create(:admin) } - let(:normal_user) { create(:user) } - let!(:namespace) { create(:namespace, name: "asd", owner: admin) } let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } let(:export_path) { "#{Dir::tmpdir}/import_file_spec" } - let(:project) { Project.last } - let(:project_hook) { Gitlab::Git::Hook.new('post-receive', project.repository.path) } background do allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) @@ -19,41 +14,43 @@ feature 'Import/Export - project import integration test', feature: true, js: tr FileUtils.rm_rf(export_path, secure: true) end - context 'admin user' do + context 'when selecting the namespace' do + let(:user) { create(:admin) } + let!(:namespace) { create(:namespace, name: "asd", owner: user) } + before do - login_as(admin) + login_as(user) end scenario 'user imports an exported project successfully' do - expect(Project.all.count).to be_zero - visit new_project_path - select2('2', from: '#project_namespace_id') + select2(namespace.id, from: '#project_namespace_id') fill_in :project_path, with: 'test-project-path', visible: true click_link 'GitLab export' expect(page).to have_content('GitLab project export') - expect(URI.parse(current_url).query).to eq('namespace_id=2&path=test-project-path') + expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=test-project-path") attach_file('file', file) - click_on 'Import project' # import starts + expect { click_on 'Import project' }.to change { Project.count }.from(0).to(1) + project = Project.last expect(project).not_to be_nil expect(project.issues).not_to be_empty expect(project.merge_requests).not_to be_empty - expect(project_hook).to exist - expect(wiki_exists?).to be true + expect(project_hook_exists?(project)).to be true + expect(wiki_exists?(project)).to be true expect(project.import_status).to eq('finished') end scenario 'invalid project' do - project = create(:project, namespace_id: 2) + project = create(:project, namespace: namespace) visit new_project_path - select2('2', from: '#project_namespace_id') + select2(namespace.id, from: '#project_namespace_id') fill_in :project_path, with: project.name, visible: true click_link 'GitLab export' @@ -66,11 +63,11 @@ feature 'Import/Export - project import integration test', feature: true, js: tr end scenario 'project with no name' do - create(:project, namespace_id: 2) + create(:project, namespace: namespace) visit new_project_path - select2('2', from: '#project_namespace_id') + select2(namespace.id, from: '#project_namespace_id') # click on disabled element find(:link, 'GitLab export').trigger('click') @@ -81,24 +78,30 @@ feature 'Import/Export - project import integration test', feature: true, js: tr end end - context 'normal user' do + context 'when limited to the default user namespace' do + let(:user) { create(:user) } before do - login_as(normal_user) + login_as(user) end - scenario 'non-admin user is allowed to import a project' do - expect(Project.all.count).to be_zero - + scenario 'passes correct namespace ID in the URL' do visit new_project_path fill_in :project_path, with: 'test-project-path', visible: true - expect(page).to have_content('GitLab export') + click_link 'GitLab export' + + expect(page).to have_content('GitLab project export') + expect(URI.parse(current_url).query).to eq("namespace_id=#{user.namespace.id}&path=test-project-path") end end - def wiki_exists? + def wiki_exists?(project) wiki = ProjectWiki.new(project) File.exist?(wiki.repository.path_to_repo) && !wiki.repository.empty? end + + def project_hook_exists?(project) + Gitlab::Git::Hook.new('post-receive', project.repository.path).exists? + end end From 474313e44034882122c60329b7c72b0f31cac45b Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 19 Oct 2016 20:42:35 +0200 Subject: [PATCH 052/176] Fix GitLab project import when a user has access only to their default namespace. Render a hidden field with namespace ID so it can be read by JavaScript and passed to "/import/gitlab_project/new" screen. --- app/views/projects/new.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index cc8cb134fb8..399ccf15b7f 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -27,6 +27,7 @@ - else .input-group-addon.static-namespace #{root_url}#{current_user.username}/ + = f.hidden_field :namespace_id, value: current_user.namespace_id .form-group.col-xs-12.col-sm-6.project-path = f.label :namespace_id, class: 'label-light' do %span From e9218c7e4e6c46d72d0860ba1107643a9f15f542 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Wed, 19 Oct 2016 10:04:05 +0200 Subject: [PATCH 053/176] Change target Ruby version for Rubocop to 2.1. We have to use the lowest common denominator to check the supported syntax and in our case it is Ruby 2.1. Please note that it will not help with unsupported syntax in HAML files because they are not checked by Rubocop. --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index bec2464c740..13df3f99613 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ require: inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.1 # Cop names are not d§splayed in offense messages by default. Change behavior # by overriding DisplayCopNames, or by giving the -D/--display-cop-names # option. From 0c0caede85b0ea6082799ae9fa6afd74a1186006 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 20 Oct 2016 17:33:12 +0300 Subject: [PATCH 054/176] Fix: Backup restore doesn't clear cache --- CHANGELOG.md | 1 + lib/tasks/gitlab/backup.rake | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98cffab8c03..20aef16b105 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Simpler arguments passed to named_route on toggle_award_url helper method + - Fix: Backup restore doesn't clear cache ## 8.13.0 (2016-10-22) diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index b43ee5b3383..a9f1255e8cf 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -51,6 +51,7 @@ namespace :gitlab do $progress.puts 'done'.color(:green) Rake::Task['gitlab:backup:db:restore'].invoke end + Rake::Task['gitlab:backup:repo:restore'].invoke unless backup.skipped?('repositories') Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads') Rake::Task['gitlab:backup:builds:restore'].invoke unless backup.skipped?('builds') @@ -58,6 +59,7 @@ namespace :gitlab do Rake::Task['gitlab:backup:lfs:restore'].invoke unless backup.skipped?('lfs') Rake::Task['gitlab:backup:registry:restore'].invoke unless backup.skipped?('registry') Rake::Task['gitlab:shell:setup'].invoke + Rake::Task['cache:clear'].invoke backup.cleanup end From 23e81bfde879dfd116cb70cb8034d0fce21fffb6 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Thu, 20 Oct 2016 16:40:24 +0200 Subject: [PATCH 055/176] Ignore external issues when bulk assigning issues to author of merge request. Fixes #23552 --- app/services/merge_requests/assign_issues_service.rb | 2 +- .../merge_requests/assign_issues_service_spec.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/services/merge_requests/assign_issues_service.rb b/app/services/merge_requests/assign_issues_service.rb index f636e5fec4f..066efa1acc3 100644 --- a/app/services/merge_requests/assign_issues_service.rb +++ b/app/services/merge_requests/assign_issues_service.rb @@ -4,7 +4,7 @@ module MergeRequests @assignable_issues ||= begin if current_user == merge_request.author closes_issues.select do |issue| - !issue.assignee_id? && can?(current_user, :admin_issue, issue) + !issue.is_a?(ExternalIssue) && !issue.assignee_id? && can?(current_user, :admin_issue, issue) end else [] diff --git a/spec/services/merge_requests/assign_issues_service_spec.rb b/spec/services/merge_requests/assign_issues_service_spec.rb index 7aeb95a15ea..5034b6ef33f 100644 --- a/spec/services/merge_requests/assign_issues_service_spec.rb +++ b/spec/services/merge_requests/assign_issues_service_spec.rb @@ -46,4 +46,16 @@ describe MergeRequests::AssignIssuesService, services: true do it 'assigns these to the merge request owner' do expect { service.execute }.to change { issue.reload.assignee }.to(user) end + + it 'ignores external issues' do + external_issue = ExternalIssue.new('JIRA-123', project) + service = described_class.new( + project, + user, + merge_request: merge_request, + closes_issues: [external_issue] + ) + + expect(service.assignable_issues.count).to eq 0 + end end From fab393984a4685164886c974747d312da21e1798 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 20 Oct 2016 14:15:39 -0200 Subject: [PATCH 056/176] [ci skip] Add a comment explaining validate_board_limit callback Callback associations are not common to see around. We want to make clear that the `before_add` callback uses the number before the addition, in this particular case 1. --- app/models/project.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/models/project.rb b/app/models/project.rb index 6685baab699..af117f0acb0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1339,6 +1339,13 @@ class Project < ActiveRecord::Base shared_projects.any? end + # Similar to the normal callbacks that hook into the life cycle of an + # Active Record object, you can also define callbacks that get triggered + # when you add an object to an association collection. If any of these + # callbacks throw an exception, the object will not be added to the + # collection. Before you add a new board to the boards collection if you + # already have 1, 2, or n it will fail, but it if you have 0 that is lower + # than the number of permitted boards per project it won't fail. def validate_board_limit(board) raise BoardLimitExceeded, 'Number of permitted boards exceeded' if boards.size >= NUMBER_OF_PERMITTED_BOARDS end From 3bace66970ff5cd71107f24511447d8c5a72715f Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 14 Oct 2016 17:25:12 -0500 Subject: [PATCH 057/176] Create protected branches bundle --- .../protected_branch_access_dropdown.js.es6 | 0 .../{ => protected_branches}/protected_branch_create.js.es6 | 0 .../{ => protected_branches}/protected_branch_dropdown.js.es6 | 0 .../{ => protected_branches}/protected_branch_edit.js.es6 | 0 .../{ => protected_branches}/protected_branch_edit_list.js.es6 | 0 .../javascripts/protected_branches/protected_branches_bundle.js | 1 + app/views/projects/protected_branches/index.html.haml | 2 ++ config/application.rb | 1 + 8 files changed, 4 insertions(+) rename app/assets/javascripts/{ => protected_branches}/protected_branch_access_dropdown.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_create.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_dropdown.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_edit.js.es6 (100%) rename app/assets/javascripts/{ => protected_branches}/protected_branch_edit_list.js.es6 (100%) create mode 100644 app/assets/javascripts/protected_branches/protected_branches_bundle.js diff --git a/app/assets/javascripts/protected_branch_access_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_access_dropdown.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js.es6 diff --git a/app/assets/javascripts/protected_branch_create.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_create.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_create.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_create.js.es6 diff --git a/app/assets/javascripts/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_dropdown.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_dropdown.js.es6 diff --git a/app/assets/javascripts/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_edit.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_edit.js.es6 diff --git a/app/assets/javascripts/protected_branch_edit_list.js.es6 b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6 similarity index 100% rename from app/assets/javascripts/protected_branch_edit_list.js.es6 rename to app/assets/javascripts/protected_branches/protected_branch_edit_list.js.es6 diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js new file mode 100644 index 00000000000..15b3affd469 --- /dev/null +++ b/app/assets/javascripts/protected_branches/protected_branches_bundle.js @@ -0,0 +1 @@ +/*= require_tree . */ diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml index 49dcc9a6ba4..42e9bdbd30e 100644 --- a/app/views/projects/protected_branches/index.html.haml +++ b/app/views/projects/protected_branches/index.html.haml @@ -1,4 +1,6 @@ - page_title "Protected branches" +- content_for :page_specific_javascripts do + = page_specific_javascript_tag('protected_branches/protected_branches_bundle.js') .row.prepend-top-default.append-bottom-default .col-lg-3 diff --git a/config/application.rb b/config/application.rb index 8a9c539cb43..f3337b00dc6 100644 --- a/config/application.rb +++ b/config/application.rb @@ -87,6 +87,7 @@ module Gitlab config.assets.precompile << "users/users_bundle.js" config.assets.precompile << "network/network_bundle.js" config.assets.precompile << "profile/profile_bundle.js" + config.assets.precompile << "protected_branches/protected_branches_bundle.js" config.assets.precompile << "diff_notes/diff_notes_bundle.js" config.assets.precompile << "boards/boards_bundle.js" config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js" From b4359fb24e599c278edeae843bd6a25c980b1243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 20 Oct 2016 18:51:03 +0200 Subject: [PATCH 058/176] Don't use Hash#slice since it's not supported in Ruby 2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/commit_statuses.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index f282a3b9cd6..f54d4f06627 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -67,9 +67,14 @@ module API pipeline = @project.ensure_pipeline(ref, commit.sha, current_user) status = GenericCommitStatus.running_or_pending.find_or_initialize_by( - project: @project, pipeline: pipeline, - user: current_user, name: name, ref: ref) - status.attributes = declared(params).slice(:target_url, :description) + project: @project, + pipeline: pipeline, + user: current_user, + name: name, + ref: ref, + target_url: params[:target_url], + description: params[:description] + ) begin case params[:state].to_s From f956de7c3991f229967339ad04aa5464c02b3ac6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 20 Oct 2016 20:16:35 +0200 Subject: [PATCH 059/176] Refactor and add new functionality to CI yaml reference [ci ski] --- doc/ci/yaml/README.md | 160 +++++++++++++++++++++++++++++++++++------- 1 file changed, 135 insertions(+), 25 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 84ea59ab687..5c0e1c44e3f 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -146,13 +146,17 @@ variables: ``` These variables can be later used in all executed commands and scripts. - The YAML-defined variables are also set to all created service containers, -thus allowing to fine tune them. +thus allowing to fine tune them. Variables can be also defined on a +[job level](#job-variables). -Variables can be also defined on [job level](#job-variables). +Except for the user defined variables, there are also the ones set up by the +Runner itself. One example would be `CI_BUILD_REF_NAME` which has the value of +the branch or tag name for which project is built. Apart from the variables +you can set in `.gitlab-ci.yml`, there are also the so called secret variables +which can be set in GitLab's UI. -[Learn more about variables.](../variables/README.md) +[Learn more about variables.][variables] ### cache @@ -541,20 +545,29 @@ An example usage of manual actions is deployment to production. > Introduced in GitLab 8.9. -`environment` is used to define that a job deploys to a specific [environment]. -This allows easy tracking of all deployments to your environments straight from -GitLab. +> You can read more about environments and find more examples in the +[documentation about environments][environment]. +`environment` is used to define that a job deploys to a specific environment. If `environment` is specified and no environment under that name exists, a new one will be created automatically. -The `environment` name must contain only letters, digits, '-', '_', '/', '$', '{', '}' and spaces. Common -names are `qa`, `staging`, and `production`, but you can use whatever name works -with your workflow. +The `environment` name can contain: ---- +- letters +- digits +- spaces +- `-` +- `_` +- `/` +- `$` +- `{` +- `}` -**Example configurations** +Common names are `qa`, `staging`, and `production`, but you can use whatever +name works with your workflow. + +In its simplest form, the `environment` keyword can be defined like: ``` deploy to production: @@ -563,39 +576,134 @@ deploy to production: environment: production ``` -The `deploy to production` job will be marked as doing deployment to -`production` environment. +In the above example, the `deploy to production` job will be marked as doing a +deployment to the `production` environment. + +#### environment:name + +> Introduced in GitLab 8.11. + +>**Note:** +Before GitLab 8.11, the name of an environment could be defined as a string like +`environment: production`. The recommended way now is to define it under the +`name` keyword. + +Instead of defining the name of the environment right after the `environment` +keyword, it is also possible to define it as a separate value. For that, use +the `name` keyword under `environment`: + +``` +deploy to production: + stage: deploy + script: git push production HEAD:master + environment: + name: production +``` + +#### environment:url + +> Introduced in GitLab 8.11. + +>**Note:** +Before GitLab 8.11, the URL could be added only in GitLab's UI. The +recommended way now is to define it in `.gitlab-ci.yml`. + +This is an optional value that when set, it exposes buttons in various places +in GitLab which when clicked take you to the defined URL. + +In the example below, if the job finishes successfully, it will create buttons +in the merge requests and in the environments/deployments pages which will point +to `https://prod.example.com`. + +``` +deploy to production: + stage: deploy + script: git push production HEAD:master + environment: + name: production + url: https://prod.example.com +``` + +#### environment:on_stop + +> [Introduced][ce-6669] in GitLab 8.13. + +Closing (stoping) environments can be achieved with the `on_stop` keyword defined under +`environment`. It declares a different job that runs in order to close +the environment. + +Read the `environment:action` section for an example. + +#### environment:action + +> [Introduced][ce-6669] in GitLab 8.13. + +The `action` keyword is to be used in conjunction with `on_stop` and is defined +in the job that is called to close the environment. + +Take for instance: + +```yaml +review_app: + stage: deploy + script: make deploy-app + environment: + name: review + on_stop: stop_review_app + +stop_review_app: + stage: deploy + script: make delete-app + when: manual + environment: + name: review + action: stop +``` + +In the above example we set up the `review_app` job to deploy to the `review` +environment, and we also defined a new `stop_review_app` job under `on_stop`. +Once the `review_app` job is successfully finished, it will trigger the +`stop_review_app` job based on what is defined under `when`. In this case we +set it up to `manual` so it will need a [manual action](#manual-actions) via +GitLab's web interface in order to run. + +The `stop_review_app` job is **required** to have the following keywords defined: + +- `when` - [reference](#when) +- `environment:name` +- `environment:action` #### dynamic environments > [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6. `environment` can also represent a configuration hash with `name` and `url`. -These parameters can use any of the defined CI [variables](#variables) +These parameters can use any of the defined [CI variables](#variables) (including predefined, secure variables and `.gitlab-ci.yml` variables). -The common use case is to create dynamic environments for branches and use them -as review apps. - ---- - -**Example configurations** +For example: ``` deploy as review app: stage: deploy - script: ... + script: make deploy environment: name: review-apps/$CI_BUILD_REF_NAME url: https://$CI_BUILD_REF_NAME.review.example.com/ ``` The `deploy as review app` job will be marked as deployment to dynamically -create the `review-apps/branch-name` environment. +create the `review-apps/$CI_BUILD_REF_NAME` environment, which `$CI_BUILD_REF_NAME` +is an [environment variable][variables] set by the Runner. If for example the +`deploy as review app` job was run in a branch named `pow`, this environment +should be accessible under `https://pow.review.example.com/`. -This environment should be accessible under `https://branch-name.review.example.com/`. +This of course implies that the underlying server which hosts the application +is properly configured. -You can see a simple example at https://gitlab.com/gitlab-examples/review-apps-nginx/. +The common use case is to create dynamic environments for branches and use them +as Review Apps. You can see a simple example using Review Apps at +https://gitlab.com/gitlab-examples/review-apps-nginx/. ### artifacts @@ -1105,3 +1213,5 @@ CI with various languages. [examples]: ../examples/README.md [ce-6323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6323 [environment]: ../environments.md +[ce-6669]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6669 +[variables]: ../variables/README.md From 4e03f4c40602b568cffd591dcd5af6bd4b9a281e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Oct 2016 14:57:42 +0100 Subject: [PATCH 060/176] Fixed issues with sticky mr tabs & sidebar Closes #23504 --- app/assets/javascripts/merge_request_tabs.js | 11 +-- app/assets/stylesheets/framework/sidebar.scss | 8 +++ .../stylesheets/pages/merge_requests.scss | 13 +++- .../projects/merge_requests/_show.html.haml | 68 ++++++++++--------- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index fd21aa1fefa..1a04a037210 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -388,8 +388,7 @@ // So we dont affix the tabs on these if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return; - var tabsWidth = $tabs.outerWidth(), - $diffTabs = $('#diff-notes-app'), + var $diffTabs = $('#diff-notes-app'), offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height()); $tabs.off('affix.bs.affix affix-top.bs.affix') @@ -398,18 +397,10 @@ top: offsetTop } }).on('affix.bs.affix', function () { - $tabs.css({ - left: $tabs.offset().left, - width: tabsWidth - }); $diffTabs.css({ marginTop: $tabs.height() }); }).on('affix-top.bs.affix', function () { - $tabs.css({ - left: '', - width: '' - }); $diffTabs.css({ marginTop: '' }); diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index ec52f326eb9..1d8e64a0e4b 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -185,6 +185,10 @@ header.header-sidebar-pinned { @media (min-width: $screen-sm-min) { padding-right: $sidebar_collapsed_width; + + .merge-request-tabs-holder.affix { + right: $sidebar_collapsed_width; + } } .sidebar-collapsed-icon { @@ -207,6 +211,10 @@ header.header-sidebar-pinned { @media (min-width: $screen-md-min) { padding-right: $gutter_width; + + .merge-request-tabs-holder.affix { + right: $gutter_width; + } } &.with-overlay { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 35a1877df95..70afa568554 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -183,11 +183,11 @@ .ci-coverage { float: right; } - + .stop-env-container { color: $gl-text-color; float: right; - + a { color: $gl-text-color; } @@ -438,11 +438,18 @@ } } -.merge-request-tabs { +.merge-request-tabs-holder { background-color: #fff; &.affix { top: 100px; + left: 0; z-index: 9; + transition: right .15s; + } + + &:not(.affix) .container-fluid { + padding-left: 0; + padding-right: 0; } } diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 0e19d224fcd..f57abe73977 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -47,39 +47,41 @@ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" - if @commits_count.nonzero? - %ul.merge-request-tabs.nav-links.no-top.no-bottom{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') } - %li.notes-tab - = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do - Discussion - %span.badge= @merge_request.mr_and_commit_notes.user.count - - if @merge_request.source_project - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do - Commits - %span.badge= @commits_count - - if @pipeline - %li.pipelines-tab - = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do - Pipelines - %span.badge= @pipelines.size - %li.builds-tab - = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do - Builds - %span.badge= @statuses.size - %li.diffs-tab - = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do - Changes - %span.badge= @merge_request.diff_size - %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true } - %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" } - .line-resolve-all{ "v-show" => "discussionCount > 0", - ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" } - %span.line-resolve-btn.is-disabled{ type: "button", - ":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" } - = render "shared/icons/icon_status_success.svg" - %span.line-resolve-text - {{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved - = render "discussions/jump_to_next" + .merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') } + %div{ class: container_class } + %ul.merge-request-tabs.nav-links.no-top.no-bottom + %li.notes-tab + = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do + Discussion + %span.badge= @merge_request.mr_and_commit_notes.user.count + - if @merge_request.source_project + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do + Commits + %span.badge= @commits_count + - if @pipeline + %li.pipelines-tab + = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do + Pipelines + %span.badge= @pipelines.size + %li.builds-tab + = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do + Builds + %span.badge= @statuses.size + %li.diffs-tab + = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do + Changes + %span.badge= @merge_request.diff_size + %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true } + %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" } + .line-resolve-all{ "v-show" => "discussionCount > 0", + ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" } + %span.line-resolve-btn.is-disabled{ type: "button", + ":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" } + = render "shared/icons/icon_status_success.svg" + %span.line-resolve-text + {{ resolvedDiscussionCount }}/{{ discussionCount }} {{ discussionCount | pluralize 'discussion' }} resolved + = render "discussions/jump_to_next" .tab-content#diff-notes-app #notes.notes.tab-pane.voting_notes From a28371dbe33c568c970c704b90760d2b540256af Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 19 Oct 2016 17:24:24 +0100 Subject: [PATCH 061/176] Fixed issue when images are loading it would push off the tabs --- app/assets/javascripts/merge_request_tabs.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 1a04a037210..9f28738e06b 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -389,12 +389,18 @@ if (Breakpoints.get().getBreakpointSize() === 'xs' || !$tabs.length) return; var $diffTabs = $('#diff-notes-app'), - offsetTop = $tabs.offset().top - ($('.navbar-fixed-top').height() + $('.layout-nav').height()); + $fixedNav = $('.navbar-fixed-top'), + $layoutNav = $('.layout-nav'); $tabs.off('affix.bs.affix affix-top.bs.affix') .affix({ offset: { - top: offsetTop + top: function () { + var tabsTop = $diffTabs.offset().top - $tabs.height(); + tabsTop = tabsTop - ($fixedNav.height() + $layoutNav.height()); + + return tabsTop; + } } }).on('affix.bs.affix', function () { $diffTabs.css({ From 8c4576418be18dc6143d029f8d51645fef951655 Mon Sep 17 00:00:00 2001 From: evhoffmann Date: Thu, 20 Oct 2016 15:00:02 -0400 Subject: [PATCH 062/176] updated some links in definitions --- doc/university/glossary/README.md | 34 +++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index 2cca5439a5a..c7cfebb7c7c 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -55,7 +55,7 @@ Entry level [subscription](https://about.gitlab.com/pricing/) for GitLab EE curr ### Bitbucket -Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor. +Atlassian's web hosting service for Git and Mercurial Projects. Read about [migrating](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_bitbucket.html) from BitBucket to a GitLab instance. ### Branch @@ -152,12 +152,16 @@ The difference between two commits, or saved changes. This will also be shown vi A folder used for storing multiple files. -### Docker +### Docker Container Registry -Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in. +A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of GitLab projects. Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server. This guarantees that it will always run the same, regardless of the environment it is running in. ### Dynamic Environment +### ElasticSearch + +Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data. + ### Emacs ### Fork @@ -168,6 +172,10 @@ Your [own copy](https://docs.gitlab.com/ce/workflow/forking_workflow.html) of a A code review [tool](https://www.gerritcodereview.com/) built on top of Git. +### Git Attributes + +A [git attributes file](https://git-scm.com/docs/gitattributes) is a simple text file that gives attributes to pathnames. + ### Git Hooks [Scripts](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) you can use to trigger actions at certain points. @@ -178,7 +186,7 @@ A single-tenant solution that provides GitLab CE or EE as a managed service. Git ### GitHub -A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. As of April 2016, the service has over 14 million users. It offers free public repos, private repos and enterprise services are paid. +A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. It offers free public repos, private repos and enterprise services are paid. Read about [importing a project](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_github.html) from GitHub to GitLab. ### GitLab CE @@ -201,7 +209,7 @@ Our free SaaS for public and private repositories. Allows you to replicate your GitLab instance to other geographical locations as a read-only fully operational version. It [can be used](https://docs.gitlab.com/ee/gitlab-geo/README.html) for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster. ### GitLab Pages -These allow you to [create websites](https://pages.gitlab.io/) for your GitLab projects, groups, or user account. +These allow you to [create websites](https://gitlab.com/help/pages/README.md) for your GitLab projects, groups, or user account. ### Gitolite @@ -249,7 +257,7 @@ An Open Source CI tool written using the Java programming language. [Jenkins](ht ### Jira -Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](https://docs.gitlab.com/ee/project_services/jira.html) to interact with JIRA Core either using an on-premises instance or the SaaS solution that Atlassian offers. +Atlassian's [project management software](https://www.atlassian.com/software/jira), i.e. a complex issue tracker. GitLab [can be configured](https://docs.gitlab.com/ee/project_services/jira.html) to interact with JIRA Core either using an on-premise instance or the SaaS solution that Atlassian offers. ### JUnit @@ -353,7 +361,7 @@ A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). I ### OAuth -An open [standard](https://oauth.net/) for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. +An open standard for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. GitLab [is](https://docs.gitlab.com/ce/integration/oauth_provider.html) an OAuth2 authentication service provider. ### Omnibus Packages @@ -413,7 +421,7 @@ A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works). ### Push -Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. +Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. Read about [advanced push rules](https://gitlab.com/help/pages/README.md) in GitLab. ### RE Read Only @@ -447,6 +455,10 @@ A route table contains rules (called routes) that determine where network traffi Actual build machines/containers that [run and execute tests](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner) you have specified to be run on GitLab CI. +### Sidekiq + +The background job processor GitLab [uses](https://docs.gitlab.com/ce/administration/troubleshooting/sidekiq.html) to asynchronously run tasks. + ### Software as a service (SaaS) Software that is hosted centrally and accessed on-demand (i.e. whenever you want to). This applies to GitLab.com. @@ -465,7 +477,7 @@ The board used to track the status and progress of each of the sprint backlog it ### Shell -[Terminal](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. +Terminal on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. You [use git]() and make changes to GitLab projects in your shell. You [use git](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) and make changes to GitLab projects in your shell. ### Single-tenant @@ -517,7 +529,7 @@ A program that allows you to perform superuser/administrator actions on Unix Ope ### Subversion (SVN) -An open source version control system. +An open source version control system. Read about [migrating from SVN](https://docs.gitlab.com/ce/workflow/importing/migrating_from_svn.html) to GitLab using SubGit. ### Tag @@ -559,7 +571,7 @@ A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building softwa ### Webhooks -A way for for an app to [provide](https://docs.gitlab.com/ce/web_hooks/web_hooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) +A way for for an app to [provide](https://docs.gitlab.com/ce/web_hooks/web_hooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) Read about setting up [custom git hooks](https://gitlab.com/help/administration/custom_hooks.md) for when webhooks are insufficient. ### Wiki From 1ff140ea386d856c526b4797f38b4937e9b26f80 Mon Sep 17 00:00:00 2001 From: Linus G Thiel Date: Tue, 11 Oct 2016 11:30:32 +0200 Subject: [PATCH 063/176] Close any open tooltips before page:fetch --- CHANGELOG.md | 1 + app/assets/javascripts/application.js | 6 +----- app/assets/javascripts/lib/utils/common_utils.js | 8 ++++++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dc323e02c..646426a437c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Don't include archived projects when creating group milestones. !4940 (Jeroen Jacobs) - Add tag shortcut from the Commit page. !6543 - Keep refs for each deployment + - Close open tooltips on page navigation (Linus Thiel) - Allow browsing branches that end with '.atom' - Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller) - Replace unique keyframes mixin with keyframe mixin with specific names (ClemMakesApps) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8a61669822c..b966a568bbd 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -123,15 +123,11 @@ return str.replace(/<(?:.|\n)*?>/gm, ''); }; - window.unbindEvents = function() { - return $(document).off('scroll'); - }; - window.shiftWindow = function() { return scrollBy(0, -100); }; - document.addEventListener("page:fetch", unbindEvents); + document.addEventListener("page:fetch", gl.utils.cleanupBeforeFetch); window.addEventListener("hashchange", shiftWindow); diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index b170e26eebf..698abae6228 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -43,6 +43,14 @@ parser.href = url; return parser; }; + + gl.utils.cleanupBeforeFetch = function() { + // Unbind scroll events + $(document).off('scroll'); + // Close any open tooltips + $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy'); + }; + return jQuery.timefor = function(time, suffix, expiredLabel) { var suffixFromNow, timefor; if (!time) { From 12991f84a2ceb157b7ddec94858ccc395c767006 Mon Sep 17 00:00:00 2001 From: evhoffmann Date: Thu, 20 Oct 2016 15:47:57 -0400 Subject: [PATCH 064/176] added skipped definition --- doc/university/glossary/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index c7cfebb7c7c..cf836667fac 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -565,6 +565,8 @@ A [virtual machine](https://en.wikipedia.org/wiki/Virtual_private_server) sold a ### VM Instance +In object-oriented programming, an [instance](http://stackoverflow.com/questions/20461907/what-is-meaning-of-instance-in-programming) is a specific realization of any object. An object may be varied in a number of ways. Each realized variation of that object is an instance. Therefore, a VM instance is an instance of a virtual machine, which is an emulation of a computer system. + ### Waterfall A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified. From 57046eb0abf280594d6625db3429f13d45499c83 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Wed, 19 Oct 2016 14:40:58 +0200 Subject: [PATCH 065/176] Ensure custom provider tab labels don't break layout. (Also fix some issues for session views on small screens.) --- app/assets/stylesheets/pages/login.scss | 34 ++++++++++++++++++- .../devise/sessions/two_factor.html.haml | 2 +- app/views/devise/shared/_tabs_ldap.html.haml | 2 +- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index bdb13bee178..9496234c773 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -143,6 +143,7 @@ &:not(.active) { background-color: $gray-light; + border-left: 1px solid $border-color; } a { @@ -170,6 +171,31 @@ } } + // Ldap configurations may need more tabs & the tab labels are user generated (arbitrarily long). + // These styles prevent this from breaking the layout, and only applied when providers are configured. + + .new-session-tabs.custom-provider-tabs { + flex-wrap: wrap; + + li { + min-width: 85px; + flex-basis: auto; + + // This styles tab elements that have wrapped to a second line. We cannot easily predict when this will happen. + // We are making somewhat of an assumption about the configuration here: that users do not have more than + // 3 LDAP servers configured (in addition to standard login) and they are not using especially long names for any + // of them. If either condition is false, this will work as expected. If both are true, there may be a missing border + // above one of the bottom row elements. If you know a better way, please implement it! + &:nth-child(n+5) { + border-top: 1px solid $border-color; + } + } + + a { + font-size: 16px; + } + } + .form-control { &:active, &:focus { @@ -203,6 +229,7 @@ .login-page { .col-sm-5.pull-right { float: none !important; + margin-bottom: 45px; } } } @@ -244,7 +271,11 @@ } .navless-container { - padding: 65px; // height of footer + bottom padding of email confirmation link + padding: 65px 15px; // height of footer + bottom padding of email confirmation link + + @media (max-width: $screen-xs-max) { + padding: 0 15px 65px; + } } } @@ -263,3 +294,4 @@ bottom: 0; } } + diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml index 0e865b807c1..fd77cdbee2e 100644 --- a/app/views/devise/sessions/two_factor.html.haml +++ b/app/views/devise/sessions/two_factor.html.haml @@ -10,7 +10,7 @@ = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post, html: { class: 'edit_user show-gl-field-errors' }) do |f| - resource_params = params[resource_name].presence || params = f.hidden_field :remember_me, value: resource_params.fetch(:remember_me, 0) - .form-group + %div = f.label 'Two-Factor Authentication code', name: :otp_attempt = f.text_field :otp_attempt, class: 'form-control', required: true, autofocus: true, autocomplete: 'off', title: 'This field is required.' %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes. diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index a057f126c45..1e957f0935f 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -1,4 +1,4 @@ -%ul.new-session-tabs.nav-links.nav-tabs +%ul.new-session-tabs.nav-links.nav-tabs{ class: ('custom-provider-tabs' if form_based_providers.any?) } - if crowd_enabled? %li.active = link_to "Crowd", "#crowd", 'data-toggle' => 'tab' From c1be12d0fcadd2b51557d53f87aace2152a97aec Mon Sep 17 00:00:00 2001 From: "Luke \"Jared\" Bennett" Date: Mon, 17 Oct 2016 19:06:56 +0000 Subject: [PATCH 066/176] Tidied up pipelines.js.es6 and removed jQuery where acceptable. --- app/assets/javascripts/pipelines.js.es6 | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index a7624de6089..5a16def5e35 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -2,20 +2,22 @@ class Pipelines { constructor() { - $(document).off('click', '.toggle-pipeline-btn').on('click', '.toggle-pipeline-btn', this.toggleGraph); + this.initGraphToggle(); this.addMarginToBuildColumns(); } + initGraphToggle() { + this.toggleButton = document.querySelector('.toggle-pipeline-btn'); + this.toggleButtonText = this.toggleButton.querySelector('.toggle-btn-text'); + this.pipelineGraph = document.querySelector('.pipeline-graph'); + this.toggleButton.addEventListener('click', this.toggleGraph.bind(this)); + } + toggleGraph() { - const $pipelineBtn = $(this).closest('.toggle-pipeline-btn'); - const $pipelineGraph = $(this).closest('.row-content-block').next('.pipeline-graph'); - const $btnText = $(this).find('.toggle-btn-text'); - const graphCollapsed = $pipelineGraph.hasClass('graph-collapsed'); - - $($pipelineBtn).add($pipelineGraph).toggleClass('graph-collapsed'); - - - graphCollapsed ? $btnText.text('Hide') : $btnText.text('Expand') + const graphCollapsed = this.pipelineGraph.classList.contains('graph-collapsed'); + this.toggleButton.classList.toggle('graph-collapsed'); + this.pipelineGraph.classList.toggle('graph-collapsed'); + graphCollapsed ? this.toggleButtonText.textContent = 'Hide' : this.toggleButtonText.textContent = 'Expand'; } addMarginToBuildColumns() { @@ -31,7 +33,7 @@ if ($('.build', $this).length === 1) $this.addClass('no-margin'); }); } - $('.pipeline-graph').removeClass('hidden'); + this.pipelineGraph.classList.remove('hidden'); } } From 599f1cb023b19173f861ed304f284f66b4a5396d Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 20 Oct 2016 22:18:39 +0100 Subject: [PATCH 067/176] converted last method to jQuery --- .../javascripts/extensions/element.js.es6 | 6 +++++ app/assets/javascripts/pipelines.js.es6 | 27 ++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 app/assets/javascripts/extensions/element.js.es6 diff --git a/app/assets/javascripts/extensions/element.js.es6 b/app/assets/javascripts/extensions/element.js.es6 new file mode 100644 index 00000000000..d5d4af3573c --- /dev/null +++ b/app/assets/javascripts/extensions/element.js.es6 @@ -0,0 +1,6 @@ +Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatches; + +Element.prototype.closest = function closest(selector, selectedElement = this) { + if (!selectedElement) return; + return selectedElement.matches(selector) ? selectedElement : Element.prototype.closest(selector, selectedElement.parentElement); +}; diff --git a/app/assets/javascripts/pipelines.js.es6 b/app/assets/javascripts/pipelines.js.es6 index 5a16def5e35..0fa56df0d2a 100644 --- a/app/assets/javascripts/pipelines.js.es6 +++ b/app/assets/javascripts/pipelines.js.es6 @@ -7,9 +7,9 @@ } initGraphToggle() { + this.pipelineGraph = document.querySelector('.pipeline-graph'); this.toggleButton = document.querySelector('.toggle-pipeline-btn'); this.toggleButtonText = this.toggleButton.querySelector('.toggle-btn-text'); - this.pipelineGraph = document.querySelector('.pipeline-graph'); this.toggleButton.addEventListener('click', this.toggleGraph.bind(this)); } @@ -17,21 +17,22 @@ const graphCollapsed = this.pipelineGraph.classList.contains('graph-collapsed'); this.toggleButton.classList.toggle('graph-collapsed'); this.pipelineGraph.classList.toggle('graph-collapsed'); - graphCollapsed ? this.toggleButtonText.textContent = 'Hide' : this.toggleButtonText.textContent = 'Expand'; + this.toggleButtonText.textContent = graphCollapsed ? 'Hide' : 'Expand'; } addMarginToBuildColumns() { - const $secondChildBuildNode = $('.build:nth-child(2)'); - if ($secondChildBuildNode.length) { - const $firstChildBuildNode = $secondChildBuildNode.prev('.build'); - const $multiBuildColumn = $secondChildBuildNode.closest('.stage-column'); - const $previousColumn = $multiBuildColumn.prev('.stage-column'); - $multiBuildColumn.addClass('left-margin'); - $firstChildBuildNode.addClass('left-connector'); - $previousColumn.each(function() { - $this = $(this); - if ($('.build', $this).length === 1) $this.addClass('no-margin'); - }); + const secondChildBuildNodes = this.pipelineGraph.querySelectorAll('.build:nth-child(2)'); + for (buildNodeIndex in secondChildBuildNodes) { + const buildNode = secondChildBuildNodes[buildNodeIndex]; + const firstChildBuildNode = buildNode.previousElementSibling; + if (!firstChildBuildNode || !firstChildBuildNode.matches('.build')) continue; + const multiBuildColumn = buildNode.closest('.stage-column'); + const previousColumn = multiBuildColumn.previousElementSibling; + if (!previousColumn || !previousColumn.matches('.stage-column')) continue; + multiBuildColumn.classList.add('left-margin'); + firstChildBuildNode.classList.add('left-connector'); + const columnBuilds = previousColumn.querySelectorAll('.build'); + if (columnBuilds.length === 1) previousColumn.classList.add('no-margin'); } this.pipelineGraph.classList.remove('hidden'); } From a82a80aa4e7841f8474cc25c21bb6d93b0eaee92 Mon Sep 17 00:00:00 2001 From: Linus G Thiel Date: Thu, 6 Oct 2016 11:31:01 +0200 Subject: [PATCH 068/176] Trim project_path whitespace on form submit When the form is submitted, any leading and/or trailing whitespace is trimmed. --- CHANGELOG.md | 1 + app/views/projects/new.html.haml | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dc323e02c..25f2a3777e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) + - Trim leading and trailing whitespace on project_path (Linus Thiel) - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Simpler arguments passed to named_route on toggle_award_url helper method diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 399ccf15b7f..932603f03b0 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -127,6 +127,11 @@ } }); + $('#new_project').submit(function(){ + var $path = $('#project_path'); + $path.val($path.val().trim()); + }); + $('#project_path').keyup(function(){ if($(this).val().length !=0) { $('.btn_import_gitlab_project').attr('disabled', false); From 1c668b125e8fd3e2959d0a2bd83447f09ea7fee4 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Fri, 21 Oct 2016 09:35:17 +1100 Subject: [PATCH 069/176] Add hover to trash icon in notes --- CHANGELOG.md | 1 + app/views/projects/notes/_note.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73dc323e02c..128f5dec039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Fix HipChat notifications rendering (airatshigapov, eisnerd) + - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method ## 8.13.0 (2016-10-22) diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 73fe6a715fa..ab719e38904 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -57,7 +57,7 @@ = link_to '#', title: 'Edit comment', class: 'note-action-button js-note-edit' do = icon('pencil', class: 'link-highlight') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do - = icon('trash-o') + = icon('trash-o', class: 'danger-highlight') .note-body{class: note_editable ? 'js-task-list-container' : ''} .note-text.md = preserve do From f87124da1f3cf48415457b2c8bdae7ce4cb573ea Mon Sep 17 00:00:00 2001 From: tauriedavis Date: Fri, 14 Oct 2016 11:42:08 -0700 Subject: [PATCH 070/176] fix font weight of project feature settings --- app/assets/stylesheets/pages/projects.scss | 16 ++- app/views/projects/edit.html.haml | 116 ++++++++++----------- 2 files changed, 72 insertions(+), 60 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 1062d7effb0..fe7cf3c87e3 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -13,9 +13,18 @@ .new_project, .edit-project { + fieldset { - &.features .control-label { - font-weight: normal; + + &.features { + + .label-light { + margin-bottom: 0; + } + + .help-block { + margin-top: 0; + } } .form-group { @@ -40,6 +49,7 @@ } .input-group > div { + &:last-child { padding-right: 0; } @@ -47,6 +57,7 @@ @media (max-width: $screen-xs-max) { .input-group > div { + margin-bottom: 14px; &:last-child { @@ -60,6 +71,7 @@ } .input-group-addon { + &.static-namespace { height: 35px; border-radius: 3px; diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index fb776e3a3e7..55b6580e640 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -46,70 +46,70 @@ %h5.prepend-top-0 Feature Visibility - = f.fields_for :project_feature do |feature_fields| - .form_group.prepend-top-20 - .row - .col-md-9 - = feature_fields.label :repository_access_level, "Repository", class: 'label-light' - %span.help-block Push files to be stored in this project - .col-md-3.js-repo-access-level - = project_feature_access_select(:repository_access_level) + = f.fields_for :project_feature do |feature_fields| + .form_group.prepend-top-20 + .row + .col-md-9 + = feature_fields.label :repository_access_level, "Repository", class: 'label-light' + %span.help-block Push files to be stored in this project + .col-md-3.js-repo-access-level + = project_feature_access_select(:repository_access_level) - .col-sm-12 - .row - .col-md-9.project-feature-nested - = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light' - %span.help-block Submit changes to be merged upstream - .col-md-3 - = project_feature_access_select(:merge_requests_access_level) + .col-sm-12 + .row + .col-md-9.project-feature-nested + = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light' + %span.help-block Submit changes to be merged upstream + .col-md-3 + = project_feature_access_select(:merge_requests_access_level) - .row - .col-md-9.project-feature-nested - = feature_fields.label :builds_access_level, "Builds", class: 'label-light' - %span.help-block Submit, test and deploy your changes before merge - .col-md-3 - = project_feature_access_select(:builds_access_level) + .row + .col-md-9.project-feature-nested + = feature_fields.label :builds_access_level, "Builds", class: 'label-light' + %span.help-block Submit, test and deploy your changes before merge + .col-md-3 + = project_feature_access_select(:builds_access_level) - .row - .col-md-9 - = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light' - %span.help-block Share code pastes with others out of Git repository - .col-md-3 - = project_feature_access_select(:snippets_access_level) + .row + .col-md-9 + = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light' + %span.help-block Share code pastes with others out of Git repository + .col-md-3 + = project_feature_access_select(:snippets_access_level) - .row - .col-md-9 - = feature_fields.label :issues_access_level, "Issues", class: 'label-light' - %span.help-block Lightweight issue tracking system for this project - .col-md-3 - = project_feature_access_select(:issues_access_level) + .row + .col-md-9 + = feature_fields.label :issues_access_level, "Issues", class: 'label-light' + %span.help-block Lightweight issue tracking system for this project + .col-md-3 + = project_feature_access_select(:issues_access_level) - .row - .col-md-9 - = feature_fields.label :wiki_access_level, "Wiki", class: 'label-light' - %span.help-block Pages for project documentation - .col-md-3 - = project_feature_access_select(:wiki_access_level) - - - if Gitlab.config.lfs.enabled && current_user.admin? - .checkbox - = f.label :lfs_enabled do - = f.check_box :lfs_enabled - %strong LFS - %br - %span.descr - Git Large File Storage - = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') + .row + .col-md-9 + = feature_fields.label :wiki_access_level, "Wiki", class: 'label-light' + %span.help-block Pages for project documentation + .col-md-3 + = project_feature_access_select(:wiki_access_level) - if Gitlab.config.lfs.enabled && current_user.admin? - .form-group - .checkbox - = f.label :container_registry_enabled do - = f.check_box :container_registry_enabled - %strong Container Registry - %br - %span.descr Enable Container Registry for this project - = link_to icon('question-circle'), help_page_path('user/project/container_registry'), target: '_blank' + .checkbox + = f.label :lfs_enabled do + = f.check_box :lfs_enabled + %strong LFS + %br + %span.descr + Git Large File Storage + = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') + + - if Gitlab.config.lfs.enabled && current_user.admin? + .form-group + .checkbox + = f.label :container_registry_enabled do + = f.check_box :container_registry_enabled + %strong Container Registry + %br + %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 %hr @@ -288,4 +288,4 @@ Saving project. %p Please wait a moment, this page will automatically refresh when ready. -= render 'shared/confirm_modal', phrase: @project.path += render 'shared/confirm_modal', phrase: @project.path \ No newline at end of file From cf31a0f0b2b5ba6d4445e3e5c767119f9cf5953a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 21 Oct 2016 01:03:16 -0700 Subject: [PATCH 071/176] Disable warming of the asset cache in Spinach tests under CI I suspect some combination of Knapsack tests cause no regular Rack tests to be loaded (i.e. all JavaScript tests), which leads to the error: ArgumentError: rack-test requires a rack application, but none was given In CI, we precompile all the assets so there is no need to warm the asset cache in any case. Closes #23613 --- features/support/capybara.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/support/capybara.rb b/features/support/capybara.rb index fe9e39cf509..dae0d0f918c 100644 --- a/features/support/capybara.rb +++ b/features/support/capybara.rb @@ -20,5 +20,5 @@ unless ENV['CI'] || ENV['CI_SERVER'] end Spinach.hooks.before_run do - TestEnv.warm_asset_cache + TestEnv.warm_asset_cache unless ENV['CI'] || ENV['CI_SERVER'] end From 168197cd5a179c961301225626ac1a175f892782 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 18 Oct 2016 16:49:19 +0300 Subject: [PATCH 072/176] Fix project member access levels --- CHANGELOG.md | 1 + ...61018124658_make_project_owners_masters.rb | 15 ++++++++ db/schema.rb | 2 +- .../project_members_controller_spec.rb | 36 +++++++++++++++++++ spec/requests/api/members_spec.rb | 11 ++++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20161018124658_make_project_owners_masters.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 670404e4fce..16ca2ff93e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -137,6 +137,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix buggy iOS tooltip layering behavior. - Make guests unable to view MRs on private projects - Fix broken Project API docs (Takuya Noguchi) + - Migrate invalid project members (owner -> master) ## 8.12.7 diff --git a/db/migrate/20161018124658_make_project_owners_masters.rb b/db/migrate/20161018124658_make_project_owners_masters.rb new file mode 100644 index 00000000000..a576bb7b622 --- /dev/null +++ b/db/migrate/20161018124658_make_project_owners_masters.rb @@ -0,0 +1,15 @@ +class MakeProjectOwnersMasters < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + update_column_in_batches(:members, :access_level, 40) do |table, query| + query.where(table[:access_level].eq(50).and(table[:source_type].eq('Project'))) + end + end + + def down + # do nothing + end +end diff --git a/db/schema.rb b/db/schema.rb index a3c7fc2fd57..f5c01511195 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -843,7 +843,7 @@ ActiveRecord::Schema.define(version: 20161019213545) do t.integer "builds_access_level" t.datetime "created_at" t.datetime "updated_at" - t.integer "repository_access_level", default: 20, null: false + t.integer "repository_access_level", default: 20, null: false end add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 8519ebc1d5f..5e487241d07 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -228,4 +228,40 @@ describe Projects::ProjectMembersController do end end end + + describe 'POST create' do + let(:stranger) { create(:user) } + + context 'when creating owner' do + before do + project.team << [user, :master] + sign_in(user) + end + + it 'does not create a member' do + expect do + post :create, user_ids: stranger.id, + namespace_id: project.namespace, + access_level: Member::OWNER, + project_id: project + end.to change { project.members.count }.by(0) + end + end + + context 'when create master' do + before do + project.team << [user, :master] + sign_in(user) + end + + it 'creates a member' do + expect do + post :create, user_ids: stranger.id, + namespace_id: project.namespace, + access_level: Member::MASTER, + project_id: project + end.to change { project.members.count }.by(1) + end + end + end end diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index d22e0595788..493c0a893d1 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -328,4 +328,15 @@ describe API::Members, api: true do it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'group' do let(:source) { group } end + + context 'Adding owner to project' do + it 'returns 403' do + expect do + post api("/projects/#{project.id}/members", master), + user_id: stranger.id, access_level: Member::OWNER + + expect(response).to have_http_status(422) + end.to change { project.members.count }.by(0) + end + end end From b332931af375a29fa0ff41d45315400beca44c7a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 20 Oct 2016 21:43:24 -0700 Subject: [PATCH 073/176] Fix broken label uniqueness label migration The previous implementation of the migration failed on staging because the migration was attempted to remove labels from projects that did not actually have duplicates. This occurred because the SQL query did not account for the project ID when selecting the labels. To replicate the problem: 1. Disable the uniqueness validation in app/models/label.rb. 2. Create a duplicate label "bug" in project A. 3. Create the same label in project B with label "bug". The migration will attempt to remove the label in B even if there are no duplicates. Closes #23609 --- db/migrate/20161017125927_add_unique_index_to_labels.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20161017125927_add_unique_index_to_labels.rb b/db/migrate/20161017125927_add_unique_index_to_labels.rb index 16ae38612de..f2b56ebfb7b 100644 --- a/db/migrate/20161017125927_add_unique_index_to_labels.rb +++ b/db/migrate/20161017125927_add_unique_index_to_labels.rb @@ -7,9 +7,9 @@ class AddUniqueIndexToLabels < ActiveRecord::Migration disable_ddl_transaction! def up - select_all('SELECT title, COUNT(id) as cnt FROM labels GROUP BY project_id, title HAVING COUNT(id) > 1').each do |label| + select_all('SELECT title, project_id, COUNT(id) as cnt FROM labels GROUP BY project_id, title HAVING COUNT(id) > 1').each do |label| label_title = quote_string(label['title']) - duplicated_ids = select_all("SELECT id FROM labels WHERE title = '#{label_title}' ORDER BY id ASC").map{ |label| label['id'] } + duplicated_ids = select_all("SELECT id FROM labels WHERE project_id = #{label['project_id']} AND title = '#{label_title}' ORDER BY id ASC").map{ |label| label['id'] } label_id = duplicated_ids.first duplicated_ids.delete(label_id) From c81ff152e08d58c13efbd50c40dd2e083ac65083 Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 21 Oct 2016 13:53:38 +0200 Subject: [PATCH 074/176] Change "Group#web_url" to return "/groups/twitter" rather than "/twitter". Bring back the old behaviour which was changed by 6b90ccb9. Fixes #23527. --- app/models/group.rb | 2 +- config/routes/group.rb | 33 ++++++++++++++++++--------------- spec/models/group_spec.rb | 6 ++++++ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 00a595d2705..6865e610718 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -68,7 +68,7 @@ class Group < Namespace end def web_url - Gitlab::Routing.url_helpers.group_url(self) + Gitlab::Routing.url_helpers.group_canonical_url(self) end def human_name diff --git a/config/routes/group.rb b/config/routes/group.rb index 4838c9d91c6..826048ba196 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -12,23 +12,26 @@ constraints(GroupUrlConstrainer.new) do end end -resources :groups, constraints: { id: /[a-zA-Z.0-9_\-]+(? 'groups#show', as: :group_canonical end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index ac862055ebc..47f89f744cb 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -265,4 +265,10 @@ describe Group, models: true do members end + + describe '#web_url' do + it 'returns the canonical URL' do + expect(group.web_url).to include("groups/#{group.name}") + end + end end From a74dfa301e77752e333467892073811677e82c89 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 21 Oct 2016 13:43:52 +0100 Subject: [PATCH 075/176] Fixed compare ellipsis messing with layout --- app/views/projects/compare/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index 76b68c544aa..7bde20c3286 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -10,7 +10,7 @@ = button_tag type: 'button', class: "form-control compare-dropdown-toggle js-compare-dropdown", required: true, data: { refs_url: refs_namespace_project_path(@project.namespace, @project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do .dropdown-toggle-text= params[:from] || 'Select branch/tag' = render "ref_dropdown" - .compare-ellipsis ... + .compare-ellipsis.inline ... .form-group.dropdown.compare-form-group.to.js-compare-to-dropdown .input-group.inline-input-group %span.input-group-addon to From 1a53511a3454bf70786d72e59530bff42ae160e4 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Fri, 14 Oct 2016 16:29:54 -0500 Subject: [PATCH 076/176] Fix object data to be sent to fetch analytics data --- app/assets/javascripts/cycle_analytics.js.es6 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/cycle_analytics.js.es6 b/app/assets/javascripts/cycle_analytics.js.es6 index bd9accacb8c..20791bab942 100644 --- a/app/assets/javascripts/cycle_analytics.js.es6 +++ b/app/assets/javascripts/cycle_analytics.js.es6 @@ -36,7 +36,11 @@ method: 'GET', dataType: 'json', contentType: 'application/json', - data: { start_date: options.startDate } + data: { + cycle_analytics: { + start_date: options.startDate + } + } }).done((data) => { this.decorateData(data); this.initDropdown(); From 6c2ab27aeaf0cd59d87e14876492a4162d48e2d7 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 19 Oct 2016 10:27:24 -0500 Subject: [PATCH 077/176] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c5c96c4528..42e3df435bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Update duration at the end of pipeline - ExpireBuildArtifactsWorker query builds table without ordering enqueuing one job per build to cleanup - Add group level labels. (!6425) + - Fix Cycle analytics not showing correct data when filtering by date. !6906 - Add an example for testing a phoenix application with Gitlab CI in the docs (Manthan Mallikarjun) - Cancelled pipelines could be retried. !6927 - Updating verbiage on git basics to be more intuitive From b939529c2a2c724f1471ab3b0ec2a5dac10c913c Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Fri, 21 Oct 2016 18:05:36 +0300 Subject: [PATCH 078/176] Fix wrong endpoint in api/users documentation, fix same typo in spec describe blocks --- doc/api/users.md | 2 +- spec/requests/api/users_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/api/users.md b/doc/api/users.md index 2b12770d5a5..a50ba5432fe 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -643,7 +643,7 @@ Parameters: | `id` | integer | yes | The ID of the user | ```bash -curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/user/:id/events +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/users/:id/events ``` Example response: diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index f83f4d2c9b1..d48752473f3 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -846,7 +846,7 @@ describe API::API, api: true do end end - describe 'PUT /user/:id/block' do + describe 'PUT /users/:id/block' do before { admin } it 'blocks existing user' do put api("/users/#{user.id}/block", admin) @@ -873,7 +873,7 @@ describe API::API, api: true do end end - describe 'PUT /user/:id/unblock' do + describe 'PUT /users/:id/unblock' do let(:blocked_user) { create(:user, state: 'blocked') } before { admin } @@ -914,7 +914,7 @@ describe API::API, api: true do end end - describe 'GET /user/:id/events' do + describe 'GET /users/:id/events' do let(:user) { create(:user) } let(:project) { create(:empty_project) } let(:note) { create(:note_on_issue, note: 'What an awesome day!', project: project) } From 97731760d7252acf8ee94c707c0e107492b1ef24 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 21 Oct 2016 18:13:41 +0200 Subject: [PATCH 079/176] Re-organize queues to use for Sidekiq Dumping too many jobs in the same queue (e.g. the "default" queue) is a dangerous setup. Jobs that take a long time to process can effectively block any other work from being performed given there are enough of these jobs. Furthermore it becomes harder to monitor the jobs as a single queue could contain jobs for different workers. In such a setup the only reliable way of getting counts per job is to iterate over all jobs in a queue, which is a rather time consuming process. By using separate queues for various workers we have better control over throughput, we can add weight to queues, and we can monitor queues better. Some workers still use the same queue whenever their work is related. For example, the various CI pipeline workers use the same "pipeline" queue. This commit includes a Rails migration that moves Sidekiq jobs from the old queues to the new ones. This migration also takes care of doing the inverse if ever needed. This does require downtime as otherwise new jobs could be scheduled in the old queues after this migration completes. This commit also includes an RSpec test that blacklists the use of the "default" queue and ensures cron workers use the "cronjob" queue. Fixes gitlab-org/gitlab-ce#23370 --- CHANGELOG.md | 1 + app/workers/admin_email_worker.rb | 3 +- app/workers/build_coverage_worker.rb | 2 +- app/workers/build_email_worker.rb | 1 + app/workers/build_finished_worker.rb | 1 + app/workers/build_hooks_worker.rb | 2 +- app/workers/build_success_worker.rb | 2 +- app/workers/clear_database_cache_worker.rb | 1 + app/workers/concerns/build_queue.rb | 8 ++ app/workers/concerns/cronjob_queue.rb | 9 ++ .../concerns/dedicated_sidekiq_queue.rb | 9 ++ app/workers/concerns/pipeline_queue.rb | 8 ++ .../concerns/repository_check_queue.rb | 8 ++ app/workers/delete_user_worker.rb | 1 + app/workers/email_receiver_worker.rb | 3 +- app/workers/emails_on_push_worker.rb | 2 +- app/workers/expire_build_artifacts_worker.rb | 1 + .../expire_build_instance_artifacts_worker.rb | 1 + app/workers/git_garbage_collect_worker.rb | 3 +- app/workers/gitlab_shell_worker.rb | 3 +- app/workers/group_destroy_worker.rb | 3 +- .../import_export_project_cleanup_worker.rb | 3 +- app/workers/irker_worker.rb | 1 + app/workers/merge_worker.rb | 3 +- app/workers/new_note_worker.rb | 3 +- app/workers/pipeline_hooks_worker.rb | 2 +- app/workers/pipeline_metrics_worker.rb | 3 +- app/workers/pipeline_process_worker.rb | 3 +- app/workers/pipeline_success_worker.rb | 2 +- app/workers/pipeline_update_worker.rb | 3 +- app/workers/post_receive.rb | 3 +- app/workers/project_cache_worker.rb | 3 +- app/workers/project_destroy_worker.rb | 3 +- app/workers/project_export_worker.rb | 3 +- app/workers/project_service_worker.rb | 3 +- app/workers/project_web_hook_worker.rb | 3 +- app/workers/prune_old_events_worker.rb | 1 + .../remove_expired_group_links_worker.rb | 1 + app/workers/remove_expired_members_worker.rb | 1 + .../repository_archive_cache_worker.rb | 3 +- app/workers/repository_check/batch_worker.rb | 21 ++-- app/workers/repository_check/clear_worker.rb | 3 +- .../single_repository_worker.rb | 3 +- app/workers/repository_fork_worker.rb | 3 +- app/workers/repository_import_worker.rb | 3 +- app/workers/requests_profiles_worker.rb | 3 +- app/workers/stuck_ci_builds_worker.rb | 1 + app/workers/system_hook_worker.rb | 3 +- app/workers/trending_projects_worker.rb | 3 +- app/workers/update_merge_requests_worker.rb | 1 + bin/background_jobs | 3 +- config/application.rb | 3 +- config/sidekiq_queues.yml | 46 ++++++++ ...736_migrate_sidekiq_queues_from_default.rb | 109 ++++++++++++++++++ doc/development/README.md | 3 +- doc/development/sidekiq_style_guide.md | 38 ++++++ spec/workers/concerns/build_queue_spec.rb | 14 +++ spec/workers/concerns/cronjob_queue_spec.rb | 18 +++ .../concerns/dedicated_sidekiq_queue_spec.rb | 20 ++++ spec/workers/concerns/pipeline_queue_spec.rb | 14 +++ .../concerns/repository_check_queue_spec.rb | 18 +++ spec/workers/every_sidekiq_worker_spec.rb | 44 +++++++ 62 files changed, 425 insertions(+), 68 deletions(-) create mode 100644 app/workers/concerns/build_queue.rb create mode 100644 app/workers/concerns/cronjob_queue.rb create mode 100644 app/workers/concerns/dedicated_sidekiq_queue.rb create mode 100644 app/workers/concerns/pipeline_queue.rb create mode 100644 app/workers/concerns/repository_check_queue.rb create mode 100644 config/sidekiq_queues.yml create mode 100644 db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb create mode 100644 doc/development/sidekiq_style_guide.md create mode 100644 spec/workers/concerns/build_queue_spec.rb create mode 100644 spec/workers/concerns/cronjob_queue_spec.rb create mode 100644 spec/workers/concerns/dedicated_sidekiq_queue_spec.rb create mode 100644 spec/workers/concerns/pipeline_queue_spec.rb create mode 100644 spec/workers/concerns/repository_check_queue_spec.rb create mode 100644 spec/workers/every_sidekiq_worker_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 518d0362d07..52d435df8f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) + - All Sidekiq workers now use their own queue - Avoid race condition when asynchronously removing expired artifacts. (!6881) - Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675) - Respond with 404 Not Found for non-existent tags (Linus Thiel) diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb index 667fff031dd..c2dc955b27c 100644 --- a/app/workers/admin_email_worker.rb +++ b/app/workers/admin_email_worker.rb @@ -1,7 +1,6 @@ class AdminEmailWorker include Sidekiq::Worker - - sidekiq_options retry: false # this job auto-repeats via sidekiq-cron + include CronjobQueue def perform repository_check_failed_count = Project.where(last_repository_check_failed: true).count diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb index 0680645a8db..def0ab1dde1 100644 --- a/app/workers/build_coverage_worker.rb +++ b/app/workers/build_coverage_worker.rb @@ -1,6 +1,6 @@ class BuildCoverageWorker include Sidekiq::Worker - sidekiq_options queue: :default + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id) diff --git a/app/workers/build_email_worker.rb b/app/workers/build_email_worker.rb index 1c7a04a66a8..5fdb1f2baa0 100644 --- a/app/workers/build_email_worker.rb +++ b/app/workers/build_email_worker.rb @@ -1,5 +1,6 @@ class BuildEmailWorker include Sidekiq::Worker + include BuildQueue def perform(build_id, recipients, push_data) recipients.each do |recipient| diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb index e7286b77ac5..466410bf08c 100644 --- a/app/workers/build_finished_worker.rb +++ b/app/workers/build_finished_worker.rb @@ -1,5 +1,6 @@ class BuildFinishedWorker include Sidekiq::Worker + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb index e22ececb3fd..9965af935d4 100644 --- a/app/workers/build_hooks_worker.rb +++ b/app/workers/build_hooks_worker.rb @@ -1,6 +1,6 @@ class BuildHooksWorker include Sidekiq::Worker - sidekiq_options queue: :default + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id) diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb index 500d357ce31..e0ad5268664 100644 --- a/app/workers/build_success_worker.rb +++ b/app/workers/build_success_worker.rb @@ -1,6 +1,6 @@ class BuildSuccessWorker include Sidekiq::Worker - sidekiq_options queue: :default + include BuildQueue def perform(build_id) Ci::Build.find_by(id: build_id).try do |build| diff --git a/app/workers/clear_database_cache_worker.rb b/app/workers/clear_database_cache_worker.rb index c541daba50e..c4cb4733482 100644 --- a/app/workers/clear_database_cache_worker.rb +++ b/app/workers/clear_database_cache_worker.rb @@ -1,6 +1,7 @@ # This worker clears all cache fields in the database, working in batches. class ClearDatabaseCacheWorker include Sidekiq::Worker + include DedicatedSidekiqQueue BATCH_SIZE = 1000 diff --git a/app/workers/concerns/build_queue.rb b/app/workers/concerns/build_queue.rb new file mode 100644 index 00000000000..cf0ead40a8b --- /dev/null +++ b/app/workers/concerns/build_queue.rb @@ -0,0 +1,8 @@ +# Concern for setting Sidekiq settings for the various CI build workers. +module BuildQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :build + end +end diff --git a/app/workers/concerns/cronjob_queue.rb b/app/workers/concerns/cronjob_queue.rb new file mode 100644 index 00000000000..e918bb011e0 --- /dev/null +++ b/app/workers/concerns/cronjob_queue.rb @@ -0,0 +1,9 @@ +# Concern that sets various Sidekiq settings for workers executed using a +# cronjob. +module CronjobQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :cronjob, retry: false + end +end diff --git a/app/workers/concerns/dedicated_sidekiq_queue.rb b/app/workers/concerns/dedicated_sidekiq_queue.rb new file mode 100644 index 00000000000..132bae6022b --- /dev/null +++ b/app/workers/concerns/dedicated_sidekiq_queue.rb @@ -0,0 +1,9 @@ +# Concern that sets the queue of a Sidekiq worker based on the worker's class +# name/namespace. +module DedicatedSidekiqQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: name.sub(/Worker\z/, '').underscore.tr('/', '_') + end +end diff --git a/app/workers/concerns/pipeline_queue.rb b/app/workers/concerns/pipeline_queue.rb new file mode 100644 index 00000000000..ca3860e1d38 --- /dev/null +++ b/app/workers/concerns/pipeline_queue.rb @@ -0,0 +1,8 @@ +# Concern for setting Sidekiq settings for the various CI pipeline workers. +module PipelineQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :pipeline + end +end diff --git a/app/workers/concerns/repository_check_queue.rb b/app/workers/concerns/repository_check_queue.rb new file mode 100644 index 00000000000..a597321ccf4 --- /dev/null +++ b/app/workers/concerns/repository_check_queue.rb @@ -0,0 +1,8 @@ +# Concern for setting Sidekiq settings for the various repository check workers. +module RepositoryCheckQueue + extend ActiveSupport::Concern + + included do + sidekiq_options queue: :repository_check, retry: false + end +end diff --git a/app/workers/delete_user_worker.rb b/app/workers/delete_user_worker.rb index 6ff361e4d80..3194c389b3d 100644 --- a/app/workers/delete_user_worker.rb +++ b/app/workers/delete_user_worker.rb @@ -1,5 +1,6 @@ class DeleteUserWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(current_user_id, delete_user_id, options = {}) delete_user = User.find(delete_user_id) diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index 842eebdea9e..d3f7e479a8d 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -1,7 +1,6 @@ class EmailReceiverWorker include Sidekiq::Worker - - sidekiq_options queue: :incoming_email + include DedicatedSidekiqQueue def perform(raw) return unless Gitlab::IncomingEmail.enabled? diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb index 1dc7e0adef7..b9cd49985dc 100644 --- a/app/workers/emails_on_push_worker.rb +++ b/app/workers/emails_on_push_worker.rb @@ -1,7 +1,7 @@ class EmailsOnPushWorker include Sidekiq::Worker + include DedicatedSidekiqQueue - sidekiq_options queue: :mailers attr_reader :email, :skip_premailer def perform(project_id, recipients, push_data, options = {}) diff --git a/app/workers/expire_build_artifacts_worker.rb b/app/workers/expire_build_artifacts_worker.rb index 174eabff9fd..a27585fd389 100644 --- a/app/workers/expire_build_artifacts_worker.rb +++ b/app/workers/expire_build_artifacts_worker.rb @@ -1,5 +1,6 @@ class ExpireBuildArtifactsWorker include Sidekiq::Worker + include CronjobQueue def perform Rails.logger.info 'Scheduling removal of build artifacts' diff --git a/app/workers/expire_build_instance_artifacts_worker.rb b/app/workers/expire_build_instance_artifacts_worker.rb index d9e2cc37bb3..eb403c134d1 100644 --- a/app/workers/expire_build_instance_artifacts_worker.rb +++ b/app/workers/expire_build_instance_artifacts_worker.rb @@ -1,5 +1,6 @@ class ExpireBuildInstanceArtifactsWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(build_id) build = Ci::Build diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb index a6cefd4d601..65f8093b5b0 100644 --- a/app/workers/git_garbage_collect_worker.rb +++ b/app/workers/git_garbage_collect_worker.rb @@ -1,8 +1,9 @@ class GitGarbageCollectWorker include Sidekiq::Worker include Gitlab::ShellAdapter + include DedicatedSidekiqQueue - sidekiq_options queue: :gitlab_shell, retry: false + sidekiq_options retry: false def perform(project_id) project = Project.find(project_id) diff --git a/app/workers/gitlab_shell_worker.rb b/app/workers/gitlab_shell_worker.rb index cfeda88bbc5..964287a1793 100644 --- a/app/workers/gitlab_shell_worker.rb +++ b/app/workers/gitlab_shell_worker.rb @@ -1,8 +1,7 @@ class GitlabShellWorker include Sidekiq::Worker include Gitlab::ShellAdapter - - sidekiq_options queue: :gitlab_shell + include DedicatedSidekiqQueue def perform(action, *arg) gitlab_shell.send(action, *arg) diff --git a/app/workers/group_destroy_worker.rb b/app/workers/group_destroy_worker.rb index 5048746f09b..a49a5fd0855 100644 --- a/app/workers/group_destroy_worker.rb +++ b/app/workers/group_destroy_worker.rb @@ -1,7 +1,6 @@ class GroupDestroyWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(group_id, user_id) begin diff --git a/app/workers/import_export_project_cleanup_worker.rb b/app/workers/import_export_project_cleanup_worker.rb index 72e3a9ae734..7957ed807ab 100644 --- a/app/workers/import_export_project_cleanup_worker.rb +++ b/app/workers/import_export_project_cleanup_worker.rb @@ -1,7 +1,6 @@ class ImportExportProjectCleanupWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include CronjobQueue def perform ImportExportCleanUpService.new.execute diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb index 19f38358eb5..7e44b241743 100644 --- a/app/workers/irker_worker.rb +++ b/app/workers/irker_worker.rb @@ -3,6 +3,7 @@ require 'socket' class IrkerWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(project_id, chans, colors, push_data, settings) project = Project.find(project_id) diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb index c87c0a252b1..79efca4f2f9 100644 --- a/app/workers/merge_worker.rb +++ b/app/workers/merge_worker.rb @@ -1,7 +1,6 @@ class MergeWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(merge_request_id, current_user_id, params) params = params.with_indifferent_access diff --git a/app/workers/new_note_worker.rb b/app/workers/new_note_worker.rb index 1b3232cd365..c3e62bb88c0 100644 --- a/app/workers/new_note_worker.rb +++ b/app/workers/new_note_worker.rb @@ -1,7 +1,6 @@ class NewNoteWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(note_id, note_params) note = Note.find(note_id) diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb index ab5e9f6daad..7e36eacebf8 100644 --- a/app/workers/pipeline_hooks_worker.rb +++ b/app/workers/pipeline_hooks_worker.rb @@ -1,6 +1,6 @@ class PipelineHooksWorker include Sidekiq::Worker - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb index 7bb92df3bbd..34f6ef161fb 100644 --- a/app/workers/pipeline_metrics_worker.rb +++ b/app/workers/pipeline_metrics_worker.rb @@ -1,7 +1,6 @@ class PipelineMetricsWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline| diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb index f44227d7086..357e4a9a1c3 100644 --- a/app/workers/pipeline_process_worker.rb +++ b/app/workers/pipeline_process_worker.rb @@ -1,7 +1,6 @@ class PipelineProcessWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb index 5dd443fea59..2aa6fff24da 100644 --- a/app/workers/pipeline_success_worker.rb +++ b/app/workers/pipeline_success_worker.rb @@ -1,6 +1,6 @@ class PipelineSuccessWorker include Sidekiq::Worker - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline| diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb index 44a7f24e401..96c4152c674 100644 --- a/app/workers/pipeline_update_worker.rb +++ b/app/workers/pipeline_update_worker.rb @@ -1,7 +1,6 @@ class PipelineUpdateWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include PipelineQueue def perform(pipeline_id) Ci::Pipeline.find_by(id: pipeline_id) diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb index a9a2b716005..eee0ca12af9 100644 --- a/app/workers/post_receive.rb +++ b/app/workers/post_receive.rb @@ -1,7 +1,6 @@ class PostReceive include Sidekiq::Worker - - sidekiq_options queue: :post_receive + include DedicatedSidekiqQueue def perform(repo_path, identifier, changes) if path = Gitlab.config.repositories.storages.find { |p| repo_path.start_with?(p[1].to_s) } diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb index 0d524e88dc3..71b274e0c99 100644 --- a/app/workers/project_cache_worker.rb +++ b/app/workers/project_cache_worker.rb @@ -5,8 +5,7 @@ # storage engine as much. class ProjectCacheWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue LEASE_TIMEOUT = 15.minutes.to_i diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb index 3062301a9b1..b462327490e 100644 --- a/app/workers/project_destroy_worker.rb +++ b/app/workers/project_destroy_worker.rb @@ -1,7 +1,6 @@ class ProjectDestroyWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include DedicatedSidekiqQueue def perform(project_id, user_id, params) begin diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb index 615311e63f5..6009aa1b191 100644 --- a/app/workers/project_export_worker.rb +++ b/app/workers/project_export_worker.rb @@ -1,7 +1,8 @@ class ProjectExportWorker include Sidekiq::Worker + include DedicatedSidekiqQueue - sidekiq_options queue: :gitlab_shell, retry: 3 + sidekiq_options retry: 3 def perform(current_user_id, project_id) current_user = User.find(current_user_id) diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb index 64d39c4d3f7..fdfdeab7b41 100644 --- a/app/workers/project_service_worker.rb +++ b/app/workers/project_service_worker.rb @@ -1,7 +1,6 @@ class ProjectServiceWorker include Sidekiq::Worker - - sidekiq_options queue: :project_web_hook + include DedicatedSidekiqQueue def perform(hook_id, data) data = data.with_indifferent_access diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb index fb878965288..efb85eafd15 100644 --- a/app/workers/project_web_hook_worker.rb +++ b/app/workers/project_web_hook_worker.rb @@ -1,7 +1,6 @@ class ProjectWebHookWorker include Sidekiq::Worker - - sidekiq_options queue: :project_web_hook + include DedicatedSidekiqQueue def perform(hook_id, data, hook_name) data = data.with_indifferent_access diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb index 5883cafe1d1..392abb9c21b 100644 --- a/app/workers/prune_old_events_worker.rb +++ b/app/workers/prune_old_events_worker.rb @@ -1,5 +1,6 @@ class PruneOldEventsWorker include Sidekiq::Worker + include CronjobQueue def perform # Contribution calendar shows maximum 12 months of events. diff --git a/app/workers/remove_expired_group_links_worker.rb b/app/workers/remove_expired_group_links_worker.rb index 246c8b6650a..2a619f83410 100644 --- a/app/workers/remove_expired_group_links_worker.rb +++ b/app/workers/remove_expired_group_links_worker.rb @@ -1,5 +1,6 @@ class RemoveExpiredGroupLinksWorker include Sidekiq::Worker + include CronjobQueue def perform ProjectGroupLink.expired.destroy_all diff --git a/app/workers/remove_expired_members_worker.rb b/app/workers/remove_expired_members_worker.rb index cf765af97ce..31f652e5f9b 100644 --- a/app/workers/remove_expired_members_worker.rb +++ b/app/workers/remove_expired_members_worker.rb @@ -1,5 +1,6 @@ class RemoveExpiredMembersWorker include Sidekiq::Worker + include CronjobQueue def perform Member.expired.find_each do |member| diff --git a/app/workers/repository_archive_cache_worker.rb b/app/workers/repository_archive_cache_worker.rb index a2e49c61f59..e47069df189 100644 --- a/app/workers/repository_archive_cache_worker.rb +++ b/app/workers/repository_archive_cache_worker.rb @@ -1,7 +1,6 @@ class RepositoryArchiveCacheWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include CronjobQueue def perform RepositoryArchiveCleanUpService.new.execute diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb index a3e16fa5212..c3e7491ec4e 100644 --- a/app/workers/repository_check/batch_worker.rb +++ b/app/workers/repository_check/batch_worker.rb @@ -1,14 +1,13 @@ module RepositoryCheck class BatchWorker include Sidekiq::Worker - + include CronjobQueue + RUN_TIME = 3600 - - sidekiq_options retry: false - + def perform start = Time.now - + # This loop will break after a little more than one hour ('a little # more' because `git fsck` may take a few minutes), or if it runs out of # projects to check. By default sidekiq-cron will start a new @@ -17,15 +16,15 @@ module RepositoryCheck project_ids.each do |project_id| break if Time.now - start >= RUN_TIME break unless current_settings.repository_checks_enabled - + next unless try_obtain_lease(project_id) - + SingleRepositoryWorker.new.perform(project_id) end end - + private - + # Project.find_each does not support WHERE clauses and # Project.find_in_batches does not support ordering. So we just build an # array of ID's. This is OK because we do it only once an hour, because @@ -39,7 +38,7 @@ module RepositoryCheck reorder('last_repository_check_at ASC').limit(limit).pluck(:id) never_checked_projects + old_check_projects end - + def try_obtain_lease(id) # Use a 24-hour timeout because on servers/projects where 'git fsck' is # super slow we definitely do not want to run it twice in parallel. @@ -48,7 +47,7 @@ module RepositoryCheck timeout: 24.hours ).try_obtain end - + def current_settings # No caching of the settings! If we cache them and an admin disables # this feature, an active RepositoryCheckWorker would keep going for up diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb index b7202ddff34..1f1b38540ee 100644 --- a/app/workers/repository_check/clear_worker.rb +++ b/app/workers/repository_check/clear_worker.rb @@ -1,8 +1,7 @@ module RepositoryCheck class ClearWorker include Sidekiq::Worker - - sidekiq_options retry: false + include RepositoryCheckQueue def perform # Do small batched updates because these updates will be slow and locking diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb index 98ddf5d0688..3d8bfc6fc6c 100644 --- a/app/workers/repository_check/single_repository_worker.rb +++ b/app/workers/repository_check/single_repository_worker.rb @@ -1,8 +1,7 @@ module RepositoryCheck class SingleRepositoryWorker include Sidekiq::Worker - - sidekiq_options retry: false + include RepositoryCheckQueue def perform(project_id) project = Project.find(project_id) diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index 61ed1c38ac4..efc99ec962a 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -1,8 +1,7 @@ class RepositoryForkWorker include Sidekiq::Worker include Gitlab::ShellAdapter - - sidekiq_options queue: :gitlab_shell + include DedicatedSidekiqQueue def perform(project_id, forked_from_repository_storage_path, source_path, target_path) Gitlab::Metrics.add_event(:fork_repository, diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index d2ca8813ab9..c8a77e21c12 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -1,8 +1,7 @@ class RepositoryImportWorker include Sidekiq::Worker include Gitlab::ShellAdapter - - sidekiq_options queue: :gitlab_shell + include DedicatedSidekiqQueue attr_accessor :project, :current_user diff --git a/app/workers/requests_profiles_worker.rb b/app/workers/requests_profiles_worker.rb index 9dd228a2483..703b025d76e 100644 --- a/app/workers/requests_profiles_worker.rb +++ b/app/workers/requests_profiles_worker.rb @@ -1,7 +1,6 @@ class RequestsProfilesWorker include Sidekiq::Worker - - sidekiq_options queue: :default + include CronjobQueue def perform Gitlab::RequestProfiler.remove_all_profiles diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb index 6828013b377..b70df5a1afa 100644 --- a/app/workers/stuck_ci_builds_worker.rb +++ b/app/workers/stuck_ci_builds_worker.rb @@ -1,5 +1,6 @@ class StuckCiBuildsWorker include Sidekiq::Worker + include CronjobQueue BUILD_STUCK_TIMEOUT = 1.day diff --git a/app/workers/system_hook_worker.rb b/app/workers/system_hook_worker.rb index a122c274763..baf2f12eeac 100644 --- a/app/workers/system_hook_worker.rb +++ b/app/workers/system_hook_worker.rb @@ -1,7 +1,6 @@ class SystemHookWorker include Sidekiq::Worker - - sidekiq_options queue: :system_hook + include DedicatedSidekiqQueue def perform(hook_id, data, hook_name) SystemHook.find(hook_id).execute(data, hook_name) diff --git a/app/workers/trending_projects_worker.rb b/app/workers/trending_projects_worker.rb index df4c4a6628b..0531630d13a 100644 --- a/app/workers/trending_projects_worker.rb +++ b/app/workers/trending_projects_worker.rb @@ -1,7 +1,6 @@ class TrendingProjectsWorker include Sidekiq::Worker - - sidekiq_options queue: :trending_projects + include CronjobQueue def perform Rails.logger.info('Refreshing trending projects') diff --git a/app/workers/update_merge_requests_worker.rb b/app/workers/update_merge_requests_worker.rb index 03f0528cdae..acc4d858136 100644 --- a/app/workers/update_merge_requests_worker.rb +++ b/app/workers/update_merge_requests_worker.rb @@ -1,5 +1,6 @@ class UpdateMergeRequestsWorker include Sidekiq::Worker + include DedicatedSidekiqQueue def perform(project_id, user_id, oldrev, newrev, ref) project = Project.find_by(id: project_id) diff --git a/bin/background_jobs b/bin/background_jobs index 25a578a1c49..f28e2f722dc 100755 --- a/bin/background_jobs +++ b/bin/background_jobs @@ -4,6 +4,7 @@ cd $(dirname $0)/.. app_root=$(pwd) sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid" sidekiq_logfile="$app_root/log/sidekiq.log" +sidekiq_config="$app_root/config/sidekiq_queues.yml" gitlab_user=$(ls -l config.ru | awk '{print $3}') warn() @@ -37,7 +38,7 @@ start_no_deamonize() start_sidekiq() { - exec bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile "$@" + exec bundle exec sidekiq -C "${sidekiq_config}" -e $RAILS_ENV -P $sidekiq_pidfile "$@" } load_ok() diff --git a/config/application.rb b/config/application.rb index f3337b00dc6..92c8467e7f4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -24,7 +24,8 @@ module Gitlab #{config.root}/app/models/ci #{config.root}/app/models/hooks #{config.root}/app/models/members - #{config.root}/app/models/project_services)) + #{config.root}/app/models/project_services + #{config.root}/app/workers/concerns)) config.generators.templates.push("#{config.root}/generator_templates") diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml new file mode 100644 index 00000000000..c2e880e891f --- /dev/null +++ b/config/sidekiq_queues.yml @@ -0,0 +1,46 @@ +# This configuration file should be exclusively used to set queue settings for +# Sidekiq. Any other setting should be specified using the Sidekiq CLI or the +# Sidekiq Ruby API (see config/initializers/sidekiq.rb). +--- +# All the queues to process and their weights. Every queue _must_ have a weight +# defined. +# +# The available weights are as follows +# +# 1: low priority +# 2: medium priority +# 3: high priority +# 5: _super_ high priority, this should only be used for _very_ important queues +# +# As per http://stackoverflow.com/a/21241357/290102 the formula for calculating +# the likelihood of a job being popped off a queue (given all queues have work +# to perform) is: +# +# chance = (queue weight / total weight of all queues) * 100 +:queues: + - [post_receive, 5] + - [merge, 5] + - [update_merge_requests, 3] + - [new_note, 2] + - [build, 2] + - [pipeline, 2] + - [gitlab_shell, 2] + - [email_receiver, 2] + - [emails_on_push, 2] + - [repository_fork, 1] + - [repository_import, 1] + - [project_service, 1] + - [clear_database_cache, 1] + - [delete_user, 1] + - [expire_build_instance_artifacts, 1] + - [group_destroy, 1] + - [irker, 1] + - [project_cache, 1] + - [project_destroy, 1] + - [project_export, 1] + - [project_web_hook, 1] + - [repository_check, 1] + - [system_hook, 1] + - [git_garbage_collect, 1] + - [cronjob, 1] + - [default, 1] diff --git a/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb new file mode 100644 index 00000000000..e875213ab96 --- /dev/null +++ b/db/migrate/20161019190736_migrate_sidekiq_queues_from_default.rb @@ -0,0 +1,109 @@ +require 'json' + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class MigrateSidekiqQueuesFromDefault < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + + DOWNTIME_REASON = <<-EOF + Moving Sidekiq jobs from queues requires Sidekiq to be stopped. Not stopping + Sidekiq will result in the loss of jobs that are scheduled after this + migration completes. + EOF + + disable_ddl_transaction! + + # Jobs for which the queue names have been changed (e.g. multiple workers + # using the same non-default queue). + # + # The keys are the old queue names, the values the jobs to move and their new + # queue names. + RENAMED_QUEUES = { + gitlab_shell: { + 'GitGarbageCollectorWorker' => :git_garbage_collector, + 'ProjectExportWorker' => :project_export, + 'RepositoryForkWorker' => :repository_fork, + 'RepositoryImportWorker' => :repository_import + }, + project_web_hook: { + 'ProjectServiceWorker' => :project_service + }, + incoming_email: { + 'EmailReceiverWorker' => :email_receiver + }, + mailers: { + 'EmailsOnPushWorker' => :emails_on_push + }, + default: { + 'AdminEmailWorker' => :cronjob, + 'BuildCoverageWorker' => :build, + 'BuildEmailWorker' => :build, + 'BuildFinishedWorker' => :build, + 'BuildHooksWorker' => :build, + 'BuildSuccessWorker' => :build, + 'ClearDatabaseCacheWorker' => :clear_database_cache, + 'DeleteUserWorker' => :delete_user, + 'ExpireBuildArtifactsWorker' => :cronjob, + 'ExpireBuildInstanceArtifactsWorker' => :expire_build_instance_artifacts, + 'GroupDestroyWorker' => :group_destroy, + 'ImportExportProjectCleanupWorker' => :cronjob, + 'IrkerWorker' => :irker, + 'MergeWorker' => :merge, + 'NewNoteWorker' => :new_note, + 'PipelineHooksWorker' => :pipeline, + 'PipelineMetricsWorker' => :pipeline, + 'PipelineProcessWorker' => :pipeline, + 'PipelineSuccessWorker' => :pipeline, + 'PipelineUpdateWorker' => :pipeline, + 'ProjectCacheWorker' => :project_cache, + 'ProjectDestroyWorker' => :project_destroy, + 'PruneOldEventsWorker' => :cronjob, + 'RemoveExpiredGroupLinksWorker' => :cronjob, + 'RemoveExpiredMembersWorker' => :cronjob, + 'RepositoryArchiveCacheWorker' => :cronjob, + 'RepositoryCheck::BatchWorker' => :cronjob, + 'RepositoryCheck::ClearWorker' => :repository_check, + 'RepositoryCheck::SingleRepositoryWorker' => :repository_check, + 'RequestsProfilesWorker' => :cronjob, + 'StuckCiBuildsWorker' => :cronjob, + 'UpdateMergeRequestsWorker' => :update_merge_requests + } + } + + def up + Sidekiq.redis do |redis| + RENAMED_QUEUES.each do |queue, jobs| + migrate_from_queue(redis, queue, jobs) + end + end + end + + def down + Sidekiq.redis do |redis| + RENAMED_QUEUES.each do |dest_queue, jobs| + jobs.each do |worker, from_queue| + migrate_from_queue(redis, from_queue, worker => dest_queue) + end + end + end + end + + def migrate_from_queue(redis, queue, job_mapping) + while job = redis.lpop("queue:#{queue}") + payload = JSON.load(job) + new_queue = job_mapping[payload['class']] + + # If we have no target queue to migrate to we're probably dealing with + # some ancient job for which the worker no longer exists. In that case + # there's no sane option we can take, other than just dropping the job. + next unless new_queue + + payload['queue'] = new_queue + + redis.lpush("queue:#{new_queue}", JSON.dump(payload)) + end + end +end diff --git a/doc/development/README.md b/doc/development/README.md index 9706cb1de7f..fb6a8a5b095 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -14,7 +14,8 @@ - [Testing standards and style guidelines](testing.md) - [UI guide](ui_guide.md) for building GitLab with existing CSS styles and elements - [Frontend guidelines](frontend.md) -- [SQL guidelines](sql.md) for SQL guidelines +- [SQL guidelines](sql.md) for working with SQL queries +- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers ## Process diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md new file mode 100644 index 00000000000..e3a20f29a09 --- /dev/null +++ b/doc/development/sidekiq_style_guide.md @@ -0,0 +1,38 @@ +# Sidekiq Style Guide + +This document outlines various guidelines that should be followed when adding or +modifying Sidekiq workers. + +## Default Queue + +Use of the "default" queue is not allowed. Every worker should use a queue that +matches the worker's purpose the closest. For example, workers that are to be +executed periodically should use the "cronjob" queue. + +A list of all available queues can be found in `config/sidekiq_queues.yml`. + +## Dedicated Queues + +Most workers should use their own queue. To ease this process a worker can +include the `DedicatedSidekiqQueue` concern as follows: + +```ruby +class ProcessSomethingWorker + include Sidekiq::Worker + include DedicatedSidekiqQueue +end +``` + +This will set the queue name based on the class' name, minus the `Worker` +suffix. In the above example this would lead to the queue being +`process_something`. + +In some cases multiple workers do use the same queue. For example, the various +workers for updating CI pipelines all use the `pipeline` queue. Adding workers +to existing queues should be done with care, as adding more workers can lead to +slow jobs blocking work (even for different jobs) on the shared queue. + +## Tests + +Each Sidekiq worker must be tested using RSpec, just like any other class. These +tests should be placed in `spec/workers`. diff --git a/spec/workers/concerns/build_queue_spec.rb b/spec/workers/concerns/build_queue_spec.rb new file mode 100644 index 00000000000..6bf955e0be2 --- /dev/null +++ b/spec/workers/concerns/build_queue_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe BuildQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include BuildQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('build') + end +end diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb new file mode 100644 index 00000000000..5d1336c21a6 --- /dev/null +++ b/spec/workers/concerns/cronjob_queue_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe CronjobQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include CronjobQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob') + end + + it 'disables retrying of failed jobs' do + expect(worker.sidekiq_options['retry']).to eq(false) + end +end diff --git a/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb b/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb new file mode 100644 index 00000000000..512baec8b7e --- /dev/null +++ b/spec/workers/concerns/dedicated_sidekiq_queue_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe DedicatedSidekiqQueue do + let(:worker) do + Class.new do + def self.name + 'Foo::Bar::DummyWorker' + end + + include Sidekiq::Worker + include DedicatedSidekiqQueue + end + end + + describe 'queue names' do + it 'sets the queue name based on the class name' do + expect(worker.sidekiq_options['queue']).to eq('foo_bar_dummy') + end + end +end diff --git a/spec/workers/concerns/pipeline_queue_spec.rb b/spec/workers/concerns/pipeline_queue_spec.rb new file mode 100644 index 00000000000..40794d0e42a --- /dev/null +++ b/spec/workers/concerns/pipeline_queue_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe PipelineQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include PipelineQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('pipeline') + end +end diff --git a/spec/workers/concerns/repository_check_queue_spec.rb b/spec/workers/concerns/repository_check_queue_spec.rb new file mode 100644 index 00000000000..8868e969829 --- /dev/null +++ b/spec/workers/concerns/repository_check_queue_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe RepositoryCheckQueue do + let(:worker) do + Class.new do + include Sidekiq::Worker + include RepositoryCheckQueue + end + end + + it 'sets the queue name of a worker' do + expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check') + end + + it 'disables retrying of failed jobs' do + expect(worker.sidekiq_options['retry']).to eq(false) + end +end diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb new file mode 100644 index 00000000000..fc9adf47c1e --- /dev/null +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe 'Every Sidekiq worker' do + let(:workers) do + root = Rails.root.join('app', 'workers') + concerns = root.join('concerns').to_s + + workers = Dir[root.join('**', '*.rb')]. + reject { |path| path.start_with?(concerns) } + + workers.map do |path| + ns = Pathname.new(path).relative_path_from(root).to_s.gsub('.rb', '') + + ns.camelize.constantize + end + end + + it 'does not use the default queue' do + workers.each do |worker| + expect(worker.sidekiq_options['queue'].to_s).not_to eq('default') + end + end + + it 'uses the cronjob queue when the worker runs as a cronjob' do + cron_workers = Settings.cron_jobs. + map { |job_name, options| options['job_class'].constantize }. + to_set + + workers.each do |worker| + next unless cron_workers.include?(worker) + + expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob') + end + end + + it 'defines the queue in the Sidekiq configuration file' do + config = YAML.load_file(Rails.root.join('config', 'sidekiq_queues.yml').to_s) + queue_names = config[:queues].map { |(queue, _)| queue }.to_set + + workers.each do |worker| + expect(queue_names).to include(worker.sidekiq_options['queue'].to_s) + end + end +end From e32858e7734a13b820d2f1439e189c1586af29d8 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 21 Oct 2016 23:08:44 +0600 Subject: [PATCH 080/176] removes extra line for empty issue description --- app/assets/stylesheets/framework/common.scss | 2 ++ app/views/projects/issues/show.html.haml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 81e4e264560..0d00b6a0d9c 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -372,3 +372,5 @@ table { margin-right: -$gl-padding; border-top: 1px solid $border-color; } + +.hide-bottom-border { border-bottom: none;} diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 6f3f238a436..6defd7834bf 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -53,7 +53,7 @@ .issue-details.issuable-details - .detail-page-description.content-block + .detail-page-description.content-block{class: ('hide-bottom-border' unless @issue.description.present? )} %h2.title = markdown_field(@issue, :title) - if @issue.description.present? From af669cbea386d900ec86510f7d43de6ca4c1771e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 17 Oct 2016 18:52:14 +0200 Subject: [PATCH 081/176] Change the approach to check if patches apply cleanly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .gitlab-ci.yml | 2 +- lib/gitlab/ee_compat_check.rb | 260 ++++++++++++++++++++++++++++ lib/tasks/ce_to_ee_merge_check.rake | 4 - lib/tasks/ee_compat_check.rake | 4 + lib/tasks/gitlab/dev.rake | 107 ++---------- 5 files changed, 276 insertions(+), 101 deletions(-) create mode 100644 lib/gitlab/ee_compat_check.rb delete mode 100644 lib/tasks/ce_to_ee_merge_check.rake create mode 100644 lib/tasks/ee_compat_check.rake diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 76117a48730..9c4b4acbaf5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -210,7 +210,7 @@ rake brakeman: *exec rake flay: *exec license_finder: *exec rake downtime_check: *exec -rake ce_to_ee_merge_check: +rake ee_compat_check: <<: *exec only: - branches diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb new file mode 100644 index 00000000000..5e8a0c14eba --- /dev/null +++ b/lib/gitlab/ee_compat_check.rb @@ -0,0 +1,260 @@ +module Gitlab + # Checks if a set of migrations requires downtime or not. + class EeCompatCheck + EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze + + attr_reader :ce_branch, :check_dir, :ce_repo + + def initialize(branch:, check_dir:, ce_repo: nil) + @ce_branch = branch + @check_dir = check_dir + @ce_repo = ce_repo || 'https://gitlab.com/gitlab-org/gitlab-ce.git' + end + + def check + ensure_ee_repo + delete_patches + + generate_patch(ce_branch, ce_patch_full_path) + + Dir.chdir(check_dir) do + step("In the #{check_dir} directory") + + step("Pulling latest master", %w[git pull --ff-only origin master]) + + status = catch(:halt_check) do + ce_branch_compat_check! + + delete_ee_branch_locally + + ee_branch_presence_check! + + ee_branch_compat_check! + end + + delete_ee_branch_locally + delete_patches + + if status.nil? + true + else + false + end + end + end + + private + + def ensure_ee_repo + if Dir.exist?(check_dir) + step("#{check_dir} already exists") + else + cmd = %W[git clone --branch master --single-branch --depth 1 #{EE_REPO} #{check_dir}] + step("Cloning #{EE_REPO} into #{check_dir}", cmd) + end + end + + def ce_branch_compat_check! + cmd = %W[git apply --check #{ce_patch_full_path}] + status = step("Checking if #{ce_patch_name} applies cleanly to EE/master", cmd) + + if status.zero? + puts ce_applies_cleanly_msg(ce_branch) + throw(:halt_check) + end + end + + def ee_branch_presence_check! + status = step("Fetching origin/#{ee_branch}", %W[git fetch origin #{ee_branch}]) + + unless status.zero? + puts + puts ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg + + throw(:halt_check, :ko) + end + end + + def ee_branch_compat_check! + step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD]) + + generate_patch(ee_branch, ee_patch_full_path) + cmd = %W[git apply --check #{ee_patch_full_path}] + status = step("Checking if #{ee_patch_name} applies cleanly to EE/master", cmd) + + unless status.zero? + puts + puts ee_branch_doesnt_apply_cleanly_msg + + throw(:halt_check, :ko) + end + + puts + puts ee_applies_cleanly_msg + end + + def generate_patch(branch, filepath) + FileUtils.rm(filepath, force: true) + + depth = 0 + loop do + depth += 10 + step("Fetching origin/master", %W[git fetch origin master --depth=#{depth}]) + status = step("Finding merge base with master", %W[git merge-base FETCH_HEAD #{branch}]) + + break if status.zero? || depth > 500 + end + + raise "#{branch} is too far behind master, please rebase it!" if depth > 500 + + step("Generating the patch against master") + output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout]) + throw(:halt_check, :ko) unless status.zero? + + File.open(filepath, 'w+') { |f| f.write(output) } + throw(:halt_check, :ko) unless File.exist?(filepath) + end + + def delete_ee_branch_locally + step("Checking out origin/master", %w[git checkout master]) + step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}]) + end + + def delete_patches + step("Deleting #{ce_patch_full_path}") + FileUtils.rm(ce_patch_full_path, force: true) + + step("Deleting #{ee_patch_full_path}") + FileUtils.rm(ee_patch_full_path, force: true) + end + + def ce_patch_name + @ce_patch_name ||= "#{ce_branch}.patch" + end + + def ce_patch_full_path + @ce_patch_full_path ||= File.expand_path(ce_patch_name, check_dir) + end + + def ee_branch + @ee_branch ||= "#{ce_branch}-ee" + end + + def ee_patch_name + @ee_patch_name ||= "#{ee_branch}.patch" + end + + def ee_patch_full_path + @ee_patch_full_path ||= File.expand_path(ee_patch_name, check_dir) + end + + def step(desc, cmd = nil) + puts "\n=> #{desc}\n" + + if cmd + puts "\n$ #{cmd.join(' ')}" + command(cmd) + end + end + + def command(cmd) + output, status = Gitlab::Popen.popen(cmd) + puts output + + status + end + + def ce_applies_cleanly_msg(ce_branch) + <<-MSG.strip_heredoc + ================================================================= + 🎉 Congratulations!! 🎉 + + The #{ce_branch} branch applies cleanly to EE/master! + + Much ❤️!! + =================================================================\n + MSG + end + + def ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg + <<-MSG.strip_heredoc + ================================================================= + 💥 Oh no! 💥 + + The #{ce_branch} branch does not apply cleanly to the current + EE/master, and no #{ee_branch} branch was found in the EE repository. + + Please create a #{ee_branch} branch that includes changes from + #{ce_branch} but also specific changes than can be applied cleanly + to EE/master. + + There are different ways to create such branch: + + 1. Create a new branch based on the CE branch and rebase it on top of EE/master + + # In the EE repo + $ git fetch #{ce_repo} #{ce_branch} + $ git checkout -b #{ee_branch} FETCH_HEAD + + # You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE" commit + # before rebasing to limit the conflicts-resolving steps during the rebase + $ git fetch origin + $ git rebase origin/master + + At this point you will likely have conflicts. + Solve them, and continue/finish the rebase. + + You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE". + + 2. Create a new branch from master and cherry-pick your CE commits + + # In the EE repo + $ git fetch origin + $ git checkout -b #{ee_branch} FETCH_HEAD + $ git fetch #{ce_repo} #{ce_branch} + $ git cherry-pick SHA # Repeat for all the commits you want to pick + + You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE" commit. + + Don't forget to push your branch to #{EE_REPO}: + + # In the EE repo + $ git push origin #{ee_branch} + + You can then retry this failed build, and hopefully it should pass. + + Stay 💪 ! + =================================================================\n + MSG + end + + def ee_branch_doesnt_apply_cleanly_msg + <<-MSG.strip_heredoc + ================================================================= + 💥 Oh no! 💥 + + The #{ce_branch} does not apply cleanly to the current + EE/master, and even though a #{ee_branch} branch exists in the EE + repository, it does not apply cleanly either to EE/master! + + Please update the #{ee_branch}, push it again to #{EE_REPO}, and + retry this build. + + Stay 💪 ! + =================================================================\n + MSG + end + + def ee_applies_cleanly_msg + <<-MSG.strip_heredoc + ================================================================= + 🎉 Congratulations!! 🎉 + + The #{ee_branch} branch applies cleanly to EE/master! + + Much ❤️!! + =================================================================\n + MSG + end + end +end diff --git a/lib/tasks/ce_to_ee_merge_check.rake b/lib/tasks/ce_to_ee_merge_check.rake deleted file mode 100644 index 424e7883060..00000000000 --- a/lib/tasks/ce_to_ee_merge_check.rake +++ /dev/null @@ -1,4 +0,0 @@ -desc 'Checks if the branch would apply cleanly to EE' -task ce_to_ee_merge_check: :environment do - Rake::Task['gitlab:dev:ce_to_ee_merge_check'].invoke -end diff --git a/lib/tasks/ee_compat_check.rake b/lib/tasks/ee_compat_check.rake new file mode 100644 index 00000000000..f494fa5c5c2 --- /dev/null +++ b/lib/tasks/ee_compat_check.rake @@ -0,0 +1,4 @@ +desc 'Checks if the branch would apply cleanly to EE' +task ee_compat_check: :environment do + Rake::Task['gitlab:dev:ee_compat_check'].invoke +end diff --git a/lib/tasks/gitlab/dev.rake b/lib/tasks/gitlab/dev.rake index 47bdb2d32d2..5ee99dfc810 100644 --- a/lib/tasks/gitlab/dev.rake +++ b/lib/tasks/gitlab/dev.rake @@ -1,106 +1,21 @@ namespace :gitlab do namespace :dev do desc 'Checks if the branch would apply cleanly to EE' - task ce_to_ee_merge_check: :environment do + task ee_compat_check: :environment do return if defined?(Gitlab::License) return unless ENV['CI'] - ce_repo = ENV['CI_BUILD_REPO'] - ce_branch = ENV['CI_BUILD_REF_NAME'] + success = + Gitlab::EeCompatCheck.new( + branch: ENV['CI_BUILD_REF_NAME'], + check_dir: File.expand_path('ee-compat-check', __dir__), + ce_repo: ENV['CI_BUILD_REPO'] + ).check - ee_repo = 'https://gitlab.com/gitlab-org/gitlab-ee.git' - ee_branch = "#{ce_branch}-ee" - ee_dir = 'gitlab-ee-merge-check' - - puts "\n=> Cloning #{ee_repo} into #{ee_dir}\n" - `git clone #{ee_repo} #{ee_dir} --depth 1` - Dir.chdir(ee_dir) do - puts "\n => Fetching #{ce_repo}/#{ce_branch}\n" - `git fetch #{ce_repo} #{ce_branch} --depth 1` - - # Try to merge the current tested branch to EE/master... - puts "\n => Merging #{ce_repo}/#{ce_branch} into #{ee_repo}/master\n" - `git merge FETCH_HEAD` - - exit 0 if $?.success? - - # Check if the -ee branch exists... - puts "\n => Check if #{ee_repo}/#{ee_branch} exists\n" - `git rev-parse --verify #{ee_branch}` - - # The -ee doesn't exist - unless $?.success? - puts - puts <<-MSG.strip_heredoc - ================================================================= - The #{ce_branch} branch cannot be merged without conflicts to the - current EE/master, and no #{ee_branch} branch was detected in - the EE repository. - - Please create a #{ee_branch} branch that includes changes from - #{ce_branch} but also specific changes than can be applied cleanly - to EE/master. - - You can create this branch as follows: - - 1. In the EE repo: - $ git fetch origin - $ git fetch #{ce_repo} #{ce_branch} - $ git checkout -b #{ee_branch} FETCH_HEAD - $ git rebase origin/master - 2. At this point you will likely have conflicts, solve them, and - continue/finish the rebase. Note: You can squash the CE commits - before rebasing. - 3. You can squash all the original #{ce_branch} commits into a - single "Port of #{ce_branch} to EE". - 4. Push your branch to #{ee_repo}: - $ git push origin #{ee_branch} - =================================================================\n - MSG - - exit 1 - end - - # Try to merge the -ee branch to EE/master... - puts "\n => Merging #{ee_repo}/#{ee_branch} into #{ee_repo}/master\n" - `git merge #{ee_branch} master` - - # The -ee cannot be merged cleanly to EE/master... - unless $?.success? - puts - puts <<-MSG.strip_heredoc - ================================================================= - The #{ce_branch} branch cannot be merged without conflicts to - EE/master, and even though the #{ee_branch} branch exists in the EE - repository, it cannot be merged without conflicts to EE/master. - - Please update the #{ee_branch}, push it again to #{ee_repo}, and - retry this job. - =================================================================\n - MSG - - exit 2 - end - - puts "\n => Merging #{ce_repo}/#{ce_branch} into #{ee_repo}/master\n" - `git merge FETCH_HEAD` - exit 0 if $?.success? - - # The -ee can be merged cleanly to EE/master, but still - # cannot be merged cleanly to EE/master... - puts - puts <<-MSG.strip_heredoc - ================================================================= - The #{ce_branch} branch cannot be merged without conflicts to EE, and - even though the #{ee_branch} branch exists in the EE repository and - applies cleanly to EE/master, it doesn't prevent conflicts when - merging #{ce_branch} into EE. - - We may be in a complex situation here. - =================================================================\n - MSG - - exit 3 + if success + exit 0 + else + exit 1 end end end From 0ca0697a9c2501db23458aa8ef35a8b030625f93 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Fri, 21 Oct 2016 23:21:31 +0600 Subject: [PATCH 082/176] adds entry in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 518d0362d07..9207d98327c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method ## 8.13.0 (2016-10-22) - + - Removes extra line for empty issue description. (!7045) - Fix save button on project pipeline settings page. (!6955) - Avoid race condition when asynchronously removing expired artifacts. (!6881) - Improve Merge When Build Succeeds triggers and execute on pipeline success. (!6675) From 3a29ea9da0abd6cfd0788f6d717a08862ed6b062 Mon Sep 17 00:00:00 2001 From: Lemures Lemniscati Date: Sat, 22 Oct 2016 03:07:26 +0900 Subject: [PATCH 083/176] Fix documents and comments on Build API `scope`. #23146 #19131 --- CHANGELOG.md | 1 + doc/api/builds.md | 8 ++++---- lib/api/builds.rb | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc3dd569aea..fa9f4dc6091 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix: Backup restore doesn't clear cache - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method + - Fix documents and comments on Build API `scope` ## 8.13.0 (2016-10-22) diff --git a/doc/api/builds.md b/doc/api/builds.md index e40f198696d..0476cac0eda 100644 --- a/doc/api/builds.md +++ b/doc/api/builds.md @@ -11,10 +11,10 @@ GET /projects/:id/builds | Attribute | Type | Required | Description | |-----------|---------|----------|---------------------| | `id` | integer | yes | The ID of a project | -| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided | +| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided | ``` -curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/builds?scope%5B0%5D=pending&scope%5B1%5D=running' ``` Example of response @@ -132,10 +132,10 @@ GET /projects/:id/repository/commits/:sha/builds |-----------|---------|----------|---------------------| | `id` | integer | yes | The ID of a project | | `sha` | string | yes | The SHA id of a commit | -| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided | +| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all builds if none provided | ``` -curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds" +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" 'https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds?scope%5B0%5D=pending&scope%5B1%5D=running' ``` Example of response diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 52bdbcae5a8..7b00c5037f1 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -8,7 +8,7 @@ module API # # Parameters: # id (required) - The ID of a project - # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled; + # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; # if none provided showing all builds) # Example Request: # GET /projects/:id/builds @@ -25,7 +25,7 @@ module API # Parameters: # id (required) - The ID of a project # sha (required) - The SHA id of a commit - # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled; + # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; # if none provided showing all builds) # Example Request: # GET /projects/:id/repository/commits/:sha/builds From 68af55c3f93be79841957ff12b736e104fdc40de Mon Sep 17 00:00:00 2001 From: Daniel Klischies Date: Fri, 21 Oct 2016 18:38:11 +0000 Subject: [PATCH 084/176] Add a note regarding syncing the git submodule conf to CI doc. --- doc/user/project/new_ci_build_permissions_model.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index 8827b501901..608475116a1 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -254,6 +254,12 @@ test: This will make GitLab CI initialize (fetch) and update (checkout) all your submodules recursively. +If git does not use the newly added relative URLs but still uses your old URLs, +you might need to add `git submodule sync --recursive` to your `.gitlab-ci.yml`, +prior to running `git submodule update --init --recursive`. This transfers the +changes from your `.gitmodules` file into the `.git` folder, which is kept by +runners between runs. + In case your environment or your Docker image doesn't have Git installed, you have to either ask your Administrator or install the missing dependency yourself: From 94ceadb4a32a4a5128d43983206518d3159354e0 Mon Sep 17 00:00:00 2001 From: Winnie Date: Fri, 21 Oct 2016 20:18:03 +0000 Subject: [PATCH 085/176] Document link syntax introduced by !5586 --- doc/user/markdown.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 56e5b802a52..7a7a0b864bd 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -501,6 +501,10 @@ There are two ways to create links, inline-style and reference-style. [I'm a reference-style link][Arbitrary case-insensitive reference text] [I'm a relative reference to a repository file](LICENSE) + + [I am an absolute reference within the repository](/doc/user/markdown.md) + + [I link to the Milestones page](/../milestones) [You can use numbers for reference-style link definitions][1] @@ -518,6 +522,10 @@ There are two ways to create links, inline-style and reference-style. [I'm a relative reference to a repository file](LICENSE)[^1] +[I am an absolute reference within the repository](/doc/user/markdown.md) + +[I link to the Milestones page](/../milestones) + [You can use numbers for reference-style link definitions][1] Or leave it empty and use the [link text itself][] From 7664c76a165e4f151bec3e5f3a25b5eb1f962bb4 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 21 Oct 2016 21:40:18 +0100 Subject: [PATCH 086/176] Removed append logic Updated test --- app/assets/javascripts/blob/template_selector.js.es6 | 7 +------ .../templates/issuable_template_selector.js.es6 | 12 +++++------- spec/features/projects/issuable_templates_spec.rb | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/blob/template_selector.js.es6 b/app/assets/javascripts/blob/template_selector.js.es6 index 4e309e480b0..2d5c6ade053 100644 --- a/app/assets/javascripts/blob/template_selector.js.es6 +++ b/app/assets/javascripts/blob/template_selector.js.es6 @@ -68,14 +68,10 @@ // To be implemented on the extending class // e.g. // Api.gitignoreText item.name, @requestFileSuccess.bind(@) - requestFileSuccess(file, { skipFocus, append } = {}) { + requestFileSuccess(file, { skipFocus } = {}) { const oldValue = this.editor.getValue(); let newValue = file.content; - if (append && oldValue.length && oldValue !== newValue) { - newValue = oldValue + '\n\n' + newValue; - } - this.editor.setValue(newValue, 1); if (!skipFocus) this.editor.focus(); @@ -99,4 +95,3 @@ global.TemplateSelector = TemplateSelector; })(window.gl || ( window.gl = {})); - diff --git a/app/assets/javascripts/templates/issuable_template_selector.js.es6 b/app/assets/javascripts/templates/issuable_template_selector.js.es6 index bd4e3c3d00d..fa1b79c8415 100644 --- a/app/assets/javascripts/templates/issuable_template_selector.js.es6 +++ b/app/assets/javascripts/templates/issuable_template_selector.js.es6 @@ -32,24 +32,22 @@ this.currentTemplate = currentTemplate; if (err) return; // Error handled by global AJAX error handler this.stopLoadingSpinner(); - this.setInputValueToTemplateContent(true); + this.setInputValueToTemplateContent(); }); return; } - setInputValueToTemplateContent(append) { + setInputValueToTemplateContent() { // `this.requestFileSuccess` sets the value of the description input field - // to the content of the template selected. If `append` is true, the - // template content will be appended to the previous value of the field, - // separated by a blank line if the previous value is non-empty. + // to the content of the template selected. if (this.titleInput.val() === '') { // If the title has not yet been set, focus the title input and // skip focusing the description input by setting `true` as the // `skipFocus` option to `requestFileSuccess`. - this.requestFileSuccess(this.currentTemplate, {skipFocus: true, append}); + this.requestFileSuccess(this.currentTemplate, {skipFocus: true}); this.titleInput.focus(); } else { - this.requestFileSuccess(this.currentTemplate, {skipFocus: false, append}); + this.requestFileSuccess(this.currentTemplate, {skipFocus: false}); } return; } diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb index d886909ce85..2f377312ea5 100644 --- a/spec/features/projects/issuable_templates_spec.rb +++ b/spec/features/projects/issuable_templates_spec.rb @@ -77,7 +77,7 @@ feature 'issuable templates', feature: true, js: true do scenario 'user selects "bug" template' do select_template 'bug' wait_for_ajax - preview_template("#{prior_description}\n\n#{template_content}") + preview_template("#{template_content}") save_changes end end From fadaba000a2faba191177793ff6aba5a0ecdbe24 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Fri, 21 Oct 2016 11:31:15 -0700 Subject: [PATCH 087/176] Add an example of how to run the backups when using docker to the docs --- doc/raketasks/backup_restore.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 26baffdf792..fc0cd1b8af2 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -30,6 +30,10 @@ Use this if you've installed GitLab from source: ``` sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ``` +If you are running GitLab within a Docker container, you can run the backup from the host: +``` +docker -t exec gitlab-rake gitlab:backup:create +``` You can specify that portions of the application data be skipped using the environment variable `SKIP`. You can skip: From d79c41e7629b6e983c4cf5c0670a1fbab37528ed Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 21 Oct 2016 22:28:39 -0700 Subject: [PATCH 088/176] Fix bug where e-mails were not being sent out via Sidekiq By default, ActionMailer uses the "mailers" queue, but this entry was not included in the list of queues for Sidekiq to use. For more details: * https://github.com/plataformatec/devise/wiki/How-To:-Send-devise-emails-in-background-(Resque,-Sidekiq-and-Delayed::Job) * http://guides.rubyonrails.org/active_job_basics.html --- config/sidekiq_queues.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index c2e880e891f..f36fe893fd0 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -27,6 +27,7 @@ - [gitlab_shell, 2] - [email_receiver, 2] - [emails_on_push, 2] + - [mailers, 2] - [repository_fork, 1] - [repository_import, 1] - [project_service, 1] From 0890aeb61a5378ec3bb98511de236ee01eee8711 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 22 Oct 2016 01:59:40 -0700 Subject: [PATCH 089/176] Fix error in generating labels Attempting to generate default set of labels would result in an error: ArgumentError: wrong number of arguments (given 1, expected 0) Closes #23649 --- CHANGELOG.md | 3 +++ lib/gitlab/issues_labels.rb | 2 +- .../projects/labels_controller_spec.rb | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c39ddca7cf..bfc6a586ade 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ Please view this file on the master branch, on stable branches it's out of date. - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method +## 8.13.1 (unreleased) + - Fix error in generating labels + ## 8.13.0 (2016-10-22) - Fix save button on project pipeline settings page. (!6955) diff --git a/lib/gitlab/issues_labels.rb b/lib/gitlab/issues_labels.rb index 01a2c19ab23..dbc759367eb 100644 --- a/lib/gitlab/issues_labels.rb +++ b/lib/gitlab/issues_labels.rb @@ -19,7 +19,7 @@ module Gitlab ] labels.each do |params| - ::Labels::FindOrCreateService.new(project.owner, project).execute(params) + ::Labels::FindOrCreateService.new(project.owner, project, params).execute end end end diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 622ab154493..7ba4406c1f6 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -70,4 +70,19 @@ describe Projects::LabelsController do get :index, namespace_id: project.namespace.to_param, project_id: project.to_param end end + + describe 'POST #generate' do + let(:admin) { create(:admin) } + let(:project) { create(:empty_project) } + + before do + sign_in(admin) + end + + it 'creates labels' do + post :generate, namespace_id: project.namespace.to_param, project_id: project.to_param + + expect(response.code).to eq(302) + end + end end From c0eb2cbbb4b14e3abb843a65d685bee400b5fffe Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Sat, 22 Oct 2016 12:42:19 +0100 Subject: [PATCH 090/176] Stop clearing the database cache on rake cache:clear --- lib/tasks/cache.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index a95a3455a4a..78ae187817a 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -29,5 +29,5 @@ namespace :cache do task all: [:db, :redis] end - task clear: 'cache:clear:all' + task clear: 'cache:clear:redis' end From e6968964870286af5ce6a1f7cf1152c057fd5c11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Sat, 22 Oct 2016 13:46:23 +0200 Subject: [PATCH 091/176] Fix status code expectation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- spec/controllers/projects/labels_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 7ba4406c1f6..41df63d445a 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -82,7 +82,7 @@ describe Projects::LabelsController do it 'creates labels' do post :generate, namespace_id: project.namespace.to_param, project_id: project.to_param - expect(response.code).to eq(302) + expect(response).to have_http_status(302) end end end From 3b7ca69e530d684faf89a8dc1ea48c26eed5ccb7 Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Sat, 22 Oct 2016 14:20:13 -0500 Subject: [PATCH 092/176] Fix sign in page Forgot your password link overlap --- CHANGELOG.md | 1 + app/assets/stylesheets/pages/login.scss | 6 ++++++ app/views/devise/sessions/_new_base.html.haml | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c39ddca7cf..d4a975e6c3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix: Backup restore doesn't clear cache - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method + - Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens ## 8.13.0 (2016-10-22) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index bdb13bee178..51048649383 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -255,6 +255,12 @@ .new_user { position: relative; padding-bottom: 35px; + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + .forgot-password { + float: none !important; + margin-top: 5px; + } + } } .move-submit-down { diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml index 525e7d99d71..5fd896f6835 100644 --- a/app/views/devise/sessions/_new_base.html.haml +++ b/app/views/devise/sessions/_new_base.html.haml @@ -12,5 +12,5 @@ %label{for: "user_remember_me"} = f.check_box :remember_me %span Remember me - .pull-right + .pull-right.forgot-password = link_to "Forgot your password?", new_password_path(resource_name) From 2eb452402f6d9ac7dc6a5b083dab824fa9767c4d Mon Sep 17 00:00:00 2001 From: Bernardo Anderson Date: Sat, 22 Oct 2016 14:38:58 -0500 Subject: [PATCH 093/176] Add empty line before media query --- app/assets/stylesheets/pages/login.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 51048649383..02c4bbf3710 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -255,6 +255,7 @@ .new_user { position: relative; padding-bottom: 35px; + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { .forgot-password { float: none !important; From 204da00ea0dc52c6746b160fd698e07f55a7fc00 Mon Sep 17 00:00:00 2001 From: Jared Ready Date: Thu, 29 Sep 2016 20:18:46 -0500 Subject: [PATCH 094/176] Move spec/mailers/shared/notify.rb to spec/support --- spec/mailers/emails/builds_spec.rb | 1 - spec/mailers/emails/merge_requests_spec.rb | 1 - spec/mailers/emails/profile_spec.rb | 1 - spec/mailers/notify_spec.rb | 1 - .../shared/notify.rb => support/notify_shared_examples.rb} | 0 5 files changed, 4 deletions(-) rename spec/{mailers/shared/notify.rb => support/notify_shared_examples.rb} (100%) diff --git a/spec/mailers/emails/builds_spec.rb b/spec/mailers/emails/builds_spec.rb index 0df89938e97..d968096783c 100644 --- a/spec/mailers/emails/builds_spec.rb +++ b/spec/mailers/emails/builds_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify do include EmailSpec::Matchers diff --git a/spec/mailers/emails/merge_requests_spec.rb b/spec/mailers/emails/merge_requests_spec.rb index 4d3811af254..e22858d1d8f 100644 --- a/spec/mailers/emails/merge_requests_spec.rb +++ b/spec/mailers/emails/merge_requests_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify, "merge request notifications" do include EmailSpec::Matchers diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb index 781472d0c00..14bc062ef12 100644 --- a/spec/mailers/emails/profile_spec.rb +++ b/spec/mailers/emails/profile_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify do include EmailSpec::Matchers diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index c8207e58e90..f5f3f58613d 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -1,6 +1,5 @@ require 'spec_helper' require 'email_spec' -require 'mailers/shared/notify' describe Notify do include EmailSpec::Helpers diff --git a/spec/mailers/shared/notify.rb b/spec/support/notify_shared_examples.rb similarity index 100% rename from spec/mailers/shared/notify.rb rename to spec/support/notify_shared_examples.rb From b1ce2eb1e5f6a4a5b413381489fbb7e63ff3e1e5 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Mon, 23 May 2016 14:19:27 +0500 Subject: [PATCH 095/176] refactor(email): use setter method instead AR callbacks --- CHANGELOG.md | 1 + app/models/email.rb | 6 ++---- spec/models/email_spec.rb | 5 +++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b25431278bd..6b8ecb68c8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method - Fix documents and comments on Build API `scope` + - Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov) ## 8.13.1 (unreleased) - Fix error in generating labels diff --git a/app/models/email.rb b/app/models/email.rb index 32a412ab878..826d4f16edb 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -7,10 +7,8 @@ class Email < ActiveRecord::Base validates :email, presence: true, uniqueness: true, email: true validate :unique_email, if: ->(email) { email.email_changed? } - before_validation :cleanup_email - - def cleanup_email - self.email = self.email.downcase.strip + def email=(value) + write_attribute(:email, value.downcase.strip) end def unique_email diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb index d9df9e0f907..fe4de1b2afb 100644 --- a/spec/models/email_spec.rb +++ b/spec/models/email_spec.rb @@ -6,4 +6,9 @@ describe Email, models: true do subject { build(:email) } end end + + it 'normalize email value' do + expect(described_class.new(email: ' inFO@exAMPLe.com ').email) + .to eq 'info@example.com' + end end From 4ea1973f5971f9c72230779d28f228b5da1148af Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 23 Oct 2016 10:31:18 -0700 Subject: [PATCH 096/176] Expire and build repository cache after project import After a project import, there's a chance that the UI checks the branch count before the project has been imported. This change causes more of the keys to be flushed after an import and forces a rebuild of the repository cache. Closes #13518 --- CHANGELOG.md | 1 + app/models/repository.rb | 18 ++++++++++++++---- spec/models/repository_spec.rb | 21 +++++---------------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b25431278bd..9999864662a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.13.1 (unreleased) - Fix error in generating labels + - Expire and build repository cache after project import ## 8.13.0 (2016-10-22) diff --git a/app/models/repository.rb b/app/models/repository.rb index 1b7f20a2134..c0460e3952f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -419,6 +419,17 @@ class Repository @exists = nil end + # expire cache that doesn't depend on repository data (when expiring) + def expire_content_cache + expire_tags_cache + expire_tag_count_cache + expire_branches_cache + expire_branch_count_cache + expire_root_ref_cache + expire_emptiness_caches + expire_exists_cache + end + # Runs code after a repository has been created. def after_create expire_exists_cache @@ -473,14 +484,13 @@ class Repository end def before_import - expire_emptiness_caches - expire_exists_cache + expire_content_cache end # Runs code after a repository has been forked/imported. def after_import - expire_emptiness_caches - expire_exists_cache + expire_content_cache + build_cache end # Runs code after a new commit has been pushed. diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index f977cf73673..187a1bf2d79 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1146,28 +1146,17 @@ describe Repository, models: true do end describe '#before_import' do - it 'flushes the emptiness cachess' do - expect(repository).to receive(:expire_emptiness_caches) - - repository.before_import - end - - it 'flushes the exists cache' do - expect(repository).to receive(:expire_exists_cache) + it 'flushes the repository caches' do + expect(repository).to receive(:expire_content_cache) repository.before_import end end describe '#after_import' do - it 'flushes the emptiness cachess' do - expect(repository).to receive(:expire_emptiness_caches) - - repository.after_import - end - - it 'flushes the exists cache' do - expect(repository).to receive(:expire_exists_cache) + it 'flushes and builds the cache' do + expect(repository).to receive(:expire_content_cache) + expect(repository).to receive(:build_cache) repository.after_import end From 8a3710f452dc2192c936b1b418d2e1a90f5ae65f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 23 Oct 2016 10:45:08 -0700 Subject: [PATCH 097/176] Remove duplicate code in repository cache clearing --- app/models/repository.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index c0460e3952f..4ae9c20726f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -445,14 +445,7 @@ class Repository expire_cache if exists? - # expire cache that don't depend on repository data (when expiring) - expire_tags_cache - expire_tag_count_cache - expire_branches_cache - expire_branch_count_cache - expire_root_ref_cache - expire_emptiness_caches - expire_exists_cache + expire_content_cache repository_event(:remove_repository) end From 59ed1d3cbbf6c89fde202889af798fc189035313 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 23 Oct 2016 21:17:23 -0700 Subject: [PATCH 098/176] Fix reply-by-email not working due to queue name mismatch mail_room was configured to deliver mail to the `incoming_email` queue while `EmailReceiveWorker` was reading the `email_receiver` queue. Adds a migration that repeats the work of a previous migration to ensure all mails that wound up in the old queue get processed. Closes #23689 --- CHANGELOG.md | 1 + config/mail_room.yml | 2 +- ...317_migrate_mailroom_queue_from_default.rb | 63 +++++++++++++++++++ db/schema.rb | 2 +- 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index b25431278bd..99f03536f01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.13.1 (unreleased) - Fix error in generating labels + - Fix reply-by-email not working due to queue name mismatch ## 8.13.0 (2016-10-22) diff --git a/config/mail_room.yml b/config/mail_room.yml index c639f8260aa..68697bd1dc4 100644 --- a/config/mail_room.yml +++ b/config/mail_room.yml @@ -25,7 +25,7 @@ :delivery_options: :redis_url: <%= config[:redis_url].to_json %> :namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %> - :queue: incoming_email + :queue: email_receiver :worker: EmailReceiverWorker :arbitration_method: redis diff --git a/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb b/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb new file mode 100644 index 00000000000..06d07bdb835 --- /dev/null +++ b/db/migrate/20161024042317_migrate_mailroom_queue_from_default.rb @@ -0,0 +1,63 @@ +require 'json' + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class MigrateMailroomQueueFromDefault < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + + DOWNTIME_REASON = <<-EOF + Moving Sidekiq jobs from queues requires Sidekiq to be stopped. Not stopping + Sidekiq will result in the loss of jobs that are scheduled after this + migration completes. + EOF + + disable_ddl_transaction! + + # Jobs for which the queue names have been changed (e.g. multiple workers + # using the same non-default queue). + # + # The keys are the old queue names, the values the jobs to move and their new + # queue names. + RENAMED_QUEUES = { + incoming_email: { + 'EmailReceiverWorker' => :email_receiver + } + } + + def up + Sidekiq.redis do |redis| + RENAMED_QUEUES.each do |queue, jobs| + migrate_from_queue(redis, queue, jobs) + end + end + end + + def down + Sidekiq.redis do |redis| + RENAMED_QUEUES.each do |dest_queue, jobs| + jobs.each do |worker, from_queue| + migrate_from_queue(redis, from_queue, worker => dest_queue) + end + end + end + end + + def migrate_from_queue(redis, queue, job_mapping) + while job = redis.lpop("queue:#{queue}") + payload = JSON.load(job) + new_queue = job_mapping[payload['class']] + + # If we have no target queue to migrate to we're probably dealing with + # some ancient job for which the worker no longer exists. In that case + # there's no sane option we can take, other than just dropping the job. + next unless new_queue + + payload['queue'] = new_queue + + redis.lpush("queue:#{new_queue}", JSON.dump(payload)) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f5c01511195..02282b0f666 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: 20161019213545) do +ActiveRecord::Schema.define(version: 20161024042317) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From f79f3a1dd621362b0894eff0a54f220f8415a2e0 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 6 Sep 2016 10:35:34 +0530 Subject: [PATCH 099/176] Fix branch protection API. 1. Previously, we were not removing existing access levels before creating new ones. This is not a problem for EE, but _is_ for CE, since we restrict the number of access levels in CE to 1. 2. The correct approach is: CE -> delete all access levels before updating a protected branch EE -> delete developer access levels if "developers_can_{merge,push}" is switched off 3. The dispatch is performed by checking if a "length: 1" validation is present on the access levels or not. 4. Another source of problems was that we didn't put multiple queries in a transaction. If the `destroy_all` passes, but the `update` fails, we should have a rollback. 5. Modifying the API to provide users direct access to CRUD access levels will make things a lot simpler. 6. Create `create/update` services separately for this API, which perform the necessary data translation, before calling the regular `create/update` services. The translation code was getting too large for the API endpoint itself, so this move makes sense. --- .../concerns/protected_branch_access.rb | 5 + .../protected_branches/api_create_service.rb | 30 +++ .../protected_branches/api_update_service.rb | 79 +++++++ lib/api/branches.rb | 47 ++-- spec/requests/api/branches_spec.rb | 218 +++++++++++------- 5 files changed, 267 insertions(+), 112 deletions(-) create mode 100644 app/services/protected_branches/api_create_service.rb create mode 100644 app/services/protected_branches/api_update_service.rb diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb index 5a7b36070e7..dd6b34d934b 100644 --- a/app/models/concerns/protected_branch_access.rb +++ b/app/models/concerns/protected_branch_access.rb @@ -1,6 +1,11 @@ module ProtectedBranchAccess extend ActiveSupport::Concern + included do + scope :master, -> () { where(access_level: Gitlab::Access::MASTER) } + scope :developer, -> () { where(access_level: Gitlab::Access::DEVELOPER) } + end + def humanize self.class.human_access_levels[self.access_level] end diff --git a/app/services/protected_branches/api_create_service.rb b/app/services/protected_branches/api_create_service.rb new file mode 100644 index 00000000000..a28056035b8 --- /dev/null +++ b/app/services/protected_branches/api_create_service.rb @@ -0,0 +1,30 @@ +# The protected branches API still uses the `developers_can_push` and `developers_can_merge` +# flags for backward compatibility, and so performs translation between that format and the +# internal data model (separate access levels). The translation code is non-trivial, and so +# lives in this service. +module ProtectedBranches + class ApiCreateService < BaseService + def initialize(project, user, params, developers_can_push:, developers_can_merge:) + super(project, user, params) + @developers_can_merge = developers_can_merge + @developers_can_push = developers_can_push + end + + def execute + if @developers_can_push + @params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) + else + @params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) + end + + if @developers_can_merge + @params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) + else + @params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) + end + + service = ProtectedBranches::CreateService.new(@project, @current_user, @params) + service.execute + end + end +end diff --git a/app/services/protected_branches/api_update_service.rb b/app/services/protected_branches/api_update_service.rb new file mode 100644 index 00000000000..41d3d413caa --- /dev/null +++ b/app/services/protected_branches/api_update_service.rb @@ -0,0 +1,79 @@ +# The protected branches API still uses the `developers_can_push` and `developers_can_merge` +# flags for backward compatibility, and so performs translation between that format and the +# internal data model (separate access levels). The translation code is non-trivial, and so +# lives in this service. +module ProtectedBranches + class ApiUpdateService < BaseService + def initialize(project, user, params, developers_can_push:, developers_can_merge:) + super(project, user, params) + @developers_can_merge = developers_can_merge + @developers_can_push = developers_can_push + end + + def execute(protected_branch) + @protected_branch = protected_branch + + protected_branch.transaction do + # If a protected branch can have only a single access level (CE), delete it to + # make room for the new access level. If a protected branch can have more than + # one access level (EE), only remove the relevant access levels. If we don't do this, + # we'll have a failed validation. + if protected_branch_restricted_to_single_access_level? + delete_redundant_ce_access_levels + else + delete_redundant_ee_access_levels + end + + if @developers_can_push + params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) + elsif @developers_can_push == false + params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) + end + + if @developers_can_merge + params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) + elsif @developers_can_merge == false + params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) + end + + service = ProtectedBranches::UpdateService.new(@project, @current_user, @params) + service.execute(protected_branch) + end + end + + private + + def delete_redundant_ce_access_levels + if @developers_can_merge || @developers_can_merge == false + @protected_branch.merge_access_levels.destroy_all + end + + if @developers_can_push || @developers_can_push == false + @protected_branch.push_access_levels.destroy_all + end + end + + def delete_redundant_ee_access_levels + if @developers_can_merge + @protected_branch.merge_access_levels.developer.destroy_all + elsif @developers_can_merge == false + @protected_branch.merge_access_levels.developer.destroy_all + @protected_branch.merge_access_levels.master.destroy_all + end + + if @developers_can_push + @protected_branch.push_access_levels.developer.destroy_all + elsif @developers_can_push == false + @protected_branch.push_access_levels.developer.destroy_all + @protected_branch.push_access_levels.master.destroy_all + end + end + + def protected_branch_restricted_to_single_access_level? + length_validator = ProtectedBranch.validators_on(:push_access_levels).find do |validator| + validator.is_a? ActiveModel::Validations::LengthValidator + end + length_validator.options[:is] == 1 if length_validator + end + end +end diff --git a/lib/api/branches.rb b/lib/api/branches.rb index b615703df93..6941cc4aca6 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -57,40 +57,25 @@ module API developers_can_merge = to_boolean(params[:developers_can_merge]) developers_can_push = to_boolean(params[:developers_can_push]) - protected_branch_params = { - name: @branch.name + params = { + name: @branch.name, } - # If `developers_can_merge` is switched off, _all_ `DEVELOPER` - # merge_access_levels need to be deleted. - if developers_can_merge == false - protected_branch.merge_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all - end + service_args = [user_project, current_user, params, + developers_can_push: developers_can_push, + developers_can_merge: developers_can_merge] - # If `developers_can_push` is switched off, _all_ `DEVELOPER` - # push_access_levels need to be deleted. - if developers_can_push == false - protected_branch.push_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all - end + protected_branch = if protected_branch + ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch) + else + ProtectedBranches::ApiCreateService.new(*service_args).execute + end - protected_branch_params.merge!( - merge_access_levels_attributes: [{ - access_level: developers_can_merge ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER - }], - push_access_levels_attributes: [{ - access_level: developers_can_push ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER - }] - ) - - if protected_branch - service = ProtectedBranches::UpdateService.new(user_project, current_user, protected_branch_params) - service.execute(protected_branch) + if protected_branch.valid? + present @branch, with: Entities::RepoBranch, project: user_project else - service = ProtectedBranches::CreateService.new(user_project, current_user, protected_branch_params) - service.execute + render_api_error!(protected_branch.errors.full_messages, 422) end - - present @branch, with: Entities::RepoBranch, project: user_project end # Unprotect a single branch @@ -123,7 +108,7 @@ module API post ":id/repository/branches" do authorize_push_project result = CreateBranchService.new(user_project, current_user). - execute(params[:branch_name], params[:ref]) + execute(params[:branch_name], params[:ref]) if result[:status] == :success present result[:branch], @@ -142,10 +127,10 @@ module API # Example Request: # DELETE /projects/:id/repository/branches/:branch delete ":id/repository/branches/:branch", - requirements: { branch: /.+/ } do + requirements: { branch: /.+/ } do authorize_push_project result = DeleteBranchService.new(user_project, current_user). - execute(params[:branch]) + execute(params[:branch]) if result[:status] == :success { diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 3fd989dd7a6..905f762d578 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -48,92 +48,154 @@ describe API::API, api: true do end describe 'PUT /projects/:id/repository/branches/:branch/protect' do - it 'protects a single branch' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) - 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 - - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(true) - expect(json_response['developers_can_merge']).to eq(false) - end - - it 'protects a single branch and developers can merge' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_merge: true - - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(true) - end - - it 'protects a single branch and developers can push and merge' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_push: true, developers_can_merge: true - - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(true) - expect(json_response['developers_can_merge']).to eq(true) - end - - it 'protects a single branch and developers cannot push and merge' do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), - developers_can_push: 'tru', developers_can_merge: 'tr' - - expect(response).to have_http_status(200) - expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) - expect(json_response['protected']).to eq(true) - expect(json_response['developers_can_push']).to eq(false) - expect(json_response['developers_can_merge']).to eq(false) - end - - context 'on a protected branch' do - let(:protected_branch) { 'foo' } - - before do - project.repository.add_branch(user, protected_branch, 'master') - create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: protected_branch) - end - - it 'updates that a developer can push' do - put api("/projects/#{project.id}/repository/branches/#{protected_branch}/protect", user), - developers_can_push: false, developers_can_merge: false + context "when a protected branch doesn't already exist" do + it 'protects a single branch' do + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch) + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(false) expect(json_response['developers_can_merge']).to eq(false) end - it 'does not update that a developer can push' do - put api("/projects/#{project.id}/repository/branches/#{protected_branch}/protect", user), - developers_can_push: 'foobar', developers_can_merge: 'foo' + 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 expect(response).to have_http_status(200) - expect(json_response['name']).to eq(protected_branch) + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) expect(json_response['protected']).to eq(true) expect(json_response['developers_can_push']).to eq(true) + expect(json_response['developers_can_merge']).to eq(false) + end + + it 'protects a single branch and developers can merge' do + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), + developers_can_merge: true + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(false) + expect(json_response['developers_can_merge']).to eq(true) + end + + it 'protects a single branch and developers can push and merge' do + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), + developers_can_push: true, developers_can_merge: true + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(true) + expect(json_response['developers_can_merge']).to eq(true) + end + + it 'protects a single branch and developers cannot push and merge' do + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), + developers_can_push: 'tru', developers_can_merge: 'tr' + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(branch_name) + expect(json_response['commit']['id']).to eq(branch_sha) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(false) + expect(json_response['developers_can_merge']).to eq(false) + end + end + + context 'for an existing protected branch' do + before do + project.repository.add_branch(user, protected_branch.name, 'master') + end + + context "when developers can push and merge" do + let(:protected_branch) { create(:protected_branch, :developers_can_push, :developers_can_merge, project: project, name: 'protected_branch') } + + it 'updates that a developer cannot push or merge' do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_push: false, developers_can_merge: false + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(protected_branch.name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(false) + expect(json_response['developers_can_merge']).to eq(false) + end + + it "doesn't result in 0 access levels when 'developers_can_push' is switched off" do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_push: false + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(protected_branch.name) + expect(protected_branch.reload.push_access_levels.first).to be_present + expect(protected_branch.reload.push_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + end + + it "doesn't result in 0 access levels when 'developers_can_merge' is switched off" do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_merge: false + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(protected_branch.name) + expect(protected_branch.reload.merge_access_levels.first).to be_present + expect(protected_branch.reload.merge_access_levels.first.access_level).to eq(Gitlab::Access::MASTER) + end + end + + context "when developers cannot push or merge" do + let(:protected_branch) { create(:protected_branch, project: project, name: 'protected_branch') } + + it 'updates that a developer can push and merge' do + put api("/projects/#{project.id}/repository/branches/#{protected_branch.name}/protect", user), + developers_can_push: true, developers_can_merge: true + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(protected_branch.name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(true) + expect(json_response['developers_can_merge']).to eq(true) + end + end + end + + context "multiple API calls" do + it "returns success when `protect` is called twice" do + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(branch_name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(false) + expect(json_response['developers_can_merge']).to eq(false) + end + + it "returns success when `protect` is called twice with `developers_can_push` turned on" do + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_push: true + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(branch_name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(true) + expect(json_response['developers_can_merge']).to eq(false) + end + + it "returns success when `protect` is called twice with `developers_can_merge` turned on" do + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true + put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user), developers_can_merge: true + + expect(response).to have_http_status(200) + expect(json_response['name']).to eq(branch_name) + expect(json_response['protected']).to eq(true) + expect(json_response['developers_can_push']).to eq(false) expect(json_response['developers_can_merge']).to eq(true) end end @@ -147,12 +209,6 @@ describe API::API, api: true do put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user2) expect(response).to have_http_status(403) end - - it "returns success when protect branch again" do - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - put api("/projects/#{project.id}/repository/branches/#{branch_name}/protect", user) - expect(response).to have_http_status(200) - end end describe "PUT /projects/:id/repository/branches/:branch/unprotect" do From b116df7e9146baf70f04eb6ec1d19091278c14d5 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 6 Sep 2016 11:19:36 +0530 Subject: [PATCH 100/176] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b25431278bd..2538585da7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -387,6 +387,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix inconsistent checkbox alignment (ClemMakesApps) - Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger) - Adds response mime type to transaction metric action when it's not HTML + - Fix branch protection API !6215 - Fix hover leading space bug in pipeline graph !5980 - Avoid conflict with admin labels when importing GitHub labels - User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496 From cef10ef7d7a20a78d377f711867e361bb51fbaf2 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Wed, 14 Sep 2016 08:04:29 +0530 Subject: [PATCH 101/176] Implement review comments from @dbalexandre. 1. Don't have any EE-only code in CE. Ok to have CE-only code in EE. 2. Use `case` instead of `if/elsif` --- .../protected_branches/api_update_service.rb | 45 ++++--------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/app/services/protected_branches/api_update_service.rb b/app/services/protected_branches/api_update_service.rb index 41d3d413caa..1c27d32aad8 100644 --- a/app/services/protected_branches/api_update_service.rb +++ b/app/services/protected_branches/api_update_service.rb @@ -14,25 +14,19 @@ module ProtectedBranches @protected_branch = protected_branch protected_branch.transaction do - # If a protected branch can have only a single access level (CE), delete it to - # make room for the new access level. If a protected branch can have more than - # one access level (EE), only remove the relevant access levels. If we don't do this, - # we'll have a failed validation. - if protected_branch_restricted_to_single_access_level? - delete_redundant_ce_access_levels - else - delete_redundant_ee_access_levels - end + delete_redundant_access_levels - if @developers_can_push + case @developers_can_push + when true params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) - elsif @developers_can_push == false + when false params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) end - if @developers_can_merge + case @developers_can_merge + when true params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) - elsif @developers_can_merge == false + when false params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) end @@ -43,7 +37,7 @@ module ProtectedBranches private - def delete_redundant_ce_access_levels + def delete_redundant_access_levels if @developers_can_merge || @developers_can_merge == false @protected_branch.merge_access_levels.destroy_all end @@ -52,28 +46,5 @@ module ProtectedBranches @protected_branch.push_access_levels.destroy_all end end - - def delete_redundant_ee_access_levels - if @developers_can_merge - @protected_branch.merge_access_levels.developer.destroy_all - elsif @developers_can_merge == false - @protected_branch.merge_access_levels.developer.destroy_all - @protected_branch.merge_access_levels.master.destroy_all - end - - if @developers_can_push - @protected_branch.push_access_levels.developer.destroy_all - elsif @developers_can_push == false - @protected_branch.push_access_levels.developer.destroy_all - @protected_branch.push_access_levels.master.destroy_all - end - end - - def protected_branch_restricted_to_single_access_level? - length_validator = ProtectedBranch.validators_on(:push_access_levels).find do |validator| - validator.is_a? ActiveModel::Validations::LengthValidator - end - length_validator.options[:is] == 1 if length_validator - end end end From b803bc7bb8ad481790d01848902e80602e77da67 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Thu, 22 Sep 2016 20:38:05 +0530 Subject: [PATCH 102/176] Implement review comments from @DouweM. --- .../concerns/protected_branch_access.rb | 4 +-- .../protected_branches/api_create_service.rb | 25 +++++++++++-------- lib/api/branches.rb | 4 +-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb index dd6b34d934b..7fd0905ee81 100644 --- a/app/models/concerns/protected_branch_access.rb +++ b/app/models/concerns/protected_branch_access.rb @@ -2,8 +2,8 @@ module ProtectedBranchAccess extend ActiveSupport::Concern included do - scope :master, -> () { where(access_level: Gitlab::Access::MASTER) } - scope :developer, -> () { where(access_level: Gitlab::Access::DEVELOPER) } + scope :master, -> { where(access_level: Gitlab::Access::MASTER) } + scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) } end def humanize diff --git a/app/services/protected_branches/api_create_service.rb b/app/services/protected_branches/api_create_service.rb index a28056035b8..cbb99ede9f3 100644 --- a/app/services/protected_branches/api_create_service.rb +++ b/app/services/protected_branches/api_create_service.rb @@ -11,17 +11,22 @@ module ProtectedBranches end def execute - if @developers_can_push - @params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) - else - @params.merge!(push_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) - end + push_access_level = + if @developers_can_push + Gitlab::Access::DEVELOPER + else + Gitlab::Access::MASTER + end - if @developers_can_merge - @params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]) - else - @params.merge!(merge_access_levels_attributes: [{ access_level: Gitlab::Access::MASTER }]) - end + merge_access_level = + if @developers_can_merge + Gitlab::Access::DEVELOPER + else + Gitlab::Access::MASTER + end + + @params.merge!(push_access_levels_attributes: [{ access_level: push_access_level }], + merge_access_levels_attributes: [{ access_level: merge_access_level }]) service = ProtectedBranches::CreateService.new(@project, @current_user, @params) service.execute diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 6941cc4aca6..7feb47784b7 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -57,11 +57,11 @@ module API developers_can_merge = to_boolean(params[:developers_can_merge]) developers_can_push = to_boolean(params[:developers_can_push]) - params = { + protected_branch_params = { name: @branch.name, } - service_args = [user_project, current_user, params, + service_args = [user_project, current_user, protected_branch_params, developers_can_push: developers_can_push, developers_can_merge: developers_can_merge] From 1051087ac4efc3dbf45bd075e36af647d2b66d62 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 30 Sep 2016 13:14:46 +0530 Subject: [PATCH 103/176] Implement second round of review comments from @DouweM. - Pass `developers_and_merge` and `developers_can_push` in `params` instead of using keyword arguments. - Refactor a slightly complex boolean check to a simple `nil?` check. --- app/services/protected_branches/api_create_service.rb | 6 +++--- app/services/protected_branches/api_update_service.rb | 10 +++++----- lib/api/branches.rb | 9 +++------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/app/services/protected_branches/api_create_service.rb b/app/services/protected_branches/api_create_service.rb index cbb99ede9f3..d714a8aaf01 100644 --- a/app/services/protected_branches/api_create_service.rb +++ b/app/services/protected_branches/api_create_service.rb @@ -4,10 +4,10 @@ # lives in this service. module ProtectedBranches class ApiCreateService < BaseService - def initialize(project, user, params, developers_can_push:, developers_can_merge:) + def initialize(project, user, params) + @developers_can_merge = params.delete(:developers_can_merge) + @developers_can_push = params.delete(:developers_can_push) super(project, user, params) - @developers_can_merge = developers_can_merge - @developers_can_push = developers_can_push end def execute diff --git a/app/services/protected_branches/api_update_service.rb b/app/services/protected_branches/api_update_service.rb index 1c27d32aad8..c28bffee2f4 100644 --- a/app/services/protected_branches/api_update_service.rb +++ b/app/services/protected_branches/api_update_service.rb @@ -4,10 +4,10 @@ # lives in this service. module ProtectedBranches class ApiUpdateService < BaseService - def initialize(project, user, params, developers_can_push:, developers_can_merge:) + def initialize(project, user, params) + @developers_can_merge = params.delete(:developers_can_merge) + @developers_can_push = params.delete(:developers_can_push) super(project, user, params) - @developers_can_merge = developers_can_merge - @developers_can_push = developers_can_push end def execute(protected_branch) @@ -38,11 +38,11 @@ module ProtectedBranches private def delete_redundant_access_levels - if @developers_can_merge || @developers_can_merge == false + unless @developers_can_merge.nil? @protected_branch.merge_access_levels.destroy_all end - if @developers_can_push || @developers_can_push == false + unless @developers_can_push.nil? @protected_branch.push_access_levels.destroy_all end end diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 7feb47784b7..6d827448994 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -54,16 +54,13 @@ module API not_found!('Branch') unless @branch protected_branch = user_project.protected_branches.find_by(name: @branch.name) - developers_can_merge = to_boolean(params[:developers_can_merge]) - developers_can_push = to_boolean(params[:developers_can_push]) - protected_branch_params = { name: @branch.name, + developers_can_push: to_boolean(params[:developers_can_push]), + developers_can_merge: to_boolean(params[:developers_can_merge]) } - service_args = [user_project, current_user, protected_branch_params, - developers_can_push: developers_can_push, - developers_can_merge: developers_can_merge] + service_args = [user_project, current_user, protected_branch_params] protected_branch = if protected_branch ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch) From db0182e261936c3e800b546d307a3d3834ff9927 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Mon, 24 Oct 2016 11:32:09 +0530 Subject: [PATCH 104/176] Implement third round of review comments from @DouweM. Extract/mutate `params` in the `execute` method of the API services, rather than in `initialize`. --- app/services/protected_branches/api_create_service.rb | 10 ++-------- app/services/protected_branches/api_update_service.rb | 9 +++------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/app/services/protected_branches/api_create_service.rb b/app/services/protected_branches/api_create_service.rb index d714a8aaf01..f2040dfa03a 100644 --- a/app/services/protected_branches/api_create_service.rb +++ b/app/services/protected_branches/api_create_service.rb @@ -4,22 +4,16 @@ # lives in this service. module ProtectedBranches class ApiCreateService < BaseService - def initialize(project, user, params) - @developers_can_merge = params.delete(:developers_can_merge) - @developers_can_push = params.delete(:developers_can_push) - super(project, user, params) - end - def execute push_access_level = - if @developers_can_push + if params.delete(:developers_can_push) Gitlab::Access::DEVELOPER else Gitlab::Access::MASTER end merge_access_level = - if @developers_can_merge + if params.delete(:developers_can_merge) Gitlab::Access::DEVELOPER else Gitlab::Access::MASTER diff --git a/app/services/protected_branches/api_update_service.rb b/app/services/protected_branches/api_update_service.rb index c28bffee2f4..050cb3b738b 100644 --- a/app/services/protected_branches/api_update_service.rb +++ b/app/services/protected_branches/api_update_service.rb @@ -4,13 +4,10 @@ # lives in this service. module ProtectedBranches class ApiUpdateService < BaseService - def initialize(project, user, params) - @developers_can_merge = params.delete(:developers_can_merge) - @developers_can_push = params.delete(:developers_can_push) - super(project, user, params) - end - def execute(protected_branch) + @developers_can_push = params.delete(:developers_can_push) + @developers_can_merge = params.delete(:developers_can_merge) + @protected_branch = protected_branch protected_branch.transaction do From 884d49ea991322f6530b3281a980527cfb909bb7 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Mon, 24 Oct 2016 13:02:15 +0600 Subject: [PATCH 105/176] code formatting corrected --- app/views/projects/issues/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 6defd7834bf..bd629b5c519 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -53,7 +53,7 @@ .issue-details.issuable-details - .detail-page-description.content-block{class: ('hide-bottom-border' unless @issue.description.present? )} + .detail-page-description.content-block{ class: ('hide-bottom-border' unless @issue.description.present? ) } %h2.title = markdown_field(@issue, :title) - if @issue.description.present? From 2fc2be9b7b8b80905acccbb74e4788a7e8a03f55 Mon Sep 17 00:00:00 2001 From: Nur Rony Date: Mon, 24 Oct 2016 13:58:46 +0600 Subject: [PATCH 106/176] removes extra line for empty milestone description --- app/assets/stylesheets/framework/common.scss | 2 +- app/views/projects/milestones/show.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 0d00b6a0d9c..800e2dba018 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -373,4 +373,4 @@ table { border-top: 1px solid $border-color; } -.hide-bottom-border { border-bottom: none;} +.hide-bottom-border { border-bottom: none !important; } diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index c83818e9199..f9ba77e87b5 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -31,7 +31,7 @@ = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-danger" do Delete - .detail-page-description.milestone-detail + .detail-page-description.milestone-detail{ class: ('hide-bottom-border' unless @milestone.description.present? ) } %h2.title = markdown_field(@milestone, :title) %div From cc6491ef6405de855b071e128631df6fdb47db8e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 20 Oct 2016 12:43:08 +0100 Subject: [PATCH 107/176] Use root_url for issue boards user link Closes #23556 --- CHANGELOG.md | 2 +- app/views/projects/boards/components/_card.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f47b27d5aa..fb4dbe1062b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ Please view this file on the master branch, on stable branches it's out of date. - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method - Fix: Backup restore doesn't clear cache - - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method - Fix documents and comments on Build API `scope` @@ -44,6 +43,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Clarify documentation for Runners API (Gennady Trafimenkov) - The instrumentation for Banzai::Renderer has been restored - Change user & group landing page routing from /u/:username to /:username + - Fixed issue boards user link when in subdirectory - Added documentation for .gitattributes files - Move Pipeline Metrics to separate worker - AbstractReferenceFilter caches project_refs on RequestStore when active diff --git a/app/views/projects/boards/components/_card.html.haml b/app/views/projects/boards/components/_card.html.haml index d8f16022407..c6d718a1cd1 100644 --- a/app/views/projects/boards/components/_card.html.haml +++ b/app/views/projects/boards/components/_card.html.haml @@ -26,7 +26,7 @@ ":title" => "label.description", data: { container: 'body' } } {{ label.title }} - %a.has-tooltip{ ":href" => "'/' + issue.assignee.username", + %a.has-tooltip{ ":href" => "'#{root_path}' + issue.assignee.username", ":title" => "'Assigned to ' + issue.assignee.name", "v-if" => "issue.assignee", data: { container: 'body' } } From 9dbd5b3cfad10b214ae5ef27c39246bbb74a5077 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 24 Oct 2016 10:19:36 +0300 Subject: [PATCH 108/176] Revert "Change "Group#web_url" to return "/groups/twitter" rather than "/twitter"." This reverts commit c81ff152e08d58c13efbd50c40dd2e083ac65083. --- app/models/group.rb | 2 +- config/routes/group.rb | 39 ++++++++++++++++++--------------------- spec/models/group_spec.rb | 6 ------ 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index 6865e610718..00a595d2705 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -68,7 +68,7 @@ class Group < Namespace end def web_url - Gitlab::Routing.url_helpers.group_canonical_url(self) + Gitlab::Routing.url_helpers.group_url(self) end def human_name diff --git a/config/routes/group.rb b/config/routes/group.rb index 826048ba196..4838c9d91c6 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -12,26 +12,23 @@ constraints(GroupUrlConstrainer.new) do end end -scope constraints: { id: /[a-zA-Z.0-9_\-]+(? 'groups#show', as: :group_canonical end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 47f89f744cb..ac862055ebc 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -265,10 +265,4 @@ describe Group, models: true do members end - - describe '#web_url' do - it 'returns the canonical URL' do - expect(group.web_url).to include("groups/#{group.name}") - end - end end From 036fac06d18e82f0d0696bd1b350548bb47125e8 Mon Sep 17 00:00:00 2001 From: Linus G Thiel Date: Wed, 5 Oct 2016 21:58:34 +0200 Subject: [PATCH 109/176] Gracefully handle adding of no users to projects and groups - Disable {project, group} members submit button if no users If no users are selected, the submit button should be disabled. - Alert user when no users were added to {project, group}. When no users were selected for adding, an alert message is flashed that no users were added. - Also, this commit adds a feedback when users were actually added to a project, in symmetry with how group members are handled. Closes #22967, #23270. --- CHANGELOG.md | 1 + app/assets/javascripts/application.js | 5 +- .../groups/group_members_controller.rb | 4 ++ .../projects/project_members_controller.rb | 6 +- .../groups/group_members/index.html.haml | 3 + .../projects/project_members/index.html.haml | 3 + .../groups/group_members_controller_spec.rb | 54 ++++++++++++++++++ .../project_members_controller_spec.rb | 57 +++++++++++++++++++ 8 files changed, 130 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 909af5fc053..3fa0e67037e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Update Gitlab Shell to fix some problems with moving projects between storages - Cache rendered markdown in the database, rather than Redis - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references + - Better handle when no users were selected for adding to group or project. (Linus Thiel) - Simplify Mentionable concern instance methods - API: Ability to retrieve version information (Robert Schilling) - Fix permission for setting an issue's due date diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8a61669822c..17cbfd0e66f 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -83,14 +83,15 @@ }; // Disable button if text field is empty - window.disableButtonIfEmptyField = function(field_selector, button_selector) { + window.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) { + event_name = event_name || 'input'; var closest_submit, field; field = $(field_selector); closest_submit = field.closest('form').find(button_selector); if (rstrip(field.val()) === "") { closest_submit.disable(); } - return field.on('input', function() { + return field.on(event_name, function() { if (rstrip($(this).val()) === "") { return closest_submit.disable(); } else { diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 18cd800c619..3a373e4a946 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -21,6 +21,10 @@ class Groups::GroupMembersController < Groups::ApplicationController end def create + if params[:user_ids].empty? + return redirect_to group_group_members_path(@group), alert: 'No users specified.' + end + @group.add_users( params[:user_ids].split(','), params[:access_level], diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 2a07d154853..2bac48c5490 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -25,6 +25,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def create + if params[:user_ids].empty? + return redirect_to namespace_project_project_members_path(@project.namespace, @project), alert: 'No users specified.' + end + @project.team.add_users( params[:user_ids].split(','), params[:access_level], @@ -32,7 +36,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController current_user: current_user ) - redirect_to namespace_project_project_members_path(@project.namespace, @project) + redirect_to namespace_project_project_members_path(@project.namespace, @project), notice: 'Users were successfully added.' end def update diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index ebf9aca7700..b295ad0e182 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -29,3 +29,6 @@ %ul.content-list = render partial: 'shared/members/member', collection: @members, as: :member = paginate @members, theme: 'gitlab' + +:javascript + window.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index bdeb704b6da..3904647b608 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -26,3 +26,6 @@ = render 'team', members: @project_members = paginate @project_members, theme: "gitlab" + +:javascript + window.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index ad15b3f8f40..82eebe6f2d4 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -13,6 +13,60 @@ describe Groups::GroupMembersController do end end + describe '#create' do + let(:group) { create(:group, :public) } + + context 'when users are added' do + let(:user) { create(:user) } + let(:group_user) { create(:user) } + let(:member) do + group.add_developer(group_user) + group.members.find_by(user_id: group_user) + end + + context 'when user does not have enough rights' do + before do + group.members.delete(member) + group.add_developer(user) + sign_in(user) + end + + it 'returns 403' do + post :create, group_id: group, + user_ids: member + + expect(response).to have_http_status(403) + expect(group.users).not_to include group_user + end + end + + context 'when user has enough rights' do + before do + group.add_owner(user) + sign_in(user) + end + + it 'adds user to members' do + post :create, group_id: group, + user_ids: member + + expect(response).to set_flash.to 'Users were successfully added.' + expect(response).to redirect_to(group_group_members_path(group)) + expect(group.users).to include group_user + end + + it 'adds no user to members' do + post :create, group_id: group, + user_ids: '' + + expect(response).to set_flash.to 'No users specified.' + expect(response).to redirect_to(group_group_members_path(group)) + expect(group.users).not_to include group_user + end + end + end + end + describe 'DELETE destroy' do let(:member) { create(:group_member, :developer, group: group) } diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 5e487241d07..44d907eafd7 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -13,6 +13,63 @@ describe Projects::ProjectMembersController do end end + describe '#create' do + let(:project) { create(:project, :public) } + + context 'when users are added' do + let(:user) { create(:user) } + let(:team_user) { create(:user) } + let(:member) do + project.team << [team_user, :developer] + project.members.find_by(user_id: team_user.id) + end + + context 'when user does not have enough rights' do + before do + project.members.delete(member) + project.team << [user, :developer] + sign_in(user) + end + + it 'returns 404' do + post :create, namespace_id: project.namespace, + project_id: project, + user_ids: member + + expect(response).to have_http_status(404) + expect(project.users).not_to include team_user + end + end + + context 'when user has enough rights' do + before do + project.team << [user, :master] + sign_in(user) + end + + it 'adds user to members' do + post :create, namespace_id: project.namespace, + project_id: project, + user_ids: member + + expect(response).to set_flash.to 'Users were successfully added.' + expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project)) + expect(project.users).to include team_user + end + + it 'adds no user to members' do + post :create, namespace_id: project.namespace, + project_id: project, + user_ids: '' + + expect(response).to set_flash.to 'No users specified.' + expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project)) + expect(project.users).not_to include team_user + end + end + end + end + describe 'DELETE destroy' do let(:member) { create(:project_member, :developer, project: project) } From c82278898d4e7932da1e0071d4dcfa13f65967f0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 19 Oct 2016 17:27:28 +0300 Subject: [PATCH 110/176] Refactor groups/projects members controller Signed-off-by: Dmitriy Zaporozhets --- .../groups/group_members_controller.rb | 2 +- .../projects/project_members_controller.rb | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 3a373e4a946..5a6e26ab8cc 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -21,7 +21,7 @@ class Groups::GroupMembersController < Groups::ApplicationController end def create - if params[:user_ids].empty? + if params[:user_ids].blank? return redirect_to group_group_members_path(@group), alert: 'No users specified.' end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 2bac48c5490..ec8512bbaba 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -25,16 +25,18 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def create - if params[:user_ids].empty? - return redirect_to namespace_project_project_members_path(@project.namespace, @project), alert: 'No users specified.' + if params[:user_ids].blank? && params[:group_ids].blank? + return redirect_to namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.' end - @project.team.add_users( - params[:user_ids].split(','), - params[:access_level], - expires_at: params[:expires_at], - current_user: current_user - ) + if params[:user_ids].present? + @project.team.add_users( + params[:user_ids].split(','), + params[:access_level], + expires_at: params[:expires_at], + current_user: current_user + ) + end redirect_to namespace_project_project_members_path(@project.namespace, @project), notice: 'Users were successfully added.' end From 1eba14ae2ecd829642e88610b0b2964e5d04158e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 19 Oct 2016 17:50:41 +0300 Subject: [PATCH 111/176] Refactor create member tests from group_members_controller_spec Signed-off-by: Dmitriy Zaporozhets --- .../groups/group_members_controller_spec.rb | 81 ++++++++----------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index 82eebe6f2d4..c7db84dd5f9 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -13,56 +13,45 @@ describe Groups::GroupMembersController do end end - describe '#create' do - let(:group) { create(:group, :public) } + describe 'POST create' do + let(:group_user) { create(:user) } - context 'when users are added' do - let(:user) { create(:user) } - let(:group_user) { create(:user) } - let(:member) do - group.add_developer(group_user) - group.members.find_by(user_id: group_user) + before { sign_in(user) } + + context 'when user does not have enough rights' do + before { group.add_developer(user) } + + it 'returns 403' do + post :create, group_id: group, + user_ids: group_user.id, + access_level: Gitlab::Access::GUEST + + expect(response).to have_http_status(403) + expect(group.users).not_to include group_user + end + end + + context 'when user has enough rights' do + before { group.add_owner(user) } + + it 'adds user to members' do + post :create, group_id: group, + user_ids: group_user.id, + access_level: Gitlab::Access::GUEST + + expect(response).to set_flash.to 'Users were successfully added.' + expect(response).to redirect_to(group_group_members_path(group)) + expect(group.users).to include group_user end - context 'when user does not have enough rights' do - before do - group.members.delete(member) - group.add_developer(user) - sign_in(user) - end + it 'adds no user to members' do + post :create, group_id: group, + user_ids: '', + access_level: Gitlab::Access::GUEST - it 'returns 403' do - post :create, group_id: group, - user_ids: member - - expect(response).to have_http_status(403) - expect(group.users).not_to include group_user - end - end - - context 'when user has enough rights' do - before do - group.add_owner(user) - sign_in(user) - end - - it 'adds user to members' do - post :create, group_id: group, - user_ids: member - - expect(response).to set_flash.to 'Users were successfully added.' - expect(response).to redirect_to(group_group_members_path(group)) - expect(group.users).to include group_user - end - - it 'adds no user to members' do - post :create, group_id: group, - user_ids: '' - - expect(response).to set_flash.to 'No users specified.' - expect(response).to redirect_to(group_group_members_path(group)) - expect(group.users).not_to include group_user - end + expect(response).to set_flash.to 'No users specified.' + expect(response).to redirect_to(group_group_members_path(group)) + expect(group.users).not_to include group_user end end end From 52707acfa73e15896c4794d55e430eb14a532f66 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 20 Oct 2016 10:43:05 +0300 Subject: [PATCH 112/176] Move changelog item to 8.14 Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa0e67037e..444320bb35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix error in generating labels - Fix reply-by-email not working due to queue name mismatch - Expire and build repository cache after project import + - Simpler arguments passed to named_route on toggle_award_url helper method + - Better handle when no users were selected for adding to group or project. (Linus Thiel) ## 8.13.0 (2016-10-22) - Removes extra line for empty issue description. (!7045) @@ -69,7 +71,6 @@ Please view this file on the master branch, on stable branches it's out of date. - Update Gitlab Shell to fix some problems with moving projects between storages - Cache rendered markdown in the database, rather than Redis - Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references - - Better handle when no users were selected for adding to group or project. (Linus Thiel) - Simplify Mentionable concern instance methods - API: Ability to retrieve version information (Robert Schilling) - Fix permission for setting an issue's due date From 3608f9284e676437e306b4f08a157f997471f4d3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 20 Oct 2016 10:59:36 +0300 Subject: [PATCH 113/176] Improve create project member test at project_members_controller_spec Signed-off-by: Dmitriy Zaporozhets --- .../project_members_controller_spec.rb | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index 44d907eafd7..b4f066d8600 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -13,58 +13,49 @@ describe Projects::ProjectMembersController do end end - describe '#create' do - let(:project) { create(:project, :public) } - + describe 'POST create' do context 'when users are added' do - let(:user) { create(:user) } - let(:team_user) { create(:user) } - let(:member) do - project.team << [team_user, :developer] - project.members.find_by(user_id: team_user.id) - end + let(:project_user) { create(:user) } + + before { sign_in(user) } context 'when user does not have enough rights' do - before do - project.members.delete(member) - project.team << [user, :developer] - sign_in(user) - end + before { project.team << [user, :developer] } it 'returns 404' do post :create, namespace_id: project.namespace, project_id: project, - user_ids: member + user_ids: project_user.id, + access_level: Gitlab::Access::GUEST expect(response).to have_http_status(404) - expect(project.users).not_to include team_user + expect(project.users).not_to include project_user end end context 'when user has enough rights' do - before do - project.team << [user, :master] - sign_in(user) - end + before { project.team << [user, :master] } it 'adds user to members' do post :create, namespace_id: project.namespace, project_id: project, - user_ids: member + user_ids: project_user.id, + access_level: Gitlab::Access::GUEST expect(response).to set_flash.to 'Users were successfully added.' expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project)) - expect(project.users).to include team_user + expect(project.users).to include project_user end it 'adds no user to members' do post :create, namespace_id: project.namespace, project_id: project, - user_ids: '' + user_ids: '', + access_level: Gitlab::Access::GUEST - expect(response).to set_flash.to 'No users specified.' + expect(response).to set_flash.to 'No users or groups specified.' expect(response).to redirect_to(namespace_project_project_members_path(project.namespace, project)) - expect(project.users).not_to include team_user + expect(project.users).not_to include project_user end end end From bedbb7b744504a0008e0bc33b21f229a84d616d6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 20 Oct 2016 11:07:06 +0300 Subject: [PATCH 114/176] Refactor js that disable form submit if no members selected Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/members.js.es6 | 1 + app/views/groups/group_members/index.html.haml | 3 --- app/views/projects/project_members/index.html.haml | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/assets/javascripts/members.js.es6 b/app/assets/javascripts/members.js.es6 index a0cd20f21e8..2bdd0f7a637 100644 --- a/app/assets/javascripts/members.js.es6 +++ b/app/assets/javascripts/members.js.es6 @@ -10,6 +10,7 @@ $('.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); + disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); } removeRow(e) { diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index b295ad0e182..ebf9aca7700 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -29,6 +29,3 @@ %ul.content-list = render partial: 'shared/members/member', collection: @members, as: :member = paginate @members, theme: 'gitlab' - -:javascript - window.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 3904647b608..bdeb704b6da 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -26,6 +26,3 @@ = render 'team', members: @project_members = paginate @project_members, theme: "gitlab" - -:javascript - window.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change'); From 80ea344e427379b5e614f14c90af5369ae1c4a16 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 20 Oct 2016 11:48:38 +0300 Subject: [PATCH 115/176] Trigger change even in select2 test helper to produce production-like behaviour Signed-off-by: Dmitriy Zaporozhets --- spec/support/select2_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/support/select2_helper.rb b/spec/support/select2_helper.rb index 35cc51725c6..d30cc8ff9f2 100644 --- a/spec/support/select2_helper.rb +++ b/spec/support/select2_helper.rb @@ -17,9 +17,9 @@ module Select2Helper selector = options.fetch(:from) if options[:multiple] - execute_script("$('#{selector}').select2('val', ['#{value}'], true);") + execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');") else - execute_script("$('#{selector}').select2('val', '#{value}', true);") + execute_script("$('#{selector}').select2('val', '#{value}').trigger('change');") end end end From f5659ac4d1ad26d4d0ba5ea10adb92511cc911f2 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 20 Oct 2016 14:58:26 +0300 Subject: [PATCH 116/176] Add parentheses around return redirect_to method Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups/group_members_controller.rb | 2 +- app/controllers/projects/project_members_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 5a6e26ab8cc..940a3ad20ba 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -22,7 +22,7 @@ class Groups::GroupMembersController < Groups::ApplicationController def create if params[:user_ids].blank? - return redirect_to group_group_members_path(@group), alert: 'No users specified.' + return redirect_to(group_group_members_path(@group), alert: 'No users specified.') end @group.add_users( diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index ec8512bbaba..073d4338338 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -26,7 +26,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController def create if params[:user_ids].blank? && params[:group_ids].blank? - return redirect_to namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.' + return redirect_to(namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.') end if params[:user_ids].present? From 7ded7c17d4da00e60d2855560a5c53b6dbaf98c0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 20 Oct 2016 15:27:02 +0300 Subject: [PATCH 117/176] Update project member controller to match recent master logic Signed-off-by: Dmitriy Zaporozhets --- .../projects/project_members_controller.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 073d4338338..d08f490de18 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -25,18 +25,16 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def create - if params[:user_ids].blank? && params[:group_ids].blank? + if params[:user_ids].blank? return redirect_to(namespace_project_project_members_path(@project.namespace, @project), alert: 'No users or groups specified.') end - if params[:user_ids].present? - @project.team.add_users( - params[:user_ids].split(','), - params[:access_level], - expires_at: params[:expires_at], - current_user: current_user - ) - end + @project.team.add_users( + params[:user_ids].split(','), + params[:access_level], + expires_at: params[:expires_at], + current_user: current_user + ) redirect_to namespace_project_project_members_path(@project.namespace, @project), notice: 'Users were successfully added.' end From 672704f6389104b3d93c00a116dd4dc8ece19136 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 24 Oct 2016 13:51:17 +0300 Subject: [PATCH 118/176] Add relative url support to routing contrainers Signed-off-by: Dmitriy Zaporozhets --- lib/constraints/namespace_url_constrainer.rb | 13 ++++++++++++- .../constraints/namespace_url_constrainer_spec.rb | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/constraints/namespace_url_constrainer.rb b/lib/constraints/namespace_url_constrainer.rb index 23920193743..91b70143f11 100644 --- a/lib/constraints/namespace_url_constrainer.rb +++ b/lib/constraints/namespace_url_constrainer.rb @@ -1,6 +1,9 @@ class NamespaceUrlConstrainer def matches?(request) - id = request.path.sub(/\A\/+/, '').split('/').first.sub(/.atom\z/, '') + id = request.path + id = id.sub(/\A#{relative_url_root}/, '') if relative_url_root + id = id.sub(/\A\/+/, '').split('/').first + id = id.sub(/.atom\z/, '') if id if id =~ Gitlab::Regex.namespace_regex find_resource(id) @@ -10,4 +13,12 @@ class NamespaceUrlConstrainer def find_resource(id) Namespace.find_by_path(id) end + + private + + def relative_url_root + if defined?(Gitlab::Application.config.relative_url_root) + Gitlab::Application.config.relative_url_root + end + end end diff --git a/spec/lib/constraints/namespace_url_constrainer_spec.rb b/spec/lib/constraints/namespace_url_constrainer_spec.rb index a5feaacb8ee..7814711fe27 100644 --- a/spec/lib/constraints/namespace_url_constrainer_spec.rb +++ b/spec/lib/constraints/namespace_url_constrainer_spec.rb @@ -17,6 +17,16 @@ describe NamespaceUrlConstrainer, lib: true do it { expect(subject.matches?(request '/g/gitlab')).to be_falsey } it { expect(subject.matches?(request '/.gitlab')).to be_falsey } end + + context 'relative url' do + before do + allow(Gitlab::Application.config).to receive(:relative_url_root) { '/gitlab' } + end + + it { expect(subject.matches?(request '/gitlab/gitlab')).to be_truthy } + it { expect(subject.matches?(request '/gitlab/gitlab-ce')).to be_falsey } + it { expect(subject.matches?(request '/gitlab/')).to be_falsey } + end end def request(path) From ccd81a872cbb28fe1dc3f8722fa2a8b85fd16ee8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 24 Oct 2016 13:53:15 +0300 Subject: [PATCH 119/176] Add changelog item for groups 404 on relative url Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 909af5fc053..af88bdc8988 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix error in generating labels - Fix reply-by-email not working due to queue name mismatch - Expire and build repository cache after project import + - Fix 404 for group pages when GitLab setup uses relative url ## 8.13.0 (2016-10-22) - Removes extra line for empty issue description. (!7045) From 1e742878356c2b920d138fdad9883c3952c891e2 Mon Sep 17 00:00:00 2001 From: Brad Jones Date: Thu, 11 Aug 2016 20:38:43 +0000 Subject: [PATCH 120/176] Specify which Fog storage drivers are imported by default in backup_restore.md --- doc/raketasks/backup_restore.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 26baffdf792..ad18331bed6 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -81,8 +81,11 @@ Deleting old backups... [SKIPPING] Starting with GitLab 7.4 you can let the backup script upload the '.tar' file it creates. It uses the [Fog library](http://fog.io/) to perform the upload. -In the example below we use Amazon S3 for storage. -Fog also supports [other storage providers](http://fog.io/storage/). +In the example below we use Amazon S3 for storage, but Fog also lets you use +[other storage providers](http://fog.io/storage/). GitLab +[imports cloud drivers](https://gitlab.com/gitlab-org/gitlab-ce/blob/30f5b9a5b711b46f1065baf755e413ceced5646b/Gemfile#L88) +for AWS, Azure, Google, OpenStack Swift and Rackspace as well. A local driver is +[also available](#uploading-to-locally-mounted-shares). For omnibus packages: From 8f527cbf06d31c084bdf71c7f882d73ff6591e29 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 24 Oct 2016 13:06:17 +0200 Subject: [PATCH 121/176] Grapify builds API --- lib/api/builds.rb | 162 ++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 83 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 7b00c5037f1..67adca6605f 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -3,15 +3,32 @@ module API class Builds < Grape::API before { authenticate! } + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do - # Get a project builds - # - # Parameters: - # id (required) - The ID of a project - # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; - # if none provided showing all builds) - # Example Request: - # GET /projects/:id/builds + helpers do + params :optional_scope do + optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', + values: ['pending', 'running', 'failed', 'success', 'canceled'], + coerce_with: ->(scope) { + if scope.is_a?(String) + [scope] + elsif scope.is_a?(Hashie::Mash) + scope.values + else + ['unknown'] + end + } + end + end + + desc 'Get a project builds' do + success Entities::Build + end + params do + use :optional_scope + end get ':id/builds' do builds = user_project.builds.order('id DESC') builds = filter_builds(builds, params[:scope]) @@ -20,15 +37,13 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Get builds for a specific commit of a project - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The SHA id of a commit - # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; - # if none provided showing all builds) - # Example Request: - # GET /projects/:id/repository/commits/:sha/builds + desc 'Get builds for a specific commit of a project' do + success Entities::Build + end + params do + requires :sha, type: String, desc: 'The SHA id of a commit' + use :optional_scope + end get ':id/repository/commits/:sha/builds' do authorize_read_builds! @@ -42,13 +57,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Get a specific build of a project - # - # Parameters: - # id (required) - The ID of a project - # build_id (required) - The ID of a build - # Example Request: - # GET /projects/:id/builds/:build_id + desc 'Get a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end get ':id/builds/:build_id' do authorize_read_builds! @@ -58,13 +72,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Download the artifacts file from build - # - # Parameters: - # id (required) - The ID of a build - # token (required) - The build authorization token - # Example Request: - # GET /projects/:id/builds/:build_id/artifacts + desc 'Download the artifacts file from build' do + detail 'This feature was introduced in GitLab 8.5' + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end get ':id/builds/:build_id/artifacts' do authorize_read_builds! @@ -73,14 +86,13 @@ module API present_artifacts!(build.artifacts_file) end - # Download the artifacts file from ref_name and job - # - # Parameters: - # id (required) - The ID of a project - # ref_name (required) - The ref from repository - # job (required) - The name for the build - # Example Request: - # GET /projects/:id/builds/artifacts/:ref_name/download?job=name + desc 'Download the artifacts file from build' do + detail 'This feature was introduced in GitLab 8.10' + end + params do + requires :ref_name, type: String, desc: 'The ref from repository' + requires :job, type: String, desc: 'The name for the build' + end get ':id/builds/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do authorize_read_builds! @@ -91,17 +103,13 @@ module API present_artifacts!(latest_build.artifacts_file) end - # Get a trace of a specific build of a project - # - # Parameters: - # id (required) - The ID of a project - # build_id (required) - The ID of a build - # Example Request: - # GET /projects/:id/build/:build_id/trace - # # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace # is saved in the DB instead of file). But before that, we need to consider how to replace the value of # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. + desc 'Get a trace of a specific build of a project' + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end get ':id/builds/:build_id/trace' do authorize_read_builds! @@ -115,13 +123,12 @@ module API body trace end - # Cancel a specific build of a project - # - # parameters: - # id (required) - the id of a project - # build_id (required) - the id of a build - # example request: - # post /projects/:id/build/:build_id/cancel + desc 'Cancel a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/cancel' do authorize_update_builds! @@ -133,13 +140,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Retry a specific build of a project - # - # parameters: - # id (required) - the id of a project - # build_id (required) - the id of a build - # example request: - # post /projects/:id/build/:build_id/retry + desc 'Retry a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/retry' do authorize_update_builds! @@ -152,13 +158,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Erase build (remove artifacts and build trace) - # - # Parameters: - # id (required) - the id of a project - # build_id (required) - the id of a build - # example Request: - # post /projects/:id/build/:build_id/erase + desc 'Erase build (remove artifacts and build trace)' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/erase' do authorize_update_builds! @@ -170,13 +175,12 @@ module API user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) end - # Keep the artifacts to prevent them from being deleted - # - # Parameters: - # id (required) - the id of a project - # build_id (required) - The ID of a build - # Example Request: - # POST /projects/:id/builds/:build_id/artifacts/keep + desc 'Keep the artifacts to prevent them from being deleted' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/artifacts/keep' do authorize_update_builds! @@ -235,14 +239,6 @@ module API return builds if scope.nil? || scope.empty? available_statuses = ::CommitStatus::AVAILABLE_STATUSES - scope = - if scope.is_a?(String) - [scope] - elsif scope.is_a?(Hashie::Mash) - scope.values - else - ['unknown'] - end unknown = scope - available_statuses render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty? From ea6c5c1c0ee8f09fff4dbf02d7bfb65107020996 Mon Sep 17 00:00:00 2001 From: barthc Date: Tue, 27 Sep 2016 15:24:50 +0100 Subject: [PATCH 122/176] Fix authored vote from notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- CHANGELOG.md | 1 + app/services/notes/create_service.rb | 6 ++++-- spec/requests/api/notes_spec.rb | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 909af5fc053..b5525c32465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.14.0 (2016-11-22) - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Trim leading and trailing whitespace on project_path (Linus Thiel) + - Prevent award emoji via notes for issues/MRs authored by user (barthc) - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index a36008c3ef5..723cc0e6834 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -7,8 +7,10 @@ module Notes if note.award_emoji? noteable = note.noteable - todo_service.new_award_emoji(noteable, current_user) - return noteable.create_award_emoji(note.award_emoji_name, current_user) + if noteable.user_can_award?(current_user, note.award_emoji_name) + todo_service.new_award_emoji(noteable, current_user) + return noteable.create_award_emoji(note.award_emoji_name, current_user) + end end # We execute commands (extracted from `params[:note]`) on the noteable diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index d58bedc3bf7..0124b7271b3 100644 --- a/spec/requests/api/notes_spec.rb +++ b/spec/requests/api/notes_spec.rb @@ -221,12 +221,23 @@ describe API::API, api: true do end end - context 'when the user is posting an award emoji' do + context 'when the user is posting an award emoji on an issue created by someone else' do + let(:issue2) { create(:issue, project: project) } + it 'returns an award emoji' do + post api("/projects/#{project.id}/issues/#{issue2.id}/notes", user), body: ':+1:' + + expect(response).to have_http_status(201) + expect(json_response['awardable_id']).to eq issue2.id + end + end + + context 'when the user is posting an award emoji on his/her own issue' do + it 'creates a new issue note' do post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:' expect(response).to have_http_status(201) - expect(json_response['awardable_id']).to eq issue.id + expect(json_response['body']).to eq(':+1:') end end end From 4a0e8f59e25a0b33e8e8cf33678688df912da8eb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 20 Oct 2016 14:54:55 +0000 Subject: [PATCH 123/176] Merge branch 'security-fix-leaking-namespace-name' into 'security' Check that user has access to a given namespace to prevent leaking namespace names. See merge request !2009 --- app/controllers/import/gitlab_projects_controller.rb | 4 ++-- app/views/import/gitlab_projects/new.html.haml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index 3ec173abcdb..36d246d185b 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -2,8 +2,8 @@ class Import::GitlabProjectsController < Import::BaseController before_action :verify_gitlab_project_import_enabled def new - @namespace_id = project_params[:namespace_id] - @namespace_name = Namespace.find(project_params[:namespace_id]).name + @namespace = Namespace.find(project_params[:namespace_id]) + return render_404 unless current_user.can?(:create_projects, @namespace) @path = project_params[:path] end diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml index 44e2653ca4a..767dffb5589 100644 --- a/app/views/import/gitlab_projects/new.html.haml +++ b/app/views/import/gitlab_projects/new.html.haml @@ -9,12 +9,12 @@ %p Project will be imported as %strong - #{@namespace_name}/#{@path} + #{@namespace.name}/#{@path} %p To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here. .form-group - = hidden_field_tag :namespace_id, @namespace_id + = hidden_field_tag :namespace_id, @namespace.id = hidden_field_tag :path, @path = label_tag :file, class: 'control-label' do %span GitLab project export From 4eb9056890d7789abc2f30be30fe3536336bdf42 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Mon, 24 Oct 2016 14:59:20 +0100 Subject: [PATCH 124/176] fixes build with cache:clear issue --- spec/tasks/gitlab/backup_rake_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 548e7780c36..73bc8326f02 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -9,6 +9,7 @@ describe 'gitlab:app namespace rake task' do Rake.application.rake_require 'tasks/gitlab/backup' Rake.application.rake_require 'tasks/gitlab/shell' Rake.application.rake_require 'tasks/gitlab/db' + Rake.application.rake_require 'tasks/cache' # empty task as env is already loaded Rake::Task.define_task :environment From f73d83db76ae8386ad4db604efb40156593b7a7a Mon Sep 17 00:00:00 2001 From: Luis HGO Date: Mon, 20 Jun 2016 22:37:40 -0300 Subject: [PATCH 125/176] Added path parameter to Commits API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- CHANGELOG.md | 1 + lib/api/commits.rb | 2 ++ spec/requests/api/commits_spec.rb | 11 +++++++++++ 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53406425f3d..ae578852fde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Trim leading and trailing whitespace on project_path (Linus Thiel) - Prevent award emoji via notes for issues/MRs authored by user (barthc) + - Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO) - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 617a240318a..2f2cf769481 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -19,6 +19,7 @@ module API optional :until, type: String, desc: 'Only commits before or in this date will be returned' optional :page, type: Integer, default: 0, desc: 'The page for pagination' optional :per_page, type: Integer, default: 20, desc: 'The number of results per page' + optional :path, type: String, desc: 'The file path' end get ":id/repository/commits" do # TODO remove the next line for 9.0, use DateTime type in the params block @@ -28,6 +29,7 @@ module API offset = params[:page] * params[:per_page] commits = user_project.repository.commits(ref, + path: params[:path], limit: params[:per_page], offset: offset, after: params[:since], diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 66fa0c0c01f..a6e8550fac3 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -72,6 +72,17 @@ describe API::API, api: true do expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format" end end + + context "path optional parameter" do + it "returns project commits matching provided path parameter" do + path = 'files/ruby/popen.rb' + + get api("/projects/#{project.id}/repository/commits?path=#{path}", user) + + expect(json_response.size).to eq(3) + expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d") + end + end end describe "Create a commit with multiple files and actions" do From 8b7634c2b665f6c3dc8b0f82b870fd2032f7d247 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 24 Oct 2016 16:45:00 +0200 Subject: [PATCH 126/176] Fix old monitoring links to point to the new location --- doc/development/performance.md | 2 +- doc/monitoring/performance/gitlab_configuration.md | 2 +- doc/monitoring/performance/influxdb_configuration.md | 2 +- doc/monitoring/performance/influxdb_schema.md | 2 +- doc/monitoring/performance/introduction.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/development/performance.md b/doc/development/performance.md index c4a964d1da3..8337c2d9cb3 100644 --- a/doc/development/performance.md +++ b/doc/development/performance.md @@ -37,7 +37,7 @@ graphs/dashboards. GitLab provides built-in tools to aid the process of improving performance: * [Sherlock](profiling.md#sherlock) -* [GitLab Performance Monitoring](../monitoring/performance/monitoring.md) +* [GitLab Performance Monitoring](../administration/monitoring/performance/monitoring.md) * [Request Profiling](../administration/monitoring/performance/request_profiling.md) GitLab employees can use GitLab.com's performance monitoring systems located at diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md index a669bb28904..19d46135930 100644 --- a/doc/monitoring/performance/gitlab_configuration.md +++ b/doc/monitoring/performance/gitlab_configuration.md @@ -1 +1 @@ -This document was moved to [administration/monitoring/performance/gitlab_configuration](../administration/monitoring/performance/gitlab_configuration.md). +This document was moved to [administration/monitoring/performance/gitlab_configuration](../../administration/monitoring/performance/gitlab_configuration.md). diff --git a/doc/monitoring/performance/influxdb_configuration.md b/doc/monitoring/performance/influxdb_configuration.md index 02647de1eb0..15fd275e916 100644 --- a/doc/monitoring/performance/influxdb_configuration.md +++ b/doc/monitoring/performance/influxdb_configuration.md @@ -1 +1 @@ -This document was moved to [administration/monitoring/performance/influxdb_configuration](../administration/monitoring/performance/influxdb_configuration.md). +This document was moved to [administration/monitoring/performance/influxdb_configuration](../../administration/monitoring/performance/influxdb_configuration.md). diff --git a/doc/monitoring/performance/influxdb_schema.md b/doc/monitoring/performance/influxdb_schema.md index a989e323e04..e53f9701dc3 100644 --- a/doc/monitoring/performance/influxdb_schema.md +++ b/doc/monitoring/performance/influxdb_schema.md @@ -1 +1 @@ -This document was moved to [administration/monitoring/performance/influxdb_schema](../administration/monitoring/performance/influxdb_schema.md). +This document was moved to [administration/monitoring/performance/influxdb_schema](../../administration/monitoring/performance/influxdb_schema.md). diff --git a/doc/monitoring/performance/introduction.md b/doc/monitoring/performance/introduction.md index ab3f3ac1664..ae88baa0c14 100644 --- a/doc/monitoring/performance/introduction.md +++ b/doc/monitoring/performance/introduction.md @@ -1 +1 @@ -This document was moved to [administration/monitoring/performance/introduction](../administration/monitoring/performance/introduction.md). +This document was moved to [administration/monitoring/performance/introduction](../../administration/monitoring/performance/introduction.md). From f3cf51523067149ac39e61ebdb4fda372122d65f Mon Sep 17 00:00:00 2001 From: Adam Niedzielski Date: Fri, 21 Oct 2016 14:12:00 +0200 Subject: [PATCH 127/176] Fix typo in project settings that prevents users from enabling container registry. Fixes #23575. --- app/views/projects/edit.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 55b6580e640..30473d14b9b 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -101,7 +101,7 @@ Git Large File Storage = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs') - - if Gitlab.config.lfs.enabled && current_user.admin? + - if Gitlab.config.registry.enabled .form-group .checkbox = f.label :container_registry_enabled do From 21666dbe119df7f2d86f0ac13c4addc814af4485 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 24 Oct 2016 14:05:33 +0200 Subject: [PATCH 128/176] Grapify the labels API --- lib/api/labels.rb | 91 +++++++++++++------------------- spec/requests/api/labels_spec.rb | 6 +-- 2 files changed, 41 insertions(+), 56 deletions(-) diff --git a/lib/api/labels.rb b/lib/api/labels.rb index 642e6345b9e..326e1e7ae00 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -3,37 +3,32 @@ module API class Labels < Grape::API before { authenticate! } + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do - # Get all labels of the project - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id/labels + desc 'Get all labels of the project' do + success Entities::Label + end get ':id/labels' do present available_labels, with: Entities::Label, current_user: current_user end - # Creates a new label - # - # Parameters: - # id (required) - The ID of a project - # name (required) - The name of the label to be created - # color (required) - Color of the label given in 6-digit hex - # notation with leading '#' sign (e.g. #FFAABB) - # description (optional) - The description of label to be created - # Example Request: - # POST /projects/:id/labels + desc 'Create a new label' do + success Entities::Label + end + params do + requires :name, type: String, desc: 'The name of the label to be created' + requires :color, type: String, desc: "The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)" + optional :description, type: String, desc: 'The description of label to be created' + end post ':id/labels' do authorize! :admin_label, user_project - required_attributes! [:name, :color] - - attrs = attributes_for_keys [:name, :color, :description] - label = user_project.find_label(attrs[:name]) + label = user_project.find_label(params[:name]) conflict!('Label already exists') if label - label = user_project.labels.create(attrs) + label = user_project.labels.create(declared(params, include_parent_namespaces: false).to_h) if label.valid? present label, with: Entities::Label, current_user: current_user @@ -42,54 +37,44 @@ module API end end - # Deletes an existing label - # - # Parameters: - # id (required) - The ID of a project - # name (required) - The name of the label to be deleted - # - # Example Request: - # DELETE /projects/:id/labels + desc 'Delete an existing label' do + success Entities::Label + end + params do + requires :name, type: String, desc: 'The name of the label to be deleted' + end delete ':id/labels' do authorize! :admin_label, user_project - required_attributes! [:name] label = user_project.find_label(params[:name]) not_found!('Label') unless label - label.destroy + present label.destroy, with: Entities::Label, current_user: current_user end - # Updates an existing label. At least one optional parameter is required. - # - # Parameters: - # id (required) - The ID of a project - # name (required) - The name of the label to be deleted - # new_name (optional) - The new name of the label - # color (optional) - Color of the label given in 6-digit hex - # notation with leading '#' sign (e.g. #FFAABB) - # description (optional) - The description of label to be created - # Example Request: - # PUT /projects/:id/labels + desc 'Update an existing label. At least one optional parameter is required.' do + success Entities::Label + end + params do + requires :name, type: String, desc: 'The name of the label to be updated' + optional :new_name, type: String, desc: 'The new name of the label' + optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)" + optional :description, type: String, desc: 'The new description of label' + at_least_one_of :new_name, :color, :description + end put ':id/labels' do authorize! :admin_label, user_project - required_attributes! [:name] label = user_project.find_label(params[:name]) not_found!('Label not found') unless label - attrs = attributes_for_keys [:new_name, :color, :description] - - if attrs.empty? - render_api_error!('Required parameters "new_name" or "color" ' \ - 'missing', - 400) - end - + update_params = declared(params, + include_parent_namespaces: false, + include_missing: false).to_h # Rename new name to the actual label attribute name - attrs[:name] = attrs.delete(:new_name) if attrs.key?(:new_name) + update_params['name'] = update_params.delete('new_name') if update_params.key?('new_name') - if label.update(attrs) + if label.update(update_params) present label, with: Entities::Label, current_user: current_user else render_validation_error!(label) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index 867bc615b97..46641fcd846 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -159,14 +159,14 @@ describe API::API, api: true do it 'returns 400 if no label name given' do put api("/projects/#{project.id}/labels", user), new_name: 'label2' expect(response).to have_http_status(400) - expect(json_response['message']).to eq('400 (Bad request) "name" not given') + expect(json_response['error']).to eq('name is missing') end it 'returns 400 if no new parameters given' do put api("/projects/#{project.id}/labels", user), name: 'label1' expect(response).to have_http_status(400) - expect(json_response['message']).to eq('Required parameters '\ - '"new_name" or "color" missing') + expect(json_response['error']).to eq('new_name, color, description are missing, '\ + 'at least one parameter must be provided') end it 'returns 400 for invalid name' do From ef56ceb40be13500af2cf727735ab24b87a753d6 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Fri, 21 Oct 2016 15:18:41 -0500 Subject: [PATCH 129/176] Change overflow scroll to auto --- app/assets/stylesheets/framework/dropdowns.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index a839371a6f2..224bc58f7a7 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -402,7 +402,7 @@ .dropdown-content { max-height: 215px; - overflow-y: scroll; + overflow-y: auto; } .dropdown-footer { From 413c012db83695753a5dbf7bb8541fb3a7248aa9 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Sat, 22 Oct 2016 20:50:24 +0200 Subject: [PATCH 130/176] Only show register tab if signup enabled. --- CHANGELOG.md | 1 + app/views/devise/shared/_tabs_normal.html.haml | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae578852fde..281ea2e3799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix 404 for group pages when GitLab setup uses relative url - Simpler arguments passed to named_route on toggle_award_url helper method - Better handle when no users were selected for adding to group or project. (Linus Thiel) + - Only show register tab if signup enabled. ## 8.13.0 (2016-10-22) - Removes extra line for empty issue description. (!7045) diff --git a/app/views/devise/shared/_tabs_normal.html.haml b/app/views/devise/shared/_tabs_normal.html.haml index 79b1d447a92..05246303fb6 100644 --- a/app/views/devise/shared/_tabs_normal.html.haml +++ b/app/views/devise/shared/_tabs_normal.html.haml @@ -1,5 +1,6 @@ %ul.nav-links.new-session-tabs.nav-tabs{ role: 'tablist'} %li.active{ role: 'presentation' } %a{ href: '#login-pane', data: { toggle: 'tab' }, role: 'tab'} Sign in - %li{ role: 'presentation'} - %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register + - if signin_enabled? && signup_enabled? + %li{ role: 'presentation'} + %a{ href: '#register-pane', data: { toggle: 'tab' }, role: 'tab'} Register From 5bc42660ef7f85ff7d7c56f93b5ec4cfaca0aff9 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Mon, 24 Oct 2016 13:26:48 +0200 Subject: [PATCH 131/176] Test login tab/pane rendering in varying configurations. --- spec/features/login_spec.rb | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 996f39ea06d..e91667f096d 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -215,4 +215,67 @@ feature 'Login', feature: true do end end end + + describe 'UI tabs and panes' do + context 'when no defaults are changed' do + it 'should correctly render tabs and panes' do + ensure_tab_pane_correctness + end + end + + context 'when signup is disabled' do + before do + stub_application_setting(signup_enabled: false) + end + it 'should correctly render tabs and panes' do + ensure_tab_pane_correctness + end + end + + context 'when ldap is enabled' do + before do + visit new_user_session_path + allow(page).to receive(:form_based_providers).and_return([:ldapmain]) + allow(page).to receive(:ldap_enabled).and_return(true) + end + + it 'should correctly render tabs and panes' do + ensure_tab_pane_correctness(false) + end + end + + context 'when crowd is enabled' do + before do + visit new_user_session_path + allow(page).to receive(:form_based_providers).and_return([:crowd]) + allow(page).to receive(:crowd_enabled?).and_return(true) + end + + it 'tabs and panes should be configured correctly' do + ensure_tab_pane_correctness(false) + end + end + + def ensure_tab_pane_correctness(visit_path=true) + if visit_path + visit new_user_session_path + end + ensure_tab_pane_counts + ensure_one_active_tab + ensure_one_active_pane + end + + def ensure_tab_pane_counts + tabs_count = page.all('[role="tab"]').size + expect(page).to have_selector('[role="tabpanel"]', count: tabs_count) + end + + def ensure_one_active_tab + expect(page).to have_selector('.nav-tabs > li.active', count: 1) + end + + def ensure_one_active_pane + expect(page).to have_selector('.tab-pane.active', count: 1) + end + end end From ffb1482193e4c60e7c5915e28917b0e58963b017 Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Mon, 24 Oct 2016 17:53:32 +0200 Subject: [PATCH 132/176] Use proper tense and spacing in login_specs. --- spec/features/login_spec.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index e91667f096d..a3e1ca923ef 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -218,7 +218,7 @@ feature 'Login', feature: true do describe 'UI tabs and panes' do context 'when no defaults are changed' do - it 'should correctly render tabs and panes' do + it 'correctly renders tabs and panes' do ensure_tab_pane_correctness end end @@ -227,7 +227,8 @@ feature 'Login', feature: true do before do stub_application_setting(signup_enabled: false) end - it 'should correctly render tabs and panes' do + + it 'correctly renders tabs and panes' do ensure_tab_pane_correctness end end @@ -239,7 +240,7 @@ feature 'Login', feature: true do allow(page).to receive(:ldap_enabled).and_return(true) end - it 'should correctly render tabs and panes' do + it 'correctly renders tabs and panes' do ensure_tab_pane_correctness(false) end end @@ -251,7 +252,7 @@ feature 'Login', feature: true do allow(page).to receive(:crowd_enabled?).and_return(true) end - it 'tabs and panes should be configured correctly' do + it 'correctly renders tabs and panes' do ensure_tab_pane_correctness(false) end end @@ -260,6 +261,7 @@ feature 'Login', feature: true do if visit_path visit new_user_session_path end + ensure_tab_pane_counts ensure_one_active_tab ensure_one_active_pane From 78de8816f587bd90725e7b46fcbd3860d3eb2889 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 25 Oct 2016 00:08:30 +0800 Subject: [PATCH 133/176] Also keep commits from source_project around, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6658#note_17190236 --- app/models/merge_request_diff.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index b8a10b7968e..dd65a9a8b86 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -299,8 +299,10 @@ class MergeRequestDiff < ActiveRecord::Base end def keep_around_commits - repository.keep_around(start_commit_sha) - repository.keep_around(head_commit_sha) - repository.keep_around(base_commit_sha) + [repository, merge_request.source_project.repository].each do |repo| + repo.keep_around(start_commit_sha) + repo.keep_around(head_commit_sha) + repo.keep_around(base_commit_sha) + end end end From b4a9247f7e59b454385890c2db46c86f076499ce Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 22 Oct 2016 23:14:52 +0100 Subject: [PATCH 134/176] Replaced deprecated use of document.body.scrollTop --- app/assets/javascripts/build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 97462a5959c..f4c387a1a05 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -148,7 +148,7 @@ }; Build.prototype.translateSidebar = function(e) { - var newPosition = this.sidebarTranslationLimits.max - document.body.scrollTop; + var newPosition = this.sidebarTranslationLimits.max - (document.body.scrollTop || document.documentElement.scrollTop); if (newPosition < this.sidebarTranslationLimits.min) newPosition = this.sidebarTranslationLimits.min; this.$sidebar.css({ top: newPosition From ed545d9f754374c7a5fe98a81544f0d1cdc88cf0 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 25 Oct 2016 01:56:39 +0800 Subject: [PATCH 135/176] Make sure merge request was created before deleting source --- spec/models/merge_request_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 6e5137602aa..6f66a0ebe75 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1286,7 +1286,8 @@ describe MergeRequest, models: true do let(:project) { create(:project) } let(:user) { create(:user) } let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) } - let(:merge_request) do + + let!(:merge_request) do create(:closed_merge_request, source_project: fork_project, target_project: project) From 4a880b292c13231f20c167ffe733bce7391b0c23 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 24 Oct 2016 12:57:40 -0500 Subject: [PATCH 136/176] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f02cc1f102..0af4d0eefa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Trim leading and trailing whitespace on project_path (Linus Thiel) - Prevent award emoji via notes for issues/MRs authored by user (barthc) - Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO) + - Fix extra space on Build sidebar on Firefox !7060 - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method From 3ea3a6a152c88325ca2fd1161a27b4a9e2d5ce51 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 14 Oct 2016 18:05:48 +0100 Subject: [PATCH 137/176] invoked the pipelines class when builds are dynamically loaded and dispatched for commit builds page --- CHANGELOG.md | 1 + app/assets/javascripts/dispatcher.js.es6 | 3 +++ app/assets/javascripts/merge_request_tabs.js | 1 + 3 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f02cc1f102..51b0adcde1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Please view this file on the master branch, on stable branches it's out of date. ## 8.13.1 (unreleased) - Fix error in generating labels - Fix reply-by-email not working due to queue name mismatch + - Fixed hidden pipeline graph on commit and MR page !6895 - Expire and build repository cache after project import - Fix 404 for group pages when GitLab setup uses relative url - Simpler arguments passed to named_route on toggle_award_url helper method diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index afc0d6f8c62..a1fe57562fa 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -117,6 +117,9 @@ new ZenMode(); shortcut_handler = new ShortcutsNavigation(); break; + case 'projects:commit:builds': + new gl.Pipelines(); + break; case 'projects:commits:show': case 'projects:activity': shortcut_handler = new ShortcutsNavigation(); diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 9f28738e06b..3dde979185b 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -282,6 +282,7 @@ document.querySelector("div#builds").innerHTML = data.html; gl.utils.localTimeAgo($('.js-timeago', 'div#builds')); _this.buildsLoaded = true; + if (!this.pipelines) this.pipelines = new gl.Pipelines(); return _this.scrollToElement("#builds"); }; })(this) From c7af4cf83027863a2d109ba82135e56092c71a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 24 Oct 2016 20:29:00 +0200 Subject: [PATCH 138/176] Don't print out implementation detail step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/ee_compat_check.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index 5e8a0c14eba..6f8d2ca3220 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -116,7 +116,7 @@ module Gitlab end def delete_ee_branch_locally - step("Checking out origin/master", %w[git checkout master]) + command(%w[git checkout master]) step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}]) end From 402dcab4f1a9e2ea7310e9d6137abe1299f48bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 24 Oct 2016 20:47:50 +0200 Subject: [PATCH 139/176] Use File.write instead of File.open + File#write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/ee_compat_check.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index 6f8d2ca3220..fd9fd56510f 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -111,7 +111,7 @@ module Gitlab output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout]) throw(:halt_check, :ko) unless status.zero? - File.open(filepath, 'w+') { |f| f.write(output) } + File.write(filepath, output) throw(:halt_check, :ko) unless File.exist?(filepath) end From d38d4a9e8e4b8aff1419f146c40bf7f4af8669ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 24 Oct 2016 21:19:46 +0200 Subject: [PATCH 140/176] Disable Rails/Output cop since it makes no sense here MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/ee_compat_check.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index fd9fd56510f..b1a6d5fe0f6 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -1,3 +1,4 @@ +# rubocop: disable Rails/Output module Gitlab # Checks if a set of migrations requires downtime or not. class EeCompatCheck From 5480114477fcb0f6a7dcc49eb75582652cb007e5 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 24 Oct 2016 14:23:40 -0500 Subject: [PATCH 141/176] Enable trailingWhitespace in scss-lint --- .scss-lint.yml | 2 +- .../stylesheets/framework/animations.scss | 4 ++-- app/assets/stylesheets/framework/logo.scss | 2 +- .../stylesheets/pages/cycle_analytics.scss | 18 +++++++++--------- app/assets/stylesheets/pages/diff.scss | 4 ++-- app/assets/stylesheets/pages/environments.scss | 8 ++++---- app/assets/stylesheets/pages/events.scss | 2 +- app/assets/stylesheets/pages/login.scss | 2 +- .../stylesheets/pages/merge_conflicts.scss | 2 +- app/assets/stylesheets/pages/notes.scss | 2 +- app/assets/stylesheets/pages/status.scss | 2 +- app/assets/stylesheets/pages/tree.scss | 4 ++-- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.scss-lint.yml b/.scss-lint.yml index 5093702519b..89f9e74f03d 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -223,7 +223,7 @@ linters: # Reports lines containing trailing whitespace. TrailingWhitespace: - enabled: false + enabled: true # Don't write trailing zeros for numeric values with a decimal point. TrailingZero: diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 1e9a45c19b8..98d3889cd44 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -1,10 +1,10 @@ // This file is based off animate.css 3.5.1, available here: // https://github.com/daneden/animate.css/blob/3.5.1/animate.css -// +// // animate.css - http://daneden.me/animate // Version - 3.5.1 // Licensed under the MIT license - http://opensource.org/licenses/MIT -// +// // Copyright (c) 2016 Daniel Eden .animated { diff --git a/app/assets/stylesheets/framework/logo.scss b/app/assets/stylesheets/framework/logo.scss index a90e45bb5f4..429cfbe7235 100644 --- a/app/assets/stylesheets/framework/logo.scss +++ b/app/assets/stylesheets/framework/logo.scss @@ -61,7 +61,7 @@ 10%, 80% { fill: $tanuki-red; } - + 20%, 90% { fill: lighten($tanuki-red, 25%); } diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss index d732008de3d..572e1e7d558 100644 --- a/app/assets/stylesheets/pages/cycle_analytics.scss +++ b/app/assets/stylesheets/pages/cycle_analytics.scss @@ -9,15 +9,15 @@ padding: 24px 0; border-bottom: none; position: relative; - + @media (max-width: $screen-sm-min) { padding: 6px 0 24px; - } + } } .column { text-align: center; - + @media (max-width: $screen-sm-min) { padding: 15px 0; } @@ -36,7 +36,7 @@ &:last-child { text-align: right; - + @media (max-width: $screen-sm-min) { text-align: center; } @@ -51,7 +51,7 @@ .bordered-box { border: 1px solid $border-color; border-radius: $border-radius-default; - + } .content-list { @@ -73,10 +73,10 @@ font-weight: 600; color: $gl-title-color; } - + &.text { color: $layout-link-gray; - + &.value-col { color: $gl-title-color; } @@ -108,13 +108,13 @@ .svg-container { text-align: center; - + svg { width: 136px; height: 136px; } } - + .inner-content { @media (max-width: $screen-sm-min) { padding: 0 28px; diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index fe6421f8b3f..f8e3ca29a2b 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -222,12 +222,12 @@ top: 13px; right: 7px; } - + .frame { top: 0; right: 0; position: absolute; - + &.deleted { margin: 0; display: block; diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 12ee0a5dc3d..fc49ff780fc 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -37,10 +37,10 @@ .branch-name { color: $gl-dark-link-color; } - + .stop-env-link { color: $table-text-gray; - + .stop-env-icon { font-size: 14px; } @@ -48,11 +48,11 @@ .deployment { .build-column { - + .build-link { color: $gl-dark-link-color; } - + .avatar { float: none; } diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 5d9a76dac05..3004959ff7b 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -142,7 +142,7 @@ .event-last-push { overflow: auto; width: 100%; - + .event-last-push-text { @include str-truncated(100%); padding: 4px 0; diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 8dac6ab999e..2be9453aaee 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -286,7 +286,7 @@ .new_user { position: relative; padding-bottom: 35px; - + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { .forgot-password { float: none !important; diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss index eed2b0ab7cc..2e917361b25 100644 --- a/app/assets/stylesheets/pages/merge_conflicts.scss +++ b/app/assets/stylesheets/pages/merge_conflicts.scss @@ -254,7 +254,7 @@ $colors: ( border-top: solid 2px $border-green-extra-light; } } - + .editor { pre { height: 350px; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index fffcdc812a7..faa0fc82ca8 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -458,7 +458,7 @@ ul.notes { .discussion-next-btn { svg { margin: 0; - + path { fill: $gray-darkest; } diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index f1d53c7b8bc..01426e28e92 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -74,7 +74,7 @@ .ci-status-icon-success_with_warning { color: $gl-warning; } - + .ci-status-icon-running { color: $blue-normal; } diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 6ea7a2b5498..84dcd6835d5 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -29,11 +29,11 @@ .last-commit { @include str-truncated(506px); - + @media (min-width: $screen-sm-max) and (max-width: $screen-md-max) { @include str-truncated(450px); } - + } .commit-history-link-spacer { From 2004b30ed60b2de37edcc0e174b0cfdc20e33db4 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 24 Oct 2016 14:29:44 -0500 Subject: [PATCH 142/176] Enable SpaceAroundOperator in scss-lint --- .scss-lint.yml | 2 +- app/assets/stylesheets/framework/modal.scss | 2 +- app/assets/stylesheets/pages/note_form.scss | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.scss-lint.yml b/.scss-lint.yml index 5093702519b..9eb6fdd767f 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -201,7 +201,7 @@ linters: # Operators should be formatted with a single space on both sides of an # infix operator. SpaceAroundOperator: - enabled: false + enabled: true # Opening braces should be preceded by a single space. SpaceBeforeBrace: diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 8374f30d0b2..8cd49280e1c 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -3,7 +3,7 @@ padding: 15px; .form-actions { - margin: -$gl-padding+1; + margin: -$gl-padding + 1; margin-top: 15px; } diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 17f28959414..25c1bbdc1c9 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -8,7 +8,7 @@ .diff-file .diff-content { tr.line_holder:hover > td .line_note_link { opacity: 1.0; - filter: alpha(opacity=100); + filter: alpha(opacity = 100); } } From f035cd486a5d929881fc7b36c9bd716d6b5c0ae4 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 24 Oct 2016 14:49:06 -0500 Subject: [PATCH 143/176] Enable SpaceAfterVariableColon in scss-lint --- .scss-lint.yml | 2 +- .../framework/tw_bootstrap_variables.scss | 122 +++++++++--------- .../stylesheets/framework/variables.scss | 44 +++---- app/assets/stylesheets/mailers/devise.scss | 10 +- 4 files changed, 89 insertions(+), 89 deletions(-) diff --git a/.scss-lint.yml b/.scss-lint.yml index 5093702519b..1958803cfde 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -191,7 +191,7 @@ linters: # Variables should be formatted with a single space separating the colon # from the variable's value. SpaceAfterVariableColon: - enabled: false + enabled: true # Variables should be formatted with no space between the name and the # colon. diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index 915aa631ef8..44fe37d3a4a 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -16,21 +16,21 @@ // $gray-light: lighten($gray-base, 46.7%) // #777 // $gray-lighter: lighten($gray-base, 93.5%) // #eee -$brand-primary: $gl-primary; -$brand-success: $gl-success; -$brand-info: $gl-info; -$brand-warning: $gl-warning; -$brand-danger: $gl-danger; +$brand-primary: $gl-primary; +$brand-success: $gl-success; +$brand-info: $gl-info; +$brand-warning: $gl-warning; +$brand-danger: $gl-danger; -$border-radius-base: 3px !default; -$border-radius-large: 3px !default; -$border-radius-small: 3px !default; +$border-radius-base: 3px !default; +$border-radius-large: 3px !default; +$border-radius-small: 3px !default; //== Scaffolding // -$text-color: $gl-text-color; -$link-color: $gl-link-color; +$text-color: $gl-text-color; +$link-color: $gl-link-color; //== Typography @@ -38,112 +38,112 @@ $link-color: $gl-link-color; //## Font, line-height, and color for body text, headings, and more. $font-family-sans-serif: $regular_font; -$font-family-monospace: $monospace_font; -$font-size-base: $gl-font-size; +$font-family-monospace: $monospace_font; +$font-size-base: $gl-font-size; //== Components // //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). -$padding-base-vertical: $gl-vert-padding; -$padding-base-horizontal: $gl-padding; -$component-active-color: #fff; -$component-active-bg: $brand-info; +$padding-base-vertical: $gl-vert-padding; +$padding-base-horizontal: $gl-padding; +$component-active-color: #fff; +$component-active-bg: $brand-info; //== Forms // //## -$input-color: $text-color; -$input-border: $border-color; -$input-border-focus: $focus-border-color; -$legend-color: $text-color; +$input-color: $text-color; +$input-border: $border-color; +$input-border-focus: $focus-border-color; +$legend-color: $text-color; //== Pagination // //## -$pagination-color: $gl-gray; -$pagination-bg: #fff; -$pagination-border: $border-color; +$pagination-color: $gl-gray; +$pagination-bg: #fff; +$pagination-border: $border-color; -$pagination-hover-color: $gl-gray; -$pagination-hover-bg: $row-hover; -$pagination-hover-border: $border-color; +$pagination-hover-color: $gl-gray; +$pagination-hover-bg: $row-hover; +$pagination-hover-border: $border-color; -$pagination-active-color: $blue-dark; -$pagination-active-bg: #fff; -$pagination-active-border: $border-color; +$pagination-active-color: $blue-dark; +$pagination-active-bg: #fff; +$pagination-active-border: $border-color; -$pagination-disabled-color: #cdcdcd; -$pagination-disabled-bg: $background-color; -$pagination-disabled-border: $border-color; +$pagination-disabled-color: #cdcdcd; +$pagination-disabled-bg: $background-color; +$pagination-disabled-border: $border-color; //== Form states and alerts // //## Define colors for form feedback states and, by default, alerts. -$state-success-text: #fff; -$state-success-bg: $brand-success; -$state-success-border: $brand-success; +$state-success-text: #fff; +$state-success-bg: $brand-success; +$state-success-border: $brand-success; -$state-info-text: #fff; -$state-info-bg: $brand-info; -$state-info-border: $brand-info; +$state-info-text: #fff; +$state-info-bg: $brand-info; +$state-info-border: $brand-info; -$state-warning-text: #fff; -$state-warning-bg: $brand-warning; -$state-warning-border: $brand-warning; +$state-warning-text: #fff; +$state-warning-bg: $brand-warning; +$state-warning-border: $brand-warning; -$state-danger-text: #fff; -$state-danger-bg: $brand-danger; -$state-danger-border: $brand-danger; +$state-danger-text: #fff; +$state-danger-bg: $brand-danger; +$state-danger-border: $brand-danger; //== Alerts // //## Define alert colors, border radius, and padding. -$alert-border-radius: 0; +$alert-border-radius: 0; //== Panels // //## -$panel-border-radius: 2px; -$panel-default-text: $text-color; -$panel-default-border: $border-color; +$panel-border-radius: 2px; +$panel-default-text: $text-color; +$panel-default-border: $border-color; $panel-default-heading-bg: $background-color; -$panel-footer-bg: $background-color; -$panel-inner-border: $border-color; +$panel-footer-bg: $background-color; +$panel-inner-border: $border-color; //== Wells // //## -$well-bg: $gray-light; -$well-border: #eee; +$well-bg: $gray-light; +$well-border: #eee; //== Code // //## -$code-color: #c7254e; -$code-bg: #f9f2f4; +$code-color: #c7254e; +$code-bg: #f9f2f4; -$kbd-color: #fff; -$kbd-bg: #333; +$kbd-color: #fff; +$kbd-bg: #333; //== Buttons // //## -$btn-default-color: $gl-text-color; -$btn-default-bg: #fff; -$btn-default-border: #e7e9ed; +$btn-default-color: $gl-text-color; +$btn-default-bg: #fff; +$btn-default-border: #e7e9ed; //== Nav // @@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding; //== Code // //## -$pre-bg: $background-color !default; -$pre-color: $gl-gray !default; +$pre-bg: $background-color !default; +$pre-color: $gl-gray !default; $pre-border-color: $border-color; $table-bg-accent: $background-color; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index eafe84570a8..b271f8cf332 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -84,39 +84,39 @@ $warning-message-border: #f0e2bb; /* * UI elements */ -$border-color: #e5e5e5; -$focus-border-color: #3aabf0; -$table-border-color: #f0f0f0; -$background-color: $gray-light; +$border-color: #e5e5e5; +$focus-border-color: #3aabf0; +$table-border-color: #f0f0f0; +$background-color: $gray-light; $dark-background-color: #f5f5f5; -$table-text-gray: #8f8f8f; +$table-text-gray: #8f8f8f; /* * Text */ -$gl-font-size: 15px; -$gl-title-color: #333; -$gl-text-color: #5c5c5c; -$gl-text-color-light: #8c8c8c; -$gl-text-green: #4a2; -$gl-text-red: #d12f19; -$gl-text-orange: #d90; -$gl-link-color: #3084bb; -$gl-dark-link-color: #333; +$gl-font-size: 15px; +$gl-title-color: #333; +$gl-text-color: #5c5c5c; +$gl-text-color-light: #8c8c8c; +$gl-text-green: #4a2; +$gl-text-red: #d12f19; +$gl-text-orange: #d90; +$gl-link-color: #3084bb; +$gl-dark-link-color: #333; $gl-placeholder-color: #8f8f8f; -$gl-icon-color: $gl-placeholder-color; -$gl-grayish-blue: #7f8fa4; -$gl-gray: $gl-text-color; -$gl-gray-dark: #313236; -$gl-gray-light: $gl-placeholder-color; -$gl-header-color: #4c4e54; +$gl-icon-color: $gl-placeholder-color; +$gl-grayish-blue: #7f8fa4; +$gl-gray: $gl-text-color; +$gl-gray-dark: #313236; +$gl-gray-light: $gl-placeholder-color; +$gl-header-color: #4c4e54; /* * Lists */ -$list-font-size: $gl-font-size; +$list-font-size: $gl-font-size; $list-title-color: $gl-title-color; -$list-text-color: $gl-text-color; +$list-text-color: $gl-text-color; $list-text-height: 42px; /* diff --git a/app/assets/stylesheets/mailers/devise.scss b/app/assets/stylesheets/mailers/devise.scss index 9495c5b3f37..b2bce482fde 100644 --- a/app/assets/stylesheets/mailers/devise.scss +++ b/app/assets/stylesheets/mailers/devise.scss @@ -5,13 +5,13 @@ // Styles defined here are embedded directly into the resulting email HTML via // the `premailer` gem. -$body-background-color: #363636; +$body-background-color: #363636; $message-background-color: #fafafa; -$header-color: #6b4fbb; -$body-color: #444; -$cta-color: #e14329; -$footer-link-color: #7e7e7e; +$header-color: #6b4fbb; +$body-color: #444; +$cta-color: #e14329; +$footer-link-color: #7e7e7e; $font-family: Helvetica, Arial, sans-serif; From 1a04a51b5249d78e4a3cfd47572a5e3dba3c4ad3 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Fri, 21 Oct 2016 14:38:08 +0300 Subject: [PATCH 144/176] Fix events order in user contributions API --- lib/api/users.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/api/users.rb b/lib/api/users.rb index e868f628404..20801b7e294 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -333,10 +333,11 @@ module API user = User.find_by(id: declared(params).id) not_found!('User') unless user - events = user.recent_events. + events = user.events. merge(ProjectsFinder.new.execute(current_user)). references(:project). with_associations. + recent. page(params[:page]) present paginate(events), with: Entities::Event From 3685e867c8bd34b8e56a7fc69c99c9563ce49a68 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Fri, 21 Oct 2016 14:38:32 +0300 Subject: [PATCH 145/176] Get rid of extra .page call --- lib/api/users.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/api/users.rb b/lib/api/users.rb index 20801b7e294..c28e07a76b7 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -337,8 +337,7 @@ module API merge(ProjectsFinder.new.execute(current_user)). references(:project). with_associations. - recent. - page(params[:page]) + recent present paginate(events), with: Entities::Event end From e15f15d5a8fe91746a7c0a038d16a99d682c1b22 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Fri, 21 Oct 2016 17:47:22 +0300 Subject: [PATCH 146/176] Add test for events order in API --- spec/requests/api/users_spec.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index d48752473f3..e911cedb522 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -958,6 +958,26 @@ describe API::API, api: true do expect(joined_event['author']['name']).to eq(user.name) end end + + context 'when there are multiple events' do + let(:old_note) { create(:note_on_issue, project: project) } + let(:new_event) { note.events.first } + let(:old_event) { old_note.events.first } + + before do + EventCreateService.new.leave_note(old_note, user) + + new_event.update(id: 1000) + old_event.update(id: 900) + end + + it 'returns them in the correct order (from newest to oldest determined by ID field)' do + get api("/users/#{user.id}/events", user) + + expect(json_response[0]['target_id']).to eq(note.id) + expect(json_response[1]['target_id']).to eq(old_note.id) + end + end end it 'returns a 404 error if not found' do From ae099857b00c8d9ed48397b782ac6410114183a6 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Sun, 23 Oct 2016 19:00:03 +0300 Subject: [PATCH 147/176] Rewrite events order spec to simulate wrong order without changing ids --- spec/requests/api/users_spec.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index e911cedb522..f07460bbc25 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -959,23 +959,24 @@ describe API::API, api: true do end end - context 'when there are multiple events' do - let(:old_note) { create(:note_on_issue, project: project) } - let(:new_event) { note.events.first } - let(:old_event) { old_note.events.first } + context 'when there are multiple events from different projects' do + let(:another_project) { create(:empty_project) } + let(:notes) { create_list(:note_on_issue, 5, project: [project, another_project].sample) } before do - EventCreateService.new.leave_note(old_note, user) + another_project.add_user(user, :developer) - new_event.update(id: 1000) - old_event.update(id: 900) + notes.each { |note| EventCreateService.new.leave_note(note, user) } end it 'returns them in the correct order (from newest to oldest determined by ID field)' do get api("/users/#{user.id}/events", user) - expect(json_response[0]['target_id']).to eq(note.id) - expect(json_response[1]['target_id']).to eq(old_note.id) + comment_events = json_response.select { |e| e['action_name'] == 'commented on' } + + notes.reverse.each_with_index do |note, i| + expect(note.id).to eq(comment_events[i]['target_id']) + end end end end From 05084a456596c7ebf853f2963551ea5475b34928 Mon Sep 17 00:00:00 2001 From: Airat Shigapov Date: Mon, 24 Oct 2016 16:47:18 +0300 Subject: [PATCH 148/176] Make events order spec deterministic, create only 3 record instead of 5, explicitely check for events order --- spec/requests/api/users_spec.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index f07460bbc25..ae8639d78d5 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -960,23 +960,25 @@ describe API::API, api: true do end context 'when there are multiple events from different projects' do - let(:another_project) { create(:empty_project) } - let(:notes) { create_list(:note_on_issue, 5, project: [project, another_project].sample) } + let(:second_note) { create(:note_on_issue, project: create(:empty_project)) } + let(:third_note) { create(:note_on_issue, project: project) } before do - another_project.add_user(user, :developer) + second_note.project.add_user(user, :developer) - notes.each { |note| EventCreateService.new.leave_note(note, user) } + [second_note, third_note].each do |note| + EventCreateService.new.leave_note(note, user) + end end - it 'returns them in the correct order (from newest to oldest determined by ID field)' do + it 'returns events in the correct order (from newest to oldest)' do get api("/users/#{user.id}/events", user) comment_events = json_response.select { |e| e['action_name'] == 'commented on' } - notes.reverse.each_with_index do |note, i| - expect(note.id).to eq(comment_events[i]['target_id']) - end + expect(comment_events[0]['target_id']).to eq(third_note.id) + expect(comment_events[1]['target_id']).to eq(second_note.id) + expect(comment_events[2]['target_id']).to eq(note.id) end end end From a31a719a4fa29373fd39eab7b84ea4955121e9fd Mon Sep 17 00:00:00 2001 From: winniehell Date: Wed, 31 Aug 2016 21:25:34 +0200 Subject: [PATCH 149/176] Add failing test for #21420 --- .../filter/relative_link_filter_spec.rb | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index 6b58f3e43ee..2bfa51deb20 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -50,14 +50,6 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do end end - shared_examples :relative_to_requested do - it 'rebuilds URL relative to the requested path' do - doc = filter(link('users.md')) - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/blob/#{ref}/doc/api/users.md" - end - end - context 'with a project_wiki' do let(:project_wiki) { double('ProjectWiki') } include_examples :preserve_unchanged @@ -188,12 +180,38 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do context 'when requested path is a file in the repo' do let(:requested_path) { 'doc/api/README.md' } - include_examples :relative_to_requested + it 'rebuilds URL relative to the containing directory' do + doc = filter(link('users.md')) + expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/doc/api/users.md" + end end context 'when requested path is a directory in the repo' do - let(:requested_path) { 'doc/api' } - include_examples :relative_to_requested + let(:requested_path) { 'doc/api/' } + it 'rebuilds URL relative to the directory' do + doc = filter(link('users.md')) + expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/doc/api/users.md" + end + end + + context 'when ref name contains percent sign' do + let(:ref) { '100%branch' } + let(:commit) { project.commit('1b12f15a11fc6e62177bef08f47bc7b5ce50b141') } + let(:requested_path) { 'foo/bar/' } + it 'correctly escapes the ref' do + doc = filter(link('.gitkeep')) + expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/foo/bar/.gitkeep" + end + end + + context 'when requested path is a directory with space in the repo' do + let(:ref) { 'master' } + let(:commit) { project.commit('38008cb17ce1466d8fec2dfa6f6ab8dcfe5cf49e') } + let(:requested_path) { 'with space/' } + it 'does not escape the space twice' do + doc = filter(link('README.md')) + expect(doc.at_css('a')['href']).to eq "/#{project_path}/blob/#{Addressable::URI.escape(ref)}/with%20space/README.md" + end end end From ac2eb1cd012914b730911422e31410a78fe242b3 Mon Sep 17 00:00:00 2001 From: winniehell Date: Fri, 26 Aug 2016 10:01:32 +0200 Subject: [PATCH 150/176] Escape ref and path for relative links (!6050) --- CHANGELOG.md | 1 + lib/banzai/filter/relative_link_filter.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86878e5af6c..8522f2adaf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix extra space on Build sidebar on Firefox !7060 - Fix HipChat notifications rendering (airatshigapov, eisnerd) - Add hover to trash icon in notes !7008 (blackst0ne) + - Escape ref and path for relative links !6050 (winniehell) - Simpler arguments passed to named_route on toggle_award_url helper method - Fix: Backup restore doesn't clear cache - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index 4fa8d05481f..f09d78be0ce 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -52,8 +52,8 @@ module Banzai relative_url_root, context[:project].path_with_namespace, uri_type(file_path), - ref, - file_path + Addressable::URI.escape(ref), + Addressable::URI.escape(file_path) ].compact.join('/').squeeze('/').chomp('/') uri From 791c9d0dd3464eb471262b73a62632ed39d47eb2 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 24 Oct 2016 15:22:06 -0500 Subject: [PATCH 151/176] Enable SingleLinePerSelector in scss-lint --- .scss-lint.yml | 2 +- .../stylesheets/framework/animations.scss | 3 +- app/assets/stylesheets/framework/blocks.scss | 3 +- app/assets/stylesheets/framework/buttons.scss | 3 +- app/assets/stylesheets/framework/common.scss | 6 ++-- .../stylesheets/framework/dropdowns.scss | 6 ++-- app/assets/stylesheets/framework/flash.scss | 6 ++-- .../stylesheets/framework/gitlab-theme.scss | 4 ++- app/assets/stylesheets/framework/header.scss | 13 ++++++--- app/assets/stylesheets/framework/lists.scss | 6 ++-- app/assets/stylesheets/framework/mobile.scss | 12 +++++--- app/assets/stylesheets/framework/nav.scss | 22 +++++++++++---- app/assets/stylesheets/framework/selects.scss | 6 ++-- app/assets/stylesheets/framework/tables.scss | 3 +- .../stylesheets/framework/tw_bootstrap.scss | 3 +- .../stylesheets/framework/typography.scss | 28 +++++++++++++++---- app/assets/stylesheets/highlight/dark.scss | 19 +++++++++---- app/assets/stylesheets/highlight/monokai.scss | 19 +++++++++---- .../stylesheets/highlight/solarized_dark.scss | 19 +++++++++---- .../highlight/solarized_light.scss | 19 +++++++++---- app/assets/stylesheets/highlight/white.scss | 13 ++++++--- app/assets/stylesheets/pages/admin.scss | 3 +- app/assets/stylesheets/pages/ci_projects.scss | 3 +- app/assets/stylesheets/pages/commit.scss | 6 ++-- app/assets/stylesheets/pages/commits.scss | 6 ++-- .../stylesheets/pages/confirmation.scss | 10 +++++-- app/assets/stylesheets/pages/detail_page.scss | 3 +- app/assets/stylesheets/pages/diff.scss | 9 ++++-- app/assets/stylesheets/pages/editor.scss | 4 ++- app/assets/stylesheets/pages/errors.scss | 4 ++- app/assets/stylesheets/pages/issues.scss | 3 +- app/assets/stylesheets/pages/login.scss | 9 ++++-- .../stylesheets/pages/merge_conflicts.scss | 3 +- app/assets/stylesheets/pages/milestone.scss | 3 +- app/assets/stylesheets/pages/note_form.scss | 3 +- app/assets/stylesheets/pages/notes.scss | 3 +- app/assets/stylesheets/pages/pipelines.scss | 12 +++++--- app/assets/stylesheets/pages/profile.scss | 3 +- app/assets/stylesheets/pages/projects.scss | 12 +++++--- app/assets/stylesheets/pages/search.scss | 6 ++-- app/assets/stylesheets/pages/tree.scss | 6 ++-- app/assets/stylesheets/print.scss | 25 ++++++++++++++--- 42 files changed, 247 insertions(+), 104 deletions(-) diff --git a/.scss-lint.yml b/.scss-lint.yml index 5093702519b..f8fc1c077b8 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -172,7 +172,7 @@ linters: # Split selectors onto separate lines after each comma, and have each # individual selector occupy a single line. SingleLinePerSelector: - enabled: false + enabled: true # Commas in lists should be followed by a space. SpaceAfterComma: diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 1e9a45c19b8..0224cc2df21 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -37,7 +37,8 @@ } @include keyframes(pulse) { - from, to { + from, + to { @include webkit-prefix(transform, scale3d(1, 1, 1)); } diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index df2e2ea8d2c..7e168092522 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -128,7 +128,8 @@ position: relative; .avatar-holder { - .avatar, .identicon { + .avatar, + .identicon { margin: 0 auto; float: none; } diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index e6656c2d69a..c0e9c8bf829 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -213,7 +213,8 @@ top: 2px; } - svg, .fa { + svg, + .fa { &:not(:last-child) { margin-right: 3px; } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 800e2dba018..ad5ac589d0f 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -143,7 +143,8 @@ li.note { } } -.wiki_content code, .readme code { +.wiki_content code, +.readme code { background-color: inherit; } @@ -350,7 +351,8 @@ table { margin-right: 10px; } -.alert, .progress { +.alert, +.progress { margin-bottom: $gl-padding; } diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index a839371a6f2..a2d0b1353da 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -275,7 +275,8 @@ a { padding-left: 25px; - &.is-indeterminate, &.is-active { + &.is-indeterminate, + &.is-active { &::before { position: absolute; left: 5px; @@ -373,7 +374,8 @@ } } -.dropdown-input-field, .default-dropdown-input { +.dropdown-input-field, +.default-dropdown-input { width: 100%; min-height: 30px; padding: 0 7px; diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss index a55dcf4a699..a9006de6d3e 100644 --- a/app/assets/stylesheets/framework/flash.scss +++ b/app/assets/stylesheets/framework/flash.scss @@ -18,7 +18,8 @@ margin: 0; } - .flash-notice, .flash-alert { + .flash-notice, + .flash-alert { border-radius: $border-radius-default; .container-fluid, @@ -30,7 +31,8 @@ &.flash-container-page { margin-bottom: 0; - .flash-notice, .flash-alert { + .flash-notice, + .flash-alert { border-radius: 0; } } diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index fe834f4e2f6..3f877d86a26 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -25,7 +25,9 @@ a { color: $color-light; - &:hover, &:focus, &:active { + &:hover, + &:focus, + &:active { background: $color-dark; } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 3a4fdd0da22..142076f65b2 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -15,7 +15,8 @@ header { margin: 8px 0; text-align: center; - .tanuki-logo, img { + .tanuki-logo, + img { height: 36px; } } @@ -54,7 +55,9 @@ header { line-height: 28px; text-align: center; - &:hover, &:focus, &:active { + &:hover, + &:focus, + &:active { background-color: $background-color; } @@ -125,7 +128,8 @@ header { left: -50%; } - svg, img { + svg, + img { height: 36px; } @@ -222,7 +226,8 @@ header { margin: 0; float: none !important; - .visible-xs, .visable-sm { + .visible-xs, + .visable-sm { display: table-cell !important; } } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 4b2627c1b87..48e34a0066e 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -76,14 +76,16 @@ /** light list with border-bottom between li **/ -ul.bordered-list, ul.unstyled-list { +ul.bordered-list, +ul.unstyled-list { @include basic-list; &.top-list { li:first-child { padding-top: 0; - h4, h5 { + h4, + h5 { margin-top: 0; } } diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 9fe390eb09d..c1ed43bc20f 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -79,7 +79,8 @@ padding-left: 15px !important; } - .nav-links, .nav-links { + .nav-links, + .nav-links { li a { font-size: 14px; padding: 19px 10px; @@ -99,18 +100,21 @@ @media (max-width: $screen-sm-max) { .issues-filters { - .milestone-filter, .labels-filter { + .milestone-filter, + .labels-filter { display: none; } } .page-title { - .note-created-ago, .new-issue-link { + .note-created-ago, + .new-issue-link { display: none; } } - .issue_edited_ago, .note_edited_ago { + .issue_edited_ago, + .note_edited_ago { display: none; } diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 899db045b74..fcaf5e18633 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -54,7 +54,9 @@ color: #959494; border-bottom: 2px solid transparent; - &:hover, &:active, &:focus { + &:hover, + &:active, + &:focus { text-decoration: none; outline: none; } @@ -211,7 +213,11 @@ padding-bottom: 0; width: 100%; - .btn, form, .dropdown, .dropdown-menu-toggle, .form-control { + .btn, + form, + .dropdown, + .dropdown-menu-toggle, + .form-control { margin: 0 0 10px; display: block; width: 100%; @@ -245,7 +251,8 @@ } &.adjust { - .nav-text, .nav-controls { + .nav-text, + .nav-controls { width: auto; } } @@ -309,13 +316,15 @@ padding-top: 10px; } - a, i { + a, + i { color: $layout-link-gray; } &.active { - a, i { + a, + i { color: $black; } @@ -328,7 +337,8 @@ } &:hover { - a, i { + a, + i { color: $black; } } diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index e0708c65695..ecdf0be1a05 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -3,7 +3,8 @@ width: 100% !important; } -.select2-container, .select2-container.select2-drop-above { +.select2-container, +.select2-container.select2-drop-above { .select2-choice { background: #fff; border-color: $input-border; @@ -71,7 +72,8 @@ } .select2-container-active { - .select2-choice, .select2-choices { + .select2-choice, + .select2-choices { box-shadow: none; } } diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index b42075c98d0..9a90d3794fd 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -23,7 +23,8 @@ table { } tr { - td, th { + td, + th { padding: 10px $gl-padding; line-height: 20px; vertical-align: middle; diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index f4106641269..59f4594bb83 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -126,7 +126,8 @@ box-shadow: none; .panel-body { - form, pre { + form, + pre { margin: 0; } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 55de9053be5..266a8024809 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -131,12 +131,14 @@ font-weight: inherit; } - ul, ol { + ul, + ol { padding: 0; margin: 3px 0 3px 28px !important; } - ul:dir(rtl), ol:dir(rtl) { + ul:dir(rtl), + ol:dir(rtl) { margin: 3px 28px 3px 0 !important; } @@ -144,7 +146,8 @@ line-height: 1.6em; } - a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] { + a[href*="/uploads/"], + a[href*="storage.googleapis.com/google-code-attachments/"] { &:before { margin-right: 4px; @@ -167,7 +170,12 @@ } /* Link to current header. */ - h1, h2, h3, h4, h5, h6 { + h1, + h2, + h3, + h4, + h5, + h6 { position: relative; a.anchor { @@ -215,7 +223,12 @@ body { margin: 12px 7px; } -h1, h2, h3, h4, h5, h6 { +h1, +h2, +h3, +h4, +h5, +h6 { color: $gl-title-color; font-weight: 600; } @@ -273,7 +286,10 @@ a > code { text-decoration: line-through; } -h1, h2, h3, h4 { +h1, +h2, +h3, +h4 { small { color: $gl-gray; } diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss index a3acee299e3..d22d9b01495 100644 --- a/app/assets/stylesheets/highlight/dark.scss +++ b/app/assets/stylesheets/highlight/dark.scss @@ -1,20 +1,25 @@ /* https://github.com/MozMorris/tomorrow-pygments */ .code.dark { // Line numbers - .line-numbers, .diff-line-num { + .line-numbers, + .diff-line-num { background-color: #1d1f21; } - .diff-line-num, .diff-line-num a { + .diff-line-num, + .diff-line-num a { color: rgba(255, 255, 255, 0.3); } // Code itself - pre.code, .diff-line-num { + pre.code, + .diff-line-num { border-color: #666; } - &, pre.code, .line_holder .line_content { + &, + pre.code, + .line_holder .line_content { background-color: #1d1f21; color: #c5c8c6; } @@ -31,11 +36,13 @@ border-color: darken(#557, 15%); } - .diff-line-num.new, .line_content.new { + .diff-line-num.new, + .line_content.new { @include diff_background(rgba(51, 255, 51, 0.1), rgba(51, 255, 51, 0.2), #808080); } - .diff-line-num.old, .line_content.old { + .diff-line-num.old, + .line_content.old { @include diff_background(rgba(255, 51, 51, 0.2), rgba(255, 51, 51, 0.25), #808080); } diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss index e9228c94db9..db8da8aab10 100644 --- a/app/assets/stylesheets/highlight/monokai.scss +++ b/app/assets/stylesheets/highlight/monokai.scss @@ -1,20 +1,25 @@ /* https://github.com/richleland/pygments-css/blob/master/monokai.css */ .code.monokai { // Line numbers - .line-numbers, .diff-line-num { + .line-numbers, + .diff-line-num { background-color: #272822; } - .diff-line-num, .diff-line-num a { + .diff-line-num, + .diff-line-num a { color: rgba(255, 255, 255, 0.3); } // Code itself - pre.code, .diff-line-num { + pre.code, + .diff-line-num { border-color: #555; } - &, pre.code, .line_holder .line_content { + &, + pre.code, + .line_holder .line_content { background-color: #272822; color: #f8f8f2; } @@ -31,11 +36,13 @@ border-color: darken(#49483e, 15%); } - .diff-line-num.new, .line_content.new { + .diff-line-num.new, + .line_content.new { @include diff_background(rgba(166, 226, 46, 0.1), rgba(166, 226, 46, 0.15), #808080); } - .diff-line-num.old, .line_content.old { + .diff-line-num.old, + .line_content.old { @include diff_background(rgba(254, 147, 140, 0.15), rgba(254, 147, 140, 0.2), #808080); } diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index c3c7773b9e2..a87333146de 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -1,20 +1,25 @@ /* https://gist.github.com/qguv/7936275 */ .code.solarized-dark { // Line numbers - .line-numbers, .diff-line-num { + .line-numbers, + .diff-line-num { background-color: #002b36; } - .diff-line-num, .diff-line-num a { + .diff-line-num, + .diff-line-num a { color: rgba(255, 255, 255, 0.3); } // Code itself - pre.code, .diff-line-num { + pre.code, + .diff-line-num { border-color: #113b46; } - &, pre.code, .line_holder .line_content { + &, + pre.code, + .line_holder .line_content { background-color: #002b36; color: #93a1a1; } @@ -31,11 +36,13 @@ border-color: darken(#174652, 15%); } - .diff-line-num.new, .line_content.new { + .diff-line-num.new, + .line_content.new { @include diff_background(rgba(133, 153, 0, 0.15), rgba(133, 153, 0, 0.25), #113b46); } - .diff-line-num.old, .line_content.old { + .diff-line-num.old, + .line_content.old { @include diff_background(rgba(220, 50, 47, 0.3), rgba(220, 50, 47, 0.25), #113b46); } diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss index 5956a28cafe..faff353ded7 100644 --- a/app/assets/stylesheets/highlight/solarized_light.scss +++ b/app/assets/stylesheets/highlight/solarized_light.scss @@ -7,20 +7,25 @@ .code.solarized-light { // Line numbers - .line-numbers, .diff-line-num { + .line-numbers, + .diff-line-num { background-color: #fdf6e3; } - .diff-line-num, .diff-line-num a { + .diff-line-num, + .diff-line-num a { color: $black-transparent; } // Code itself - pre.code, .diff-line-num { + pre.code, + .diff-line-num { border-color: #c5d0d4; } - &, pre.code, .line_holder .line_content { + &, + pre.code, + .line_holder .line_content { background-color: #fdf6e3; color: #586e75; } @@ -37,11 +42,13 @@ border-color: darken(#ddd8c5, 15%); } - .diff-line-num.new, .line_content.new { + .diff-line-num.new, + .line_content.new { @include diff_background(rgba(133, 153, 0, 0.2), rgba(133, 153, 0, 0.25), #c5d0d4); } - .diff-line-num.old, .line_content.old { + .diff-line-num.old, + .line_content.old { @include diff_background(rgba(220, 50, 47, 0.2), rgba(220, 50, 47, 0.25), #c5d0d4); } diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 6f31a5235c0..d5367d5f3f0 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -7,20 +7,25 @@ .code.white { // Line numbers - .line-numbers, .diff-line-num { + .line-numbers, + .diff-line-num { background-color: $background-color; } - .diff-line-num, .diff-line-num a { + .diff-line-num, + .diff-line-num a { color: $black-transparent; } // Code itself - pre.code, .diff-line-num { + pre.code, + .diff-line-num { border-color: $table-border-gray; } - &, pre.code, .line_holder .line_content { + &, + pre.code, + .line_holder .line_content { background-color: #fff; color: #333; } diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss index 140d589024b..63396a6bb29 100644 --- a/app/assets/stylesheets/pages/admin.scss +++ b/app/assets/stylesheets/pages/admin.scss @@ -56,7 +56,8 @@ padding: 10px; text-align: center; - > div, p { + > div, + p { display: inline; margin: 0; diff --git a/app/assets/stylesheets/pages/ci_projects.scss b/app/assets/stylesheets/pages/ci_projects.scss index 67a9d7d2cf7..87c453a7a27 100644 --- a/app/assets/stylesheets/pages/ci_projects.scss +++ b/app/assets/stylesheets/pages/ci_projects.scss @@ -12,7 +12,8 @@ border-color: $border-color; } - th, td { + th, + td { padding: 10px $gl-padding; } diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index 264e7e01a34..8ecac08137b 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -2,14 +2,16 @@ display: block; } -.commit-author, .commit-committer { +.commit-author, +.commit-committer { display: block; color: #999; font-weight: normal; font-style: italic; } -.commit-author strong, .commit-committer strong { +.commit-author strong, +.commit-committer strong { font-weight: bold; font-style: normal; } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 2b5621e20d6..ad315cfae62 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -63,7 +63,8 @@ display: inline-block; } - .btn-clipboard, .btn-transparent { + .btn-clipboard, + .btn-transparent { padding-left: 0; padding-right: 0; } @@ -162,7 +163,8 @@ .branch-commit { color: $gl-gray; - .commit-id, .commit-row-message { + .commit-id, + .commit-row-message { color: $gl-gray; } } diff --git a/app/assets/stylesheets/pages/confirmation.scss b/app/assets/stylesheets/pages/confirmation.scss index 292225c5261..81e5cee240d 100644 --- a/app/assets/stylesheets/pages/confirmation.scss +++ b/app/assets/stylesheets/pages/confirmation.scss @@ -2,7 +2,12 @@ margin-bottom: 20px; border-bottom: 1px solid #eee; - > h1, h2, h3, h4, h5, h6 { + > h1, + h2, + h3, + h4, + h5, + h6 { font-weight: 400; } @@ -10,7 +15,8 @@ margin-bottom: 20px; } - ul, ol { + ul, + ol { padding-left: 0; } diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss index 2357671c2ae..0f0c0abe7ae 100644 --- a/app/assets/stylesheets/pages/detail_page.scss +++ b/app/assets/stylesheets/pages/detail_page.scss @@ -13,7 +13,8 @@ color: #5c5d5e; } - .issue_created_ago, .author_link { + .issue_created_ago, + .author_link { white-space: nowrap; } } diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index fe6421f8b3f..9627d1c841b 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -124,7 +124,8 @@ } } - .old_line, .new_line { + .old_line, + .new_line { margin: 0; padding: 0; border: none; @@ -281,7 +282,8 @@ position: relative; } - .frame.added, .frame.deleted { + .frame.added, + .frame.deleted { position: absolute; display: block; top: 0; @@ -347,7 +349,8 @@ text-align: center; background: #eee; - ul, li { + ul, + li { list-style: none; margin: 0; padding: 0; diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 029dabd2138..cb8cefaca97 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -91,7 +91,9 @@ } } - .gitignore-selector, .license-selector, .gitlab-ci-yml-selector { + .gitignore-selector, + .license-selector, + .gitlab-ci-yml-selector { .dropdown { line-height: 21px; } diff --git a/app/assets/stylesheets/pages/errors.scss b/app/assets/stylesheets/pages/errors.scss index 32d2d7b1dbf..11309817d31 100644 --- a/app/assets/stylesheets/pages/errors.scss +++ b/app/assets/stylesheets/pages/errors.scss @@ -2,7 +2,9 @@ max-width: 400px; margin: 0 auto; - h1, h2, h3 { + h1, + h2, + h3 { text-align: center; } diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 623da67a239..3e7fc3fa52c 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -43,7 +43,8 @@ ul.related-merge-requests > li { } } -.merge-requests-title, .related-branches-title { +.merge-requests-title, +.related-branches-title { font-size: 16px; font-weight: 600; } diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 8dac6ab999e..82e46377308 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -41,7 +41,8 @@ font-size: 13px; } - .login-box, .omniauth-container { + .login-box, + .omniauth-container { box-shadow: 0 0 0 1px $border-color; border-bottom-right-radius: 2px; border-bottom-left-radius: 2px; @@ -198,7 +199,8 @@ .form-control { - &:active, &:focus { + &:active, + &:focus { background-color: #fff; } } @@ -261,7 +263,8 @@ position: relative; } - .footer-container, hr.footer-fixed { + .footer-container, + hr.footer-fixed { position: absolute; bottom: 0; left: 0; diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss index eed2b0ab7cc..aa8057e4b9d 100644 --- a/app/assets/stylesheets/pages/merge_conflicts.scss +++ b/app/assets/stylesheets/pages/merge_conflicts.scss @@ -101,7 +101,8 @@ $colors: ( @mixin color-scheme($color) { - .header.line_content, .diff-line-num { + .header.line_content, + .diff-line-num { &.origin { background-color: map-get($colors, #{$color}_header_origin_neutral); border-color: map-get($colors, #{$color}_header_origin_neutral); diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index dd6d1783667..13402acd8e1 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -50,7 +50,8 @@ } } -.issues-sortable-list, .merge_requests-sortable-list { +.issues-sortable-list, +.merge_requests-sortable-list { .issuable-detail { display: block; margin-top: 7px; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 17f28959414..c1a3c082cfa 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -24,7 +24,8 @@ display: none; } -.new-note, .note-edit-form { +.new-note, +.note-edit-form { .note-form-actions { margin-top: $gl-padding; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index fffcdc812a7..586f21821f7 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -28,7 +28,8 @@ ul.notes { } } - .note-created-ago, .note-updated-at { + .note-created-ago, + .note-updated-at { white-space: nowrap; } diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 5b8dc7f8c40..f88175365c6 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -248,7 +248,8 @@ font-size: 14px; } - svg, .fa { + svg, + .fa { margin-right: 0; } } @@ -529,7 +530,8 @@ // Connect each build (except for first) with curved lines &:not(:first-child) { - &::after, &::before { + &::after, + &::before { content: ''; top: -49px; position: absolute; @@ -555,7 +557,8 @@ // Connect second build to first build with smaller curved line &:nth-child(2) { - &::after, &::before { + &::after, + &::before { height: 29px; top: -9px; } @@ -570,7 +573,8 @@ .build { // Remove right connecting horizontal line from first build in last stage &:first-child { - &::after, &::before { + &::after, + &::before { border: none; } } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index ed80d2beec2..3f6fdaebc1d 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -253,7 +253,8 @@ } table.u2f-registrations { - th:not(:last-child), td:not(:last-child) { + th:not(:last-child), + td:not(:last-child) { border-right: solid 1px transparent; } } \ No newline at end of file diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index fe7cf3c87e3..f6355941837 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -6,7 +6,8 @@ } } -.no-ssh-key-message, .project-limit-message { +.no-ssh-key-message, +.project-limit-message { background-color: #f28d35; margin-bottom: 0; } @@ -385,7 +386,8 @@ a.deploy-project-label { text-align: center; width: 169px; - &:hover, &.forked { + &:hover, + &.forked { background-color: $row-hover; border-color: $row-hover-border; } @@ -734,7 +736,8 @@ pre.light-well { .table-bordered { border-radius: 1px; - th:not(:last-child), td:not(:last-child) { + th:not(:last-child), + td:not(:last-child) { border-right: solid 1px transparent; } } @@ -757,7 +760,8 @@ pre.light-well { } } -.project-refs-form .dropdown-menu, .dropdown-menu-projects { +.project-refs-form .dropdown-menu, +.dropdown-menu-projects { width: 300px; @media (min-width: $screen-sm-min) { diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index e77f9816d8a..6d472e8293f 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -65,7 +65,8 @@ .search-input-wrap { width: 100%; - .search-icon, .clear-icon { + .search-icon, + .clear-icon { position: absolute; right: 5px; top: 0; @@ -185,7 +186,8 @@ padding-right: $gl-padding + 15px; } - .btn-search, .btn-new { + .btn-search, + .btn-new { width: 100%; margin-top: 5px; diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 6ea7a2b5498..99d53d52119 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -23,7 +23,8 @@ border-bottom: 1px solid $table-border-gray; border-top: 1px solid $table-border-gray; - td, th { + td, + th { line-height: 21px; } @@ -74,7 +75,8 @@ max-width: 320px; vertical-align: middle; - i, a { + i, + a { color: $gl-dark-link-color; } diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index a30b6492572..8239b7e6879 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -1,7 +1,24 @@ -.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; } -.wiki h1 {font-size: 30px;} -.wiki h2 {font-size: 22px;} -.wiki h3 {font-size: 18px; font-weight: bold; } +.wiki h1, +.wiki h2, +.wiki h3, +.wiki h4, +.wiki h5, +.wiki h6 { + margin-top: 17px; +} + +.wiki h1 { + font-size: 30px; +} + +.wiki h2 { + font-size: 22px; +} + +.wiki h3 { + font-size: 18px; + font-weight: bold; +} header, nav, From 03b6108f6f293830e50d113068877be155549fdd Mon Sep 17 00:00:00 2001 From: David Wagner Date: Tue, 18 Oct 2016 23:20:36 +0200 Subject: [PATCH 152/176] Remove redundant class_name and foreign_key overrides They were Rails' default and are unnecessarily overridden. Signed-off-by: David Wagner --- app/models/ci/build.rb | 4 ++-- app/models/ci/pipeline.rb | 6 +++--- app/models/ci/runner.rb | 6 +++--- app/models/ci/runner_project.rb | 4 ++-- app/models/ci/trigger.rb | 4 ++-- app/models/ci/trigger_request.rb | 6 +++--- app/models/ci/variable.rb | 2 +- app/models/commit_status.rb | 2 +- app/models/group.rb | 2 +- app/models/members/group_member.rb | 2 +- app/models/members/project_member.rb | 2 +- app/models/merge_request.rb | 4 ++-- app/models/project.rb | 8 ++++---- app/models/user.rb | 8 ++++---- spec/models/members/project_member_spec.rb | 2 +- spec/models/merge_request_spec.rb | 4 ++-- spec/models/user_spec.rb | 4 ++-- 17 files changed, 35 insertions(+), 35 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index a6b606d13de..bf5f92f8462 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -3,8 +3,8 @@ module Ci include TokenAuthenticatable include AfterCommitQueue - belongs_to :runner, class_name: 'Ci::Runner' - belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' + belongs_to :runner + belongs_to :trigger_request belongs_to :erased_by, class_name: 'User' serialize :options diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index d5c1e03b461..adda3b8f40c 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -7,12 +7,12 @@ module Ci self.table_name = 'ci_commits' - belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id + belongs_to :project, foreign_key: :gl_project_id belongs_to :user has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id - has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id - has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id + has_many :builds, foreign_key: :commit_id + has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id validates_presence_of :sha, unless: :importing? validates_presence_of :ref, unless: :importing? diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 44cb19ece3b..123930273e0 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -6,9 +6,9 @@ module Ci AVAILABLE_SCOPES = %w[specific shared active paused online] FORM_EDITABLE = %i[description tag_list active run_untagged locked] - has_many :builds, class_name: 'Ci::Build' - has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' - has_many :projects, through: :runner_projects, class_name: '::Project', foreign_key: :gl_project_id + has_many :builds + has_many :runner_projects, dependent: :destroy + has_many :projects, through: :runner_projects, foreign_key: :gl_project_id has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index 4b44ffa886e..1f9baeca5b1 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -2,8 +2,8 @@ module Ci class RunnerProject < ActiveRecord::Base extend Ci::Model - belongs_to :runner, class_name: 'Ci::Runner' - belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id + belongs_to :runner + belongs_to :project, foreign_key: :gl_project_id validates_uniqueness_of :runner_id, scope: :gl_project_id end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index a0b19b51a12..62889fe80d8 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -4,8 +4,8 @@ module Ci acts_as_paranoid - belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id - has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' + belongs_to :project, foreign_key: :gl_project_id + has_many :trigger_requests, dependent: :destroy validates_presence_of :token validates_uniqueness_of :token diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index fc674871743..2b807731d0d 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -2,9 +2,9 @@ module Ci class TriggerRequest < ActiveRecord::Base extend Ci::Model - belongs_to :trigger, class_name: 'Ci::Trigger' - belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id - has_many :builds, class_name: 'Ci::Build' + belongs_to :trigger + belongs_to :pipeline, foreign_key: :commit_id + has_many :builds serialize :variables diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 6959223aed9..94d9e2b3208 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -2,7 +2,7 @@ module Ci class Variable < ActiveRecord::Base extend Ci::Model - belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id + belongs_to :project, foreign_key: :gl_project_id validates_uniqueness_of :key, scope: :gl_project_id validates :key, diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 7b554be4f9a..4cb3a69416e 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base self.table_name = 'ci_builds' - belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id + belongs_to :project, foreign_key: :gl_project_id belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id belongs_to :user diff --git a/app/models/group.rb b/app/models/group.rb index 00a595d2705..552e1154df6 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -6,7 +6,7 @@ class Group < Namespace include AccessRequestable include Referable - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source alias_method :members, :group_members has_many :users, through: :group_members has_many :owners, diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index 1b54a85d064..204f34f0269 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -1,7 +1,7 @@ class GroupMember < Member SOURCE_TYPE = 'Namespace' - belongs_to :group, class_name: 'Group', foreign_key: 'source_id' + belongs_to :group, foreign_key: 'source_id' # Make sure group member points only to group as it source default_value_for :source_type, SOURCE_TYPE diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index e4880973117..008fff0857c 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -3,7 +3,7 @@ class ProjectMember < Member include Gitlab::ShellAdapter - belongs_to :project, class_name: 'Project', foreign_key: 'source_id' + belongs_to :project, foreign_key: 'source_id' # Make sure project member points only to project as it source default_value_for :source_type, SOURCE_TYPE diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c476a3bb14e..4872f8b8649 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -6,8 +6,8 @@ class MergeRequest < ActiveRecord::Base include Taskable include Importable - belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project" - belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" + belongs_to :target_project, class_name: "Project" + belongs_to :source_project, class_name: "Project" belongs_to :merge_user, class_name: "User" has_many :merge_request_diffs, dependent: :destroy diff --git a/app/models/project.rb b/app/models/project.rb index af117f0acb0..fbf7012972e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -63,11 +63,11 @@ class Project < ActiveRecord::Base alias_attribute :title, :name # Relations - belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' + belongs_to :creator, class_name: 'User' belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id' belongs_to :namespace - has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id' + has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event' has_many :boards, before_add: :validate_board_limit, dependent: :destroy # Project services @@ -116,7 +116,7 @@ class Project < ActiveRecord::Base has_many :hooks, dependent: :destroy, class_name: 'ProjectHook' has_many :protected_branches, dependent: :destroy - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'ProjectMember' + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source alias_method :members, :project_members has_many :users, through: :project_members @@ -137,7 +137,7 @@ class Project < ActiveRecord::Base has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :project_feature, dependent: :destroy - has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id + has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id diff --git a/app/models/user.rb b/app/models/user.rb index f367f4616fb..521879444d4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -47,7 +47,7 @@ class User < ActiveRecord::Base # # Namespace for personal projects - has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, class_name: "Namespace" + has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id # Profile has_many :keys, dependent: :destroy @@ -66,17 +66,17 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember' + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :users_star_projects, dependent: :destroy has_many :starred_projects, through: :users_star_projects, source: :project - has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet" + has_many :snippets, dependent: :destroy, foreign_key: :author_id has_many :issues, dependent: :destroy, foreign_key: :author_id has_many :notes, dependent: :destroy, foreign_key: :author_id has_many :merge_requests, dependent: :destroy, foreign_key: :author_id - has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event" + has_many :events, dependent: :destroy, foreign_key: :author_id has_many :subscriptions, dependent: :destroy has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event" has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue" diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index b2fe96e2e02..f6b2ec5ae31 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe ProjectMember, models: true do describe 'associations' do - it { is_expected.to belong_to(:project).class_name('Project').with_foreign_key(:source_id) } + it { is_expected.to belong_to(:project).with_foreign_key(:source_id) } end describe 'validations' do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 6e5137602aa..3155eff9ee1 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -6,8 +6,8 @@ describe MergeRequest, models: true do subject { create(:merge_request) } describe 'associations' do - it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') } - it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') } + it { is_expected.to belong_to(:target_project).class_name('Project') } + it { is_expected.to belong_to(:source_project).class_name('Project') } it { is_expected.to belong_to(:merge_user).class_name("User") } it { is_expected.to have_many(:merge_request_diffs).dependent(:destroy) } end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 65b2896930a..10c39b90212 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -15,11 +15,11 @@ describe User, models: true do describe 'associations' do it { is_expected.to have_one(:namespace) } - it { is_expected.to have_many(:snippets).class_name('Snippet').dependent(:destroy) } + it { is_expected.to have_many(:snippets).dependent(:destroy) } it { is_expected.to have_many(:project_members).dependent(:destroy) } it { is_expected.to have_many(:groups) } it { is_expected.to have_many(:keys).dependent(:destroy) } - it { is_expected.to have_many(:events).class_name('Event').dependent(:destroy) } + it { is_expected.to have_many(:events).dependent(:destroy) } it { is_expected.to have_many(:recent_events).class_name('Event') } it { is_expected.to have_many(:issues).dependent(:destroy) } it { is_expected.to have_many(:notes).dependent(:destroy) } From 67d2b2acb3ec88e981f50aa4f4b8a86b9cc1b288 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 24 Oct 2016 16:26:41 -0500 Subject: [PATCH 153/176] replace jquery.cookie vendor script with js.cookie --- app/assets/javascripts/activities.js | 2 +- app/assets/javascripts/application.js | 2 +- app/assets/javascripts/awards_handler.js | 6 +- .../boards/stores/boards_store.js.es6 | 4 +- app/assets/javascripts/cycle_analytics.js.es6 | 4 +- .../merge_conflict_store.js.es6 | 4 +- app/assets/javascripts/merge_request_tabs.js | 4 +- app/assets/javascripts/project.js | 4 +- app/assets/javascripts/right_sidebar.js | 2 +- app/assets/javascripts/sidebar.js.es6 | 4 +- app/assets/javascripts/user.js.es6 | 2 +- spec/javascripts/activities_spec.js.es6 | 2 +- spec/javascripts/awards_handler_spec.js | 8 +- .../boards/boards_store_spec.js.es6 | 4 +- spec/javascripts/boards/issue_spec.js.es6 | 2 +- spec/javascripts/boards/list_spec.js.es6 | 2 +- spec/javascripts/right_sidebar_spec.js | 2 +- vendor/assets/javascripts/jquery.cookie.js | 41 ----- vendor/assets/javascripts/js.cookie.js | 156 ++++++++++++++++++ 19 files changed, 185 insertions(+), 70 deletions(-) delete mode 100644 vendor/assets/javascripts/jquery.cookie.js create mode 100644 vendor/assets/javascripts/js.cookie.js diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index f4f8cf04184..c16ec088f7c 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -24,7 +24,7 @@ var filter = sender.attr("id").split("_")[0]; $('.event-filter .active').removeClass("active"); - $.cookie("event_filter", filter, { + Cookies.set("event_filter", filter, { path: gon.relative_url_root || '/' }); diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 17cbfd0e66f..44da01effde 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -11,13 +11,13 @@ /*= require jquery-ui/effect-highlight */ /*= require jquery-ui/sortable */ /*= require jquery_ujs */ -/*= require jquery.cookie */ /*= require jquery.endless-scroll */ /*= require jquery.highlight */ /*= require jquery.waitforimages */ /*= require jquery.atwho */ /*= require jquery.scrollTo */ /*= require jquery.turbolinks */ +/*= require js.cookie */ /*= require turbolinks */ /*= require autosave */ /*= require bootstrap/affix */ diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 44af1c135a0..40f074fe089 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -322,7 +322,7 @@ var frequentlyUsedEmojis; frequentlyUsedEmojis = this.getFrequentlyUsedEmojis(); frequentlyUsedEmojis.push(emoji); - return $.cookie('frequently_used_emojis', frequentlyUsedEmojis.join(','), { + Cookies.set('frequently_used_emojis', frequentlyUsedEmojis.join(','), { path: gon.relative_url_root || '/', expires: 365 }); @@ -330,13 +330,13 @@ AwardsHandler.prototype.getFrequentlyUsedEmojis = function() { var frequentlyUsedEmojis; - frequentlyUsedEmojis = ($.cookie('frequently_used_emojis') || '').split(','); + frequentlyUsedEmojis = (Cookies.get('frequently_used_emojis') || '').split(','); return _.compact(_.uniq(frequentlyUsedEmojis)); }; AwardsHandler.prototype.renderFrequentlyUsedBlock = function() { var emoji, frequentlyUsedEmojis, i, len, ul; - if ($.cookie('frequently_used_emojis')) { + if (Cookies.get('frequently_used_emojis')) { frequentlyUsedEmojis = this.getFrequentlyUsedEmojis(); ul = $("
    "); for (i = 0, len = frequentlyUsedEmojis.length; i < len; i++) { diff --git a/app/assets/javascripts/boards/stores/boards_store.js.es6 b/app/assets/javascripts/boards/stores/boards_store.js.es6 index bd07ee0c161..a12d3a59265 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js.es6 +++ b/app/assets/javascripts/boards/stores/boards_store.js.es6 @@ -58,12 +58,12 @@ removeBlankState () { this.removeList('blank'); - $.cookie('issue_board_welcome_hidden', 'true', { + Cookies.set('issue_board_welcome_hidden', 'true', { expires: 365 * 10 }); }, welcomeIsHidden () { - return $.cookie('issue_board_welcome_hidden') === 'true'; + return Cookies.get('issue_board_welcome_hidden') === 'true'; }, removeList (id, type = 'blank') { const list = this.findList('id', id, type); diff --git a/app/assets/javascripts/cycle_analytics.js.es6 b/app/assets/javascripts/cycle_analytics.js.es6 index 20791bab942..da473a9bffc 100644 --- a/app/assets/javascripts/cycle_analytics.js.es6 +++ b/app/assets/javascripts/cycle_analytics.js.es6 @@ -6,7 +6,7 @@ const store = gl.cycleAnalyticsStore = { isLoading: true, hasError: false, - isHelpDismissed: $.cookie(COOKIE_NAME), + isHelpDismissed: Cookies.get(COOKIE_NAME), analytics: {} }; @@ -75,7 +75,7 @@ dismissLanding() { store.isHelpDismissed = true; - $.cookie(COOKIE_NAME, true, { + Cookies.set(COOKIE_NAME, true, { path: gon.relative_url_root || '/' }); } diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 index 5c5c65f29d4..8d202c8500a 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -1,7 +1,7 @@ ((global) => { global.mergeConflicts = global.mergeConflicts || {}; - const diffViewType = $.cookie('diff_view'); + const diffViewType = Cookies.get('diff_view'); const HEAD_HEADER_TEXT = 'HEAD//our changes'; const ORIGIN_HEADER_TEXT = 'origin//their changes'; const HEAD_BUTTON_TITLE = 'Use ours'; @@ -180,7 +180,7 @@ this.state.diffView = viewType; this.state.isParallel = viewType === VIEW_TYPES.PARALLEL; - $.cookie('diff_view', viewType, { + Cookies.set('diff_view', viewType, { path: gon.relative_url_root || '/' }); }, diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 3dde979185b..18cc4d4ca93 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -3,7 +3,7 @@ // Handles persisting and restoring the current tab selection and lazily-loading // content on the MergeRequests#show page. // -/*= require jquery.cookie */ +/*= require js.cookie */ // // ### Example Markup @@ -368,7 +368,7 @@ MergeRequestTabs.prototype.expandView = function() { var $gutterIcon; - if ($.cookie('collapsed_gutter') === 'true') { + if (Cookies.get('collapsed_gutter') === 'true') { return; } $gutterIcon = $('.js-sidebar-toggle i:visible'); diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index a6c015299a0..6505d30f1ca 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -23,14 +23,14 @@ return $(this).parents('form').submit(); }); $('.hide-no-ssh-message').on('click', function(e) { - $.cookie('hide_no_ssh_message', 'false', { + Cookies.set('hide_no_ssh_message', 'false', { path: gon.relative_url_root || '/' }); $(this).parents('.no-ssh-key-message').remove(); return e.preventDefault(); }); $('.hide-no-password-message').on('click', function(e) { - $.cookie('hide_no_password_message', 'false', { + Cookies.set('hide_no_password_message', 'false', { path: gon.relative_url_root || '/' }); $(this).parents('.no-password-message').remove(); diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index e3d5f413c77..a2a7ed3b6c3 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -29,7 +29,7 @@ $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); } if (!triggered) { - return $.cookie("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'), { + return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'), { path: gon.relative_url_root || '/' }); } diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6 index 755fac8107b..d7d49271f9e 100644 --- a/app/assets/javascripts/sidebar.js.es6 +++ b/app/assets/javascripts/sidebar.js.es6 @@ -28,7 +28,7 @@ } init() { - this.isPinned = $.cookie(pinnedStateCookie) === 'true'; + this.isPinned = Cookies.get(pinnedStateCookie) === 'true'; this.isExpanded = ( window.innerWidth >= sidebarBreakpoint && $(pageSelector).hasClass(expandedPageClass) @@ -62,7 +62,7 @@ if (!this.isPinned) { this.isExpanded = false; } - $.cookie(pinnedStateCookie, this.isPinned ? 'true' : 'false', { + Cookies.set(pinnedStateCookie, this.isPinned ? 'true' : 'false', { path: gon.relative_url_root || '/', expires: 3650 }); diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6 index 0f97924d94e..d3f7c79c696 100644 --- a/app/assets/javascripts/user.js.es6 +++ b/app/assets/javascripts/user.js.es6 @@ -24,7 +24,7 @@ $('.hide-project-limit-message').on('click', e => { e.preventDefault(); const path = gon.relative_url_root || '/'; - $.cookie('hide_project_limit_message', 'false', { + Cookies.set('hide_project_limit_message', 'false', { path: path }); $(this).parents('.project-limit-message').remove(); diff --git a/spec/javascripts/activities_spec.js.es6 b/spec/javascripts/activities_spec.js.es6 index 743b15460c6..6699dee3cf7 100644 --- a/spec/javascripts/activities_spec.js.es6 +++ b/spec/javascripts/activities_spec.js.es6 @@ -1,4 +1,4 @@ -/*= require jquery.cookie.js */ +/*= require js.cookie.js */ /*= require jquery.endless-scroll.js */ /*= require pager */ /*= require activities */ diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index 019ce3b0702..5ce7892c838 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -1,7 +1,7 @@ /*= require awards_handler */ /*= require jquery */ -/*= require jquery.cookie */ +/*= require js.cookie */ /*= require ./fixtures/emoji_menu */ (function() { @@ -44,7 +44,7 @@ spyOn(jQuery, 'get').and.callFake(function(req, cb) { return cb(window.emojiMenu); }); - spyOn(jQuery, 'cookie'); + spyOn(Cookies, 'set'); }); afterEach(function() { // restore original url root value @@ -194,7 +194,7 @@ it('should set a cookie with the correct default path', function() { gon.relative_url_root = ''; awardsHandler.addEmojiToFrequentlyUsedList('sunglasses'); - expect(jQuery.cookie) + expect(Cookies.set) .toHaveBeenCalledWith('frequently_used_emojis', 'sunglasses', { path: '/', expires: 365 @@ -204,7 +204,7 @@ it('should set a cookie with the correct custom root path', function() { gon.relative_url_root = '/gitlab/subdir'; awardsHandler.addEmojiToFrequentlyUsedList('alien'); - expect(jQuery.cookie) + expect(Cookies.set) .toHaveBeenCalledWith('frequently_used_emojis', 'alien', { path: '/gitlab/subdir', expires: 365 diff --git a/spec/javascripts/boards/boards_store_spec.js.es6 b/spec/javascripts/boards/boards_store_spec.js.es6 index 15c305ce321..8402a996192 100644 --- a/spec/javascripts/boards/boards_store_spec.js.es6 +++ b/spec/javascripts/boards/boards_store_spec.js.es6 @@ -1,6 +1,6 @@ //= require jquery //= require jquery_ujs -//= require jquery.cookie +//= require js.cookie //= require vue //= require vue-resource //= require lib/utils/url_utility @@ -17,7 +17,7 @@ gl.boardService = new BoardService('/test/issue-boards/board', '1'); gl.issueBoards.BoardsStore.create(); - $.cookie('issue_board_welcome_hidden', 'false'); + Cookies.set('issue_board_welcome_hidden', 'false'); }); describe('Store', () => { diff --git a/spec/javascripts/boards/issue_spec.js.es6 b/spec/javascripts/boards/issue_spec.js.es6 index 328c6f82ab5..cfece7ea7e8 100644 --- a/spec/javascripts/boards/issue_spec.js.es6 +++ b/spec/javascripts/boards/issue_spec.js.es6 @@ -1,6 +1,6 @@ //= require jquery //= require jquery_ujs -//= require jquery.cookie +//= require js.cookie //= require vue //= require vue-resource //= require lib/utils/url_utility diff --git a/spec/javascripts/boards/list_spec.js.es6 b/spec/javascripts/boards/list_spec.js.es6 index ec78d82e919..7b2c0945ce8 100644 --- a/spec/javascripts/boards/list_spec.js.es6 +++ b/spec/javascripts/boards/list_spec.js.es6 @@ -1,6 +1,6 @@ //= require jquery //= require jquery_ujs -//= require jquery.cookie +//= require js.cookie //= require vue //= require vue-resource //= require lib/utils/url_utility diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index c937a4706f7..5aa8d5b5826 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -1,7 +1,7 @@ /*= require right_sidebar */ /*= require jquery */ -/*= require jquery.cookie */ +/*= require js.cookie */ (function() { var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState; diff --git a/vendor/assets/javascripts/jquery.cookie.js b/vendor/assets/javascripts/jquery.cookie.js deleted file mode 100644 index 6a3e394b403..00000000000 --- a/vendor/assets/javascripts/jquery.cookie.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * jQuery Cookie plugin - * - * Copyright (c) 2010 Klaus Hartl (stilbuero.de) - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - */ -jQuery.cookie = function (key, value, options) { - - // key and at least value given, set cookie... - if (arguments.length > 1 && String(value) !== "[object Object]") { - options = jQuery.extend({}, options); - - if (value === null || value === undefined) { - options.expires = -1; - } - - if (typeof options.expires === 'number') { - var days = options.expires, t = options.expires = new Date(); - t.setDate(t.getDate() + days); - } - - value = String(value); - - return (document.cookie = [ - encodeURIComponent(key), '=', - options.raw ? value : encodeURIComponent(value), - options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE - options.path ? '; path=' + options.path : '', - options.domain ? '; domain=' + options.domain : '', - options.secure ? '; secure' : '' - ].join('')); - } - - // key and possibly options given, get cookie... - options = value || {}; - var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent; - return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null; -}; diff --git a/vendor/assets/javascripts/js.cookie.js b/vendor/assets/javascripts/js.cookie.js new file mode 100644 index 00000000000..92dbba162c4 --- /dev/null +++ b/vendor/assets/javascripts/js.cookie.js @@ -0,0 +1,156 @@ +/*! + * JavaScript Cookie v2.1.3 + * https://github.com/js-cookie/js-cookie + * + * Copyright 2006, 2015 Klaus Hartl & Fagner Brack + * Released under the MIT license + */ +;(function (factory) { + var registeredInModuleLoader = false; + if (typeof define === 'function' && define.amd) { + define(factory); + registeredInModuleLoader = true; + } + if (typeof exports === 'object') { + module.exports = factory(); + registeredInModuleLoader = true; + } + if (!registeredInModuleLoader) { + var OldCookies = window.Cookies; + var api = window.Cookies = factory(); + api.noConflict = function () { + window.Cookies = OldCookies; + return api; + }; + } +}(function () { + function extend () { + var i = 0; + var result = {}; + for (; i < arguments.length; i++) { + var attributes = arguments[ i ]; + for (var key in attributes) { + result[key] = attributes[key]; + } + } + return result; + } + + function init (converter) { + function api (key, value, attributes) { + var result; + if (typeof document === 'undefined') { + return; + } + + // Write + + if (arguments.length > 1) { + attributes = extend({ + path: '/' + }, api.defaults, attributes); + + if (typeof attributes.expires === 'number') { + var expires = new Date(); + expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5); + attributes.expires = expires; + } + + try { + result = JSON.stringify(value); + if (/^[\{\[]/.test(result)) { + value = result; + } + } catch (e) {} + + if (!converter.write) { + value = encodeURIComponent(String(value)) + .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent); + } else { + value = converter.write(value, key); + } + + key = encodeURIComponent(String(key)); + key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent); + key = key.replace(/[\(\)]/g, escape); + + return (document.cookie = [ + key, '=', value, + attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + attributes.path ? '; path=' + attributes.path : '', + attributes.domain ? '; domain=' + attributes.domain : '', + attributes.secure ? '; secure' : '' + ].join('')); + } + + // Read + + if (!key) { + result = {}; + } + + // To prevent the for loop in the first place assign an empty array + // in case there are no cookies at all. Also prevents odd result when + // calling "get()" + var cookies = document.cookie ? document.cookie.split('; ') : []; + var rdecode = /(%[0-9A-Z]{2})+/g; + var i = 0; + + for (; i < cookies.length; i++) { + var parts = cookies[i].split('='); + var cookie = parts.slice(1).join('='); + + if (cookie.charAt(0) === '"') { + cookie = cookie.slice(1, -1); + } + + try { + var name = parts[0].replace(rdecode, decodeURIComponent); + cookie = converter.read ? + converter.read(cookie, name) : converter(cookie, name) || + cookie.replace(rdecode, decodeURIComponent); + + if (this.json) { + try { + cookie = JSON.parse(cookie); + } catch (e) {} + } + + if (key === name) { + result = cookie; + break; + } + + if (!key) { + result[name] = cookie; + } + } catch (e) {} + } + + return result; + } + + api.set = api; + api.get = function (key) { + return api.call(api, key); + }; + api.getJSON = function () { + return api.apply({ + json: true + }, [].slice.call(arguments)); + }; + api.defaults = {}; + + api.remove = function (key, attributes) { + api(key, '', extend(attributes, { + expires: -1 + })); + }; + + api.withConverter = init; + + return api; + } + + return init(function () {}); +})); \ No newline at end of file From 1c0213f28e423b28c17bf7b377086a782ba88b50 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 24 Oct 2016 16:31:01 -0500 Subject: [PATCH 154/176] set default path for all calls to Cookies.set() --- app/assets/javascripts/application.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 44da01effde..817337daf54 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -149,6 +149,10 @@ $document = $(document); $window = $(window); $body = $('body'); + + // Set the default path for all cookies to GitLab's root directory + Cookies.defaults.path = gon.relative_url_root || '/'; + gl.utils.preventDisabledButtons(); bootstrapBreakpoint = bp.getBreakpointSize(); $(".nav-sidebar").niceScroll({ From f8520f5d096a7b62649c7465491003b3317c5e75 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 24 Oct 2016 16:54:38 -0500 Subject: [PATCH 155/176] remove manual Cookie.set "path" option in favor of global setting --- app/assets/javascripts/activities.js | 4 +--- app/assets/javascripts/awards_handler.js | 5 +--- app/assets/javascripts/cycle_analytics.js.es6 | 4 +--- .../merge_conflict_store.js.es6 | 4 +--- app/assets/javascripts/project.js | 8 ++----- app/assets/javascripts/right_sidebar.js | 4 +--- app/assets/javascripts/sidebar.js.es6 | 5 +--- app/assets/javascripts/user.js.es6 | 5 +--- spec/javascripts/awards_handler_spec.js | 23 ------------------- 9 files changed, 9 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index c16ec088f7c..e9287d015c3 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -24,9 +24,7 @@ var filter = sender.attr("id").split("_")[0]; $('.event-filter .active').removeClass("active"); - Cookies.set("event_filter", filter, { - path: gon.relative_url_root || '/' - }); + Cookies.set("event_filter", filter); sender.closest('li').toggleClass("active"); }; diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 40f074fe089..d5966db0bf9 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -322,10 +322,7 @@ var frequentlyUsedEmojis; frequentlyUsedEmojis = this.getFrequentlyUsedEmojis(); frequentlyUsedEmojis.push(emoji); - Cookies.set('frequently_used_emojis', frequentlyUsedEmojis.join(','), { - path: gon.relative_url_root || '/', - expires: 365 - }); + Cookies.set('frequently_used_emojis', frequentlyUsedEmojis.join(','), { expires: 365 }); }; AwardsHandler.prototype.getFrequentlyUsedEmojis = function() { diff --git a/app/assets/javascripts/cycle_analytics.js.es6 b/app/assets/javascripts/cycle_analytics.js.es6 index da473a9bffc..43c60d25248 100644 --- a/app/assets/javascripts/cycle_analytics.js.es6 +++ b/app/assets/javascripts/cycle_analytics.js.es6 @@ -75,9 +75,7 @@ dismissLanding() { store.isHelpDismissed = true; - Cookies.set(COOKIE_NAME, true, { - path: gon.relative_url_root || '/' - }); + Cookies.set(COOKIE_NAME, true); } initDropdown() { diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 index 8d202c8500a..791db57262f 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 +++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js.es6 @@ -180,9 +180,7 @@ this.state.diffView = viewType; this.state.isParallel = viewType === VIEW_TYPES.PARALLEL; - Cookies.set('diff_view', viewType, { - path: gon.relative_url_root || '/' - }); + Cookies.set('diff_view', viewType); }, getHeadHeaderLine(id) { diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js index 6505d30f1ca..b370e6bb9f8 100644 --- a/app/assets/javascripts/project.js +++ b/app/assets/javascripts/project.js @@ -23,16 +23,12 @@ return $(this).parents('form').submit(); }); $('.hide-no-ssh-message').on('click', function(e) { - Cookies.set('hide_no_ssh_message', 'false', { - path: gon.relative_url_root || '/' - }); + Cookies.set('hide_no_ssh_message', 'false'); $(this).parents('.no-ssh-key-message').remove(); return e.preventDefault(); }); $('.hide-no-password-message').on('click', function(e) { - Cookies.set('hide_no_password_message', 'false', { - path: gon.relative_url_root || '/' - }); + Cookies.set('hide_no_password_message', 'false'); $(this).parents('.no-password-message').remove(); return e.preventDefault(); }); diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index a2a7ed3b6c3..c4b9557c421 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -29,9 +29,7 @@ $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded'); } if (!triggered) { - return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed'), { - path: gon.relative_url_root || '/' - }); + return Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); } }); return $(document).off('click', '.js-issuable-todo').on('click', '.js-issuable-todo', this.toggleTodo); diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6 index d7d49271f9e..73c359af7f6 100644 --- a/app/assets/javascripts/sidebar.js.es6 +++ b/app/assets/javascripts/sidebar.js.es6 @@ -62,10 +62,7 @@ if (!this.isPinned) { this.isExpanded = false; } - Cookies.set(pinnedStateCookie, this.isPinned ? 'true' : 'false', { - path: gon.relative_url_root || '/', - expires: 3650 - }); + Cookies.set(pinnedStateCookie, this.isPinned ? 'true' : 'false', { expires: 3650 }); this.renderState(); } diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6 index d3f7c79c696..111ea7b2ec4 100644 --- a/app/assets/javascripts/user.js.es6 +++ b/app/assets/javascripts/user.js.es6 @@ -23,10 +23,7 @@ hideProjectLimitMessage() { $('.hide-project-limit-message').on('click', e => { e.preventDefault(); - const path = gon.relative_url_root || '/'; - Cookies.set('hide_project_limit_message', 'false', { - path: path - }); + Cookies.set('hide_project_limit_message', 'false'); $(this).parents('.project-limit-message').remove(); }); } diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js index 5ce7892c838..f5e400a2e39 100644 --- a/spec/javascripts/awards_handler_spec.js +++ b/spec/javascripts/awards_handler_spec.js @@ -44,7 +44,6 @@ spyOn(jQuery, 'get').and.callFake(function(req, cb) { return cb(window.emojiMenu); }); - spyOn(Cookies, 'set'); }); afterEach(function() { // restore original url root value @@ -190,28 +189,6 @@ return expect($thumbsUpEmoji.data("original-title")).toBe('sam'); }); }); - describe('::addEmojiToFrequentlyUsedList', function() { - it('should set a cookie with the correct default path', function() { - gon.relative_url_root = ''; - awardsHandler.addEmojiToFrequentlyUsedList('sunglasses'); - expect(Cookies.set) - .toHaveBeenCalledWith('frequently_used_emojis', 'sunglasses', { - path: '/', - expires: 365 - }) - ; - }); - it('should set a cookie with the correct custom root path', function() { - gon.relative_url_root = '/gitlab/subdir'; - awardsHandler.addEmojiToFrequentlyUsedList('alien'); - expect(Cookies.set) - .toHaveBeenCalledWith('frequently_used_emojis', 'alien', { - path: '/gitlab/subdir', - expires: 365 - }) - ; - }); - }); describe('search', function() { return it('should filter the emoji', function() { $('.js-add-award').eq(0).click(); From 0dcdd43544a3111c6addb7f2d3af3b6a66e1ad2f Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 24 Oct 2016 18:10:33 -0500 Subject: [PATCH 156/176] update CHANGELOG.md to reflect !7085 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86878e5af6c..3d184f64867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Add hover to trash icon in notes !7008 (blackst0ne) - Simpler arguments passed to named_route on toggle_award_url helper method - Fix: Backup restore doesn't clear cache + - Replace jquery.cookie plugin with js.cookie !7085 - Use MergeRequestsClosingIssues cache data on Issue#closed_by_merge_requests method - Fix Sign in page 'Forgot your password?' link overlaps on medium-large screens - Fix documents and comments on Build API `scope` From 541b9eb65828719c6dea20bc750873f2882d9d07 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Mon, 24 Oct 2016 21:44:24 -0400 Subject: [PATCH 157/176] Fix rubocop build error --- spec/features/login_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index a3e1ca923ef..76bcfbe523a 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -257,7 +257,7 @@ feature 'Login', feature: true do end end - def ensure_tab_pane_correctness(visit_path=true) + def ensure_tab_pane_correctness(visit_path = true) if visit_path visit new_user_session_path end From fed3f718d8e2223305fb1c0ab4e72d514f50a8f6 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 25 Oct 2016 11:03:49 +0530 Subject: [PATCH 158/176] Fix `User#to_reference` 1. Changes in 8.13 require `Referable`s that don't have a project reference to accept two arguments - `from_project` and `target_project`. 2. `User#to_reference` was not changed to accept the `target_project` (even though it is not used). Moving an issue containing a user reference would throw a "invalid number of arguments" exception. Fixes #23662 --- app/models/user.rb | 2 +- spec/services/issues/move_service_spec.rb | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index f367f4616fb..9181db40eb4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -309,7 +309,7 @@ class User < ActiveRecord::Base username end - def to_reference(_from_project = nil) + def to_reference(_from_project = nil, _target_project = nil) "#{self.class.reference_prefix}#{username}" end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 93bf0f64963..34ec13c43c6 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -207,10 +207,10 @@ describe Issues::MoveService, services: true do end end - describe 'rewritting references' do + describe 'rewriting references' do include_context 'issue move executed' - context 'issue reference' do + context 'issue references' do let(:another_issue) { create(:issue, project: old_project) } let(:description) { "Some description #{another_issue.to_reference}" } @@ -219,6 +219,16 @@ describe Issues::MoveService, services: true do .to eq "Some description #{old_project.to_reference}#{another_issue.to_reference}" end end + + context "user references" do + let(:another_issue) { create(:issue, project: old_project) } + let(:description) { "Some description #{user.to_reference}" } + + it "doesn't throw any errors for issues containing user references" do + expect(new_issue.description) + .to eq "Some description #{user.to_reference}" + end + end end context 'moving to same project' do From ddafea060d4b607cd3f5c29e947cdbf6483dcd5d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 23 Oct 2016 16:46:21 -0700 Subject: [PATCH 159/176] Fix bug where labels would be assigned to issues that were moved If you attempt to move an issue from one project to another and leave labels blank, LabelsFinder would assign all labels in the new project to that issue. The issue is that :title is passed along to the Finder, but since it appears empty no filtering is done. As a result, all labels in the group are returned. This fix handles that case. Closes #23668 --- CHANGELOG.md | 1 + app/finders/labels_finder.rb | 4 ++++ spec/finders/labels_finder_spec.rb | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86878e5af6c..1e2150b9ca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Refactor email, use setter method instead AR callbacks for email attribute (Semyon Pupkov) ## 8.13.1 (unreleased) + - Fix bug where labels would be assigned to issues that were moved - Fix error in generating labels - Fix reply-by-email not working due to queue name mismatch - Fixed hidden pipeline graph on commit and MR page !6895 diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 6ace14a4bb5..2291c64b84d 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -35,6 +35,10 @@ class LabelsFinder < UnionFinder end def with_title(items) + # Match no labels if an empty title is supplied to avoid matching all + # labels (e.g. when an issue is moved) + return Label.none if params[:title] && params[:title].empty? + items = items.where(title: title) if title items end diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index 27acc464ea2..114399ea3dc 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -64,6 +64,12 @@ describe LabelsFinder do expect(finder.execute).to eq [group_label_2] end + + it 'returns no labels if empty titles are supplied' do + finder = described_class.new(user, title: []) + + expect(finder.execute).to be_empty + end end end end From 3e941b136d335b789c7efea46fc3b8af294d3f47 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 23 Oct 2016 20:57:06 -0700 Subject: [PATCH 160/176] Add spec in Issues::MoveService to fix label assignment regression --- spec/services/issues/move_service_spec.rb | 31 +++++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 93bf0f64963..8c33389003a 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -23,15 +23,16 @@ describe Issues::MoveService, services: true do old_project.team << [user, :reporter] new_project.team << [user, :reporter] - ['label1', 'label2'].each do |label| + labels = 2.times.map { |x| "label%d" % (x + 1) } + + labels.each do |label| old_issue.labels << create(:label, project_id: old_project.id, title: label) - end - new_project.labels << create(:label, title: 'label1') - new_project.labels << create(:label, title: 'label2') - end + new_project.labels << create(:label, title: label) + end + end end describe '#execute' do @@ -277,5 +278,25 @@ describe Issues::MoveService, services: true do it { expect { move }.to raise_error(StandardError, /permissions/) } end end + + context 'movable issue with no assigned labels' do + before do + old_project.team << [user, :reporter] + new_project.team << [user, :reporter] + + labels = 2.times.map { |x| "label%d" % (x + 1) } + + labels.each do |label| + new_project.labels << create(:label, title: label) + end + end + + include_context 'issue move executed' + + it 'does not assign labels to new issue' do + expected_label_titles = new_issue.reload.labels.map(&:title) + expect(expected_label_titles.size).to eq 0 + end + end end end From 7c0ccbaac4aad5057f76d4f62b3a892aae64e190 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 24 Oct 2016 10:05:21 +0200 Subject: [PATCH 161/176] Fix Rubocop offenses in issue move specs --- spec/services/issues/move_service_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 8c33389003a..302eef8bf7e 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -23,7 +23,7 @@ describe Issues::MoveService, services: true do old_project.team << [user, :reporter] new_project.team << [user, :reporter] - labels = 2.times.map { |x| "label%d" % (x + 1) } + labels = Array.new(2) { |x| "label%d" % (x + 1) } labels.each do |label| old_issue.labels << create(:label, @@ -32,7 +32,7 @@ describe Issues::MoveService, services: true do new_project.labels << create(:label, title: label) end - end + end end describe '#execute' do @@ -284,7 +284,7 @@ describe Issues::MoveService, services: true do old_project.team << [user, :reporter] new_project.team << [user, :reporter] - labels = 2.times.map { |x| "label%d" % (x + 1) } + labels = Array.new(2) { |x| "label%d" % (x + 1) } labels.each do |label| new_project.labels << create(:label, title: label) From af4d16d9b8eada31be308f87ab596e34e9907e73 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 24 Oct 2016 06:25:18 -0700 Subject: [PATCH 162/176] Allow the use of params[:name] when filtering labels --- app/finders/labels_finder.rb | 13 ++++++++++--- spec/finders/labels_finder_spec.rb | 12 ++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 2291c64b84d..032172fdfa8 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -37,10 +37,13 @@ class LabelsFinder < UnionFinder def with_title(items) # Match no labels if an empty title is supplied to avoid matching all # labels (e.g. when an issue is moved) - return Label.none if params[:title] && params[:title].empty? + return items.none if raw_title && raw_title.empty? - items = items.where(title: title) if title - items + if title + items = items.where(title: title) + else + items + end end def group_id @@ -59,6 +62,10 @@ class LabelsFinder < UnionFinder params[:title].presence || params[:name].presence end + def raw_title + params[:title] || params[:name] + end + def project return @project if defined?(@project) diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index 114399ea3dc..eb8df8e2bb2 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -65,11 +65,23 @@ describe LabelsFinder do expect(finder.execute).to eq [group_label_2] end + it 'returns label with title alias' do + finder = described_class.new(user, name: 'Group Label 2') + + expect(finder.execute).to eq [group_label_2] + end + it 'returns no labels if empty titles are supplied' do finder = described_class.new(user, title: []) expect(finder.execute).to be_empty end + + it 'returns no labels if empty names are supplied' do + finder = described_class.new(user, name: []) + + expect(finder.execute).to be_empty + end end end end From ce256c28f2012a9c20fd1872fa91214b402528bf Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 24 Oct 2016 06:43:13 -0700 Subject: [PATCH 163/176] Improve label filtering implementation --- app/finders/labels_finder.rb | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 032172fdfa8..8a85f7a2952 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -35,13 +35,11 @@ class LabelsFinder < UnionFinder end def with_title(items) - # Match no labels if an empty title is supplied to avoid matching all - # labels (e.g. when an issue is moved) - return items.none if raw_title && raw_title.empty? - if title - items = items.where(title: title) - else + items.where(title: title) + elsif params[:title] || params[:name] # empty input, should match nothing + items.none + else # not filtering items end end @@ -62,10 +60,6 @@ class LabelsFinder < UnionFinder params[:title].presence || params[:name].presence end - def raw_title - params[:title] || params[:name] - end - def project return @project if defined?(@project) From 02f835c105df6c46d5c73468eedf1e2b0a0793b5 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 24 Oct 2016 23:04:38 -0700 Subject: [PATCH 164/176] Improve readability and add specs for label filtering --- app/finders/labels_finder.rb | 13 +++++-------- spec/finders/labels_finder_spec.rb | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 8a85f7a2952..95e62cdb02a 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -35,13 +35,10 @@ class LabelsFinder < UnionFinder end def with_title(items) - if title - items.where(title: title) - elsif params[:title] || params[:name] # empty input, should match nothing - items.none - else # not filtering - items - end + return items if title.nil? + return items.none if title.blank? + + items.where(title: title) end def group_id @@ -57,7 +54,7 @@ class LabelsFinder < UnionFinder end def title - params[:title].presence || params[:name].presence + params[:title] || params[:name] end def project diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index eb8df8e2bb2..10cfb66ec1c 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -38,6 +38,14 @@ describe LabelsFinder do expect(finder.execute).to eq [group_label_2, group_label_3, project_label_1, group_label_1, project_label_2, project_label_4] end + + it 'returns labels available if nil title is supplied' do + group_2.add_developer(user) + # params[:title] will return `nil` regardless whether it is specified + finder = described_class.new(user, title: nil) + + expect(finder.execute).to eq [group_label_2, group_label_3, project_label_1, group_label_1, project_label_2, project_label_4] + end end context 'filtering by group_id' do @@ -71,13 +79,19 @@ describe LabelsFinder do expect(finder.execute).to eq [group_label_2] end - it 'returns no labels if empty titles are supplied' do + it 'returns no labels if empty title is supplied' do finder = described_class.new(user, title: []) expect(finder.execute).to be_empty end - it 'returns no labels if empty names are supplied' do + it 'returns no labels if blank title is supplied' do + finder = described_class.new(user, title: '') + + expect(finder.execute).to be_empty + end + + it 'returns no labels if empty name is supplied' do finder = described_class.new(user, name: []) expect(finder.execute).to be_empty From e805253d4e2132827f45ce82c99b50c1e1314cfc Mon Sep 17 00:00:00 2001 From: Sonmez Kartal Date: Tue, 25 Oct 2016 09:41:29 +0300 Subject: [PATCH 165/176] Add docker-compose environment initialization command Signed-off-by: Sonmez Kartal --- doc/administration/integration/koding.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/administration/integration/koding.md b/doc/administration/integration/koding.md index a2c358af095..b95c425842c 100644 --- a/doc/administration/integration/koding.md +++ b/doc/administration/integration/koding.md @@ -61,6 +61,7 @@ executing commands in the following snippet. ```bash git clone https://github.com/koding/koding.git cd koding +docker-compose -f docker-compose-init.yml run init docker-compose up ``` From d0f0bad2d5b9860006273526e157c796eccd943c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 25 Oct 2016 08:43:52 +0200 Subject: [PATCH 166/176] Keep the new resque.yml aside and use it once we've checked out master MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- .gitlab-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 76117a48730..9f3cb61398d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -285,10 +285,13 @@ migration paths: - git checkout HEAD . - git fetch --tags - git checkout v8.5.9 + - mv config/resque.yml config/resque.yml.new - 'echo test: unix:/var/opt/gitlab/redis/redis.socket > config/resque.yml' - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" --retry=3 - rake db:drop db:create db:schema:load db:seed_fu - git checkout $CI_BUILD_REF + - rm config/resque.yml + - mv config/resque.yml.new config/resque.yml - rake db:migrate coverage: From 692eb3f84e912bd586f923bd87737bd15ae3b750 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 25 Oct 2016 10:50:29 +0200 Subject: [PATCH 167/176] Capitalize Git [ci skip] --- doc/user/project/new_ci_build_permissions_model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index 608475116a1..60b7bec2ba7 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -254,7 +254,7 @@ test: This will make GitLab CI initialize (fetch) and update (checkout) all your submodules recursively. -If git does not use the newly added relative URLs but still uses your old URLs, +If Git does not use the newly added relative URLs but still uses your old URLs, you might need to add `git submodule sync --recursive` to your `.gitlab-ci.yml`, prior to running `git submodule update --init --recursive`. This transfers the changes from your `.gitmodules` file into the `.git` folder, which is kept by From e0a79ab4e8ecf89db46b564a9ab7135c5a9081ec Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Fri, 7 Oct 2016 14:03:01 +0200 Subject: [PATCH 168/176] Increase debounce wait on issues search execution. --- CHANGELOG.md | 1 + app/assets/javascripts/issuable.js.es6 | 44 +++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 970f88b9fa7..f029c8a94fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Add RTL support to markdown renderer (Ebrahim Byagowi) - Add word-wrap to issue title on issue and milestone boards (ClemMakesApps) - Fix todos page mobile viewport layout (ClemMakesApps) + - Make issues search less finicky - Fix inconsistent highlighting of already selected activity nav-links (ClemMakesApps) - Remove redundant mixins (ClemMakesApps) - Added 'Download' button to the Snippets page (Justin DiPierro) diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6 index 57f7e4ef230..25f98ef8689 100644 --- a/app/assets/javascripts/issuable.js.es6 +++ b/app/assets/javascripts/issuable.js.es6 @@ -15,16 +15,47 @@ return Issuable.labelRow = _.template('<% _.each(labels, function(label){ %> <%- label.title %> <% }); %>'); }, initSearch: function() { - // `immediate` param set to false debounces on the `trailing` edge, lets user finish typing - const debouncedExecSearch = _.debounce(Issuable.executeSearch, 500, false); + const $searchInput = $('#issuable_search'); - $('#issuable_search').off('keyup').on('keyup', debouncedExecSearch); + Issuable.initSearchState($searchInput); + + // `immediate` param set to false debounces on the `trailing` edge, lets user finish typing + const debouncedExecSearch = _.debounce(Issuable.executeSearch, 1000, false); + + $searchInput.off('keyup').on('keyup', debouncedExecSearch); // ensures existing filters are preserved when manually submitted - $('#issue_search_form').on('submit', (e) => { + $('#issuable_search_form').on('submit', (e) => { e.preventDefault(); debouncedExecSearch(e); }); + + }, + initSearchState: function($searchInput) { + const currentSearchVal = $searchInput.val(); + + Issuable.searchState = { + elem: $searchInput, + current: currentSearchVal + }; + + Issuable.maybeFocusOnSearch(); + }, + accessSearchPristine: function(set) { + // store reference to previous value to prevent search on non-mutating keyup + const state = Issuable.searchState; + const currentSearchVal = state.elem.val(); + + if (set) { + state.current = currentSearchVal; + } else { + return state.current === currentSearchVal; + } + }, + maybeFocusOnSearch: function() { + if (Issuable.searchState.current !== '') { + Issuable.searchState.elem.focus(); + } }, executeSearch: function(e) { const $search = $('#issuable_search'); @@ -32,6 +63,11 @@ const $searchValue = $search.val(); const $filtersForm = $('.js-filter-form'); const $input = $(`input[name='${$searchName}']`, $filtersForm); + const isPristine = Issuable.accessSearchPristine(); + + if (isPristine) { + return; + } if (!$input.length) { $filtersForm.append(``); From 995d4df151621a79e1a45bfdf8917038511870ff Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Tue, 25 Oct 2016 10:49:18 +0200 Subject: [PATCH 169/176] Ensure cursor is applied to end of issues search input. --- app/assets/javascripts/issuable.js.es6 | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6 index 25f98ef8689..8c3c2d54c1a 100644 --- a/app/assets/javascripts/issuable.js.es6 +++ b/app/assets/javascripts/issuable.js.es6 @@ -53,8 +53,22 @@ } }, maybeFocusOnSearch: function() { - if (Issuable.searchState.current !== '') { - Issuable.searchState.elem.focus(); + const currentSearchVal = Issuable.searchState.current; + if (currentSearchVal !== '') { + const queryLength = currentSearchVal.length; + const $searchInput = Issuable.searchState.elem; + + /* The following ensures that the cursor is initially placed at + * the end of search input when focus is applied. It accounts + * for differences in browser implementations of `setSelectionRange` + * and cursor placement for elements in focus. + */ + $searchInput.focus(); + if ($searchInput.setSelectionRange) { + $searchInput.setSelectionRange(queryLength, queryLength); + } else { + $searchInput.val(currentSearchVal); + } } }, executeSearch: function(e) { From f1bb7ddfa76e73fc67922637bbfaf17d83df7ddc Mon Sep 17 00:00:00 2001 From: Bryce Johnson Date: Tue, 25 Oct 2016 12:46:28 +0200 Subject: [PATCH 170/176] Ensure search val is defined. --- app/assets/javascripts/issuable.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/issuable.js.es6 b/app/assets/javascripts/issuable.js.es6 index 8c3c2d54c1a..54056ac50c8 100644 --- a/app/assets/javascripts/issuable.js.es6 +++ b/app/assets/javascripts/issuable.js.es6 @@ -54,7 +54,7 @@ }, maybeFocusOnSearch: function() { const currentSearchVal = Issuable.searchState.current; - if (currentSearchVal !== '') { + if (currentSearchVal && currentSearchVal !== '') { const queryLength = currentSearchVal.length; const $searchInput = Issuable.searchState.elem; From a84c97186e6ff07db4ee6e202862a8ecb199f6cb Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 25 Oct 2016 12:39:06 +0200 Subject: [PATCH 171/176] Improve redis config tasks for migration paths job --- .gitlab-ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9f3cb61398d..bd102527502 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -279,19 +279,20 @@ bundler:audit: migration paths: stage: test <<: *use-db + variables: + SETUP_DB: "false" only: - master@gitlab-org/gitlab-ce script: - git checkout HEAD . - git fetch --tags - git checkout v8.5.9 - - mv config/resque.yml config/resque.yml.new - - 'echo test: unix:/var/opt/gitlab/redis/redis.socket > config/resque.yml' + - cp config/resque.yml.example config/resque.yml + - sed -i 's/localhost/redis/g' config/resque.yml - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" --retry=3 - rake db:drop db:create db:schema:load db:seed_fu - git checkout $CI_BUILD_REF - - rm config/resque.yml - - mv config/resque.yml.new config/resque.yml + - source scripts/prepare_build.sh - rake db:migrate coverage: From cabc131cfbee72e3a1eaae94619dcf1e3cc59d5a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 25 Oct 2016 12:57:56 +0100 Subject: [PATCH 172/176] Stop unauthized users dragging on issue boards Closes #23763 --- CHANGELOG.md | 3 ++- app/helpers/boards_helper.rb | 2 +- spec/features/boards/boards_spec.rb | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21f2bec867f..a8603170355 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,8 @@ Please view this file on the master branch, on stable branches it's out of date. - Fixed hidden pipeline graph on commit and MR page !6895 - Expire and build repository cache after project import - Fix 404 for group pages when GitLab setup uses relative url - - Simpler arguments passed to named_route on toggle_award_url helper method + - Simpler arguments passed to named_route on toggle_award_url helper method + - Fix unauthorized users dragging on issue boards - Better handle when no users were selected for adding to group or project. (Linus Thiel) - Only show register tab if signup enabled. diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index b7247ffa8b2..38c586ccd31 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -5,7 +5,7 @@ module BoardsHelper { endpoint: namespace_project_boards_path(@project.namespace, @project), board_id: board.id, - disabled: !can?(current_user, :admin_list, @project), + disabled: "#{!can?(current_user, :admin_list, @project)}", issue_link_base: namespace_project_issues_path(@project.namespace, @project) } end diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 0fb1608a0a3..c533ce1d87f 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -624,6 +624,10 @@ describe 'Issue Boards', feature: true, js: true do it 'does not show create new list' do expect(page).not_to have_selector('.js-new-board-list') end + + it 'does not allow dragging' do + expect(page).not_to have_selector('.user-can-drag') + end end context 'as guest user' do From ed47f4a30891529eac2fcc99a344038bf3793cc4 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 25 Oct 2016 01:37:16 -0700 Subject: [PATCH 173/176] Reduce overhead of LabelFinder by avoiding #presence call Some users experienced 502 timeouts when viewing group labels. Labels#open_issues_count and Label#open_merge_requests_count were taking a long time to load because they were loading every ActiveRecord of the user-accessible projects into memory. This change modifies the system so that #presence and hence to_a isn't called. Closes #23684 --- CHANGELOG.md | 1 + app/finders/labels_finder.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86878e5af6c..8ae39105ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix reply-by-email not working due to queue name mismatch - Fixed hidden pipeline graph on commit and MR page !6895 - Expire and build repository cache after project import + - Reduce overhead of LabelFinder by avoiding #presence call !7094 - Fix 404 for group pages when GitLab setup uses relative url - Simpler arguments passed to named_route on toggle_award_url helper method - Better handle when no users were selected for adding to group or project. (Linus Thiel) diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 6ace14a4bb5..44c659b609a 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -48,7 +48,7 @@ class LabelsFinder < UnionFinder end def projects_ids - params[:project_ids].presence + params[:project_ids] end def title From 3b4af59a5fed342bf7a4718dba6189cdf1d0c017 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 25 Oct 2016 16:01:24 +0200 Subject: [PATCH 174/176] Don't schedule ProjectCacheWorker unless needed This changes ProjectCacheWorker.perform_async so it only schedules a job when no lease for the given project is present. This ensures we don't end up scheduling hundreds of jobs when they won't be executed anyway. --- CHANGELOG.md | 1 + app/workers/project_cache_worker.rb | 16 +++++++-- lib/gitlab/exclusive_lease.rb | 9 ++++- spec/lib/gitlab/exclusive_lease_spec.rb | 43 +++++++++++++++-------- spec/spec_helper.rb | 6 ++++ spec/workers/project_cache_worker_spec.rb | 20 +++++++++++ 6 files changed, 77 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60f932e1f76..027f4e89dd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Please view this file on the master branch, on stable branches it's out of date. - Fix unauthorized users dragging on issue boards - Better handle when no users were selected for adding to group or project. (Linus Thiel) - Only show register tab if signup enabled. + - Only schedule ProjectCacheWorker jobs when needed ## 8.13.0 (2016-10-22) - Removes extra line for empty issue description. (!7045) diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb index 71b274e0c99..4dfa745fb50 100644 --- a/app/workers/project_cache_worker.rb +++ b/app/workers/project_cache_worker.rb @@ -9,6 +9,18 @@ class ProjectCacheWorker LEASE_TIMEOUT = 15.minutes.to_i + def self.lease_for(project_id) + Gitlab::ExclusiveLease. + new("project_cache_worker:#{project_id}", timeout: LEASE_TIMEOUT) + end + + # Overwrite Sidekiq's implementation so we only schedule when actually needed. + def self.perform_async(project_id) + # If a lease for this project is still being held there's no point in + # scheduling a new job. + super unless lease_for(project_id).exists? + end + def perform(project_id) if try_obtain_lease_for(project_id) Rails.logger. @@ -37,8 +49,6 @@ class ProjectCacheWorker end def try_obtain_lease_for(project_id) - Gitlab::ExclusiveLease. - new("project_cache_worker:#{project_id}", timeout: LEASE_TIMEOUT). - try_obtain + self.class.lease_for(project_id).try_obtain end end diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb index ffe49364379..7e8f35e9298 100644 --- a/lib/gitlab/exclusive_lease.rb +++ b/lib/gitlab/exclusive_lease.rb @@ -27,7 +27,7 @@ module Gitlab # on begin/ensure blocks to cancel a lease, because the 'ensure' does # not always run. Think of 'kill -9' from the Unicorn master for # instance. - # + # # If you find that leases are getting in your way, ask yourself: would # it be enough to lower the lease timeout? Another thing that might be # appropriate is to only use a lease for bulk/automated operations, and @@ -48,6 +48,13 @@ module Gitlab end end + # Returns true if the key for this lease is set. + def exists? + Gitlab::Redis.with do |redis| + redis.exists(redis_key) + end + end + # No #cancel method. See comments above! private diff --git a/spec/lib/gitlab/exclusive_lease_spec.rb b/spec/lib/gitlab/exclusive_lease_spec.rb index fbdb7ea34ac..6b3bd08b978 100644 --- a/spec/lib/gitlab/exclusive_lease_spec.rb +++ b/spec/lib/gitlab/exclusive_lease_spec.rb @@ -1,21 +1,36 @@ require 'spec_helper' -describe Gitlab::ExclusiveLease do - it 'cannot obtain twice before the lease has expired' do - lease = Gitlab::ExclusiveLease.new(unique_key, timeout: 3600) - expect(lease.try_obtain).to eq(true) - expect(lease.try_obtain).to eq(false) +describe Gitlab::ExclusiveLease, type: :redis do + let(:unique_key) { SecureRandom.hex(10) } + + describe '#try_obtain' do + it 'cannot obtain twice before the lease has expired' do + lease = Gitlab::ExclusiveLease.new(unique_key, timeout: 3600) + expect(lease.try_obtain).to eq(true) + expect(lease.try_obtain).to eq(false) + end + + it 'can obtain after the lease has expired' do + timeout = 1 + lease = Gitlab::ExclusiveLease.new(unique_key, timeout: timeout) + lease.try_obtain # start the lease + sleep(2 * timeout) # lease should have expired now + expect(lease.try_obtain).to eq(true) + end end - it 'can obtain after the lease has expired' do - timeout = 1 - lease = Gitlab::ExclusiveLease.new(unique_key, timeout: timeout) - lease.try_obtain # start the lease - sleep(2 * timeout) # lease should have expired now - expect(lease.try_obtain).to eq(true) - end + describe '#exists?' do + it 'returns true for an existing lease' do + lease = Gitlab::ExclusiveLease.new(unique_key, timeout: 3600) + lease.try_obtain - def unique_key - SecureRandom.hex(10) + expect(lease.exists?).to eq(true) + end + + it 'returns false for a lease that does not exist' do + lease = Gitlab::ExclusiveLease.new(unique_key, timeout: 3600) + + expect(lease.exists?).to eq(false) + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b19f5824236..06d52f0f735 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -50,6 +50,12 @@ RSpec.configure do |config| example.run Rails.cache = caching_store end + + config.around(:each, :redis) do |example| + Gitlab::Redis.with(&:flushall) + example.run + Gitlab::Redis.with(&:flushall) + end end FactoryGirl::SyntaxRunner.class_eval do diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb index f5b60b90d11..bfa8c0ff2c6 100644 --- a/spec/workers/project_cache_worker_spec.rb +++ b/spec/workers/project_cache_worker_spec.rb @@ -5,6 +5,26 @@ describe ProjectCacheWorker do subject { described_class.new } + describe '.perform_async' do + it 'schedules the job when no lease exists' do + allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:exists?). + and_return(false) + + expect_any_instance_of(described_class).to receive(:perform) + + described_class.perform_async(project.id) + end + + it 'does not schedule the job when a lease exists' do + allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:exists?). + and_return(true) + + expect_any_instance_of(described_class).not_to receive(:perform) + + described_class.perform_async(project.id) + end + end + describe '#perform' do context 'when an exclusive lease can be obtained' do before do From 723e5d65d6985d355a689c1a963d4d1242564c2f Mon Sep 17 00:00:00 2001 From: Brad Jones Date: Thu, 11 Aug 2016 20:38:43 +0000 Subject: [PATCH 175/176] Specify which Fog storage drivers are imported by default in backup_restore.md --- doc/raketasks/backup_restore.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index fc0cd1b8af2..0ad84705cfd 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -85,8 +85,11 @@ Deleting old backups... [SKIPPING] Starting with GitLab 7.4 you can let the backup script upload the '.tar' file it creates. It uses the [Fog library](http://fog.io/) to perform the upload. -In the example below we use Amazon S3 for storage. -Fog also supports [other storage providers](http://fog.io/storage/). +In the example below we use Amazon S3 for storage, but Fog also lets you use +[other storage providers](http://fog.io/storage/). GitLab +[imports cloud drivers](https://gitlab.com/gitlab-org/gitlab-ce/blob/30f5b9a5b711b46f1065baf755e413ceced5646b/Gemfile#L88) +for AWS, Azure, Google, OpenStack Swift and Rackspace as well. A local driver is +[also available](#uploading-to-locally-mounted-shares). For omnibus packages: From 3fe5004a37602177363945fa1a7805b802ebe39d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 25 Oct 2016 17:04:58 -0700 Subject: [PATCH 176/176] Remove use of wait_for_ajax since jQuery was removed Fixes #23812 --- spec/features/merge_requests/created_from_fork_spec.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb index cfc1244429f..142649297cc 100644 --- a/spec/features/merge_requests/created_from_fork_spec.rb +++ b/spec/features/merge_requests/created_from_fork_spec.rb @@ -40,8 +40,6 @@ feature 'Merge request created from fork' do end context 'pipeline present in source project' do - include WaitForAjax - given(:pipeline) do create(:ci_pipeline, project: fork_project, @@ -57,7 +55,6 @@ feature 'Merge request created from fork' do scenario 'user visits a pipelines page', js: true do visit_merge_request(merge_request) page.within('.merge-request-tabs') { click_link 'Builds' } - wait_for_ajax page.within('table.ci-table') do expect(page).to have_content 'rspec'