From 7ea3bbd4e0b01b4d25a984f5e6c0471053d8874a Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Sun, 14 Feb 2016 10:31:28 +0100 Subject: [PATCH 001/112] add bundle clean to upgrade docs --- doc/update/8.4-to-8.5.md | 3 +++ doc/update/patch_versions.md | 1 + 2 files changed, 4 insertions(+) diff --git a/doc/update/8.4-to-8.5.md b/doc/update/8.4-to-8.5.md index 42b26439848..c8083a45470 100644 --- a/doc/update/8.4-to-8.5.md +++ b/doc/update/8.4-to-8.5.md @@ -64,6 +64,9 @@ sudo -u git -H bundle install --without postgres development test --deployment # PostgreSQL installations (note: the line below states '--without mysql') sudo -u git -H bundle install --without mysql development test --deployment +# Clean up old gems +sudo -u git -H bundle clean + # Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index a10e62877ba..ce7ae0d285c 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -62,6 +62,7 @@ sudo -u git -H bundle install --without development test mysql --deployment # MySQL sudo -u git -H bundle install --without development test postgres --deployment +sudo -u git -H bundle clean sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` From d24d806ea9743e05072a3260e7a4c1e8edd5e2dd Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 29 Feb 2016 10:20:45 +0100 Subject: [PATCH 002/112] Deprecated GitLab CI API clean up - README --- doc/ci/api/README.md | 87 ++++---------------------------------------- 1 file changed, 8 insertions(+), 79 deletions(-) diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md index cf9710ede57..cb860bc43f0 100644 --- a/doc/ci/api/README.md +++ b/doc/ci/api/README.md @@ -1,86 +1,15 @@ # GitLab CI API +## Purpose + +Main purpose of GitLab CI API is to provide necessary data and context for +GitLab CI Runners. + +For consumer API take a look at this [documentation](../../api/README.md) where +you will find all relevant information. + ## Resources -- [Projects](projects.md) - [Runners](runners.md) - [Commits](commits.md) - [Builds](builds.md) - - -## Authentication - -GitLab CI API uses different types of authentication depends on what API you use. -Each API document has section with information about authentication you need to use. - -GitLab CI API has 4 authentication methods: - -* GitLab user token & GitLab url -* GitLab CI project token -* GitLab CI runners registration token -* GitLab CI runner token - - -### Authentication #1: GitLab user token & GitLab url - -Authentication is done by -sending the `private-token` of a valid user and the `url` of an -authorized GitLab instance via a query string along with the API -request: - - GET http://gitlab.example.com/ci/api/v1/projects?private_token=QVy1PB7sTxfy4pqfZM1U&url=http://demo.gitlab.com/ - -If preferred, you may instead send the `private-token` as a header in -your request: - - curl --header "PRIVATE-TOKEN: QVy1PB7sTxfy4pqfZM1U" "http://gitlab.example.com/ci/api/v1/projects?url=http://demo.gitlab.com/" - - -### Authentication #2: GitLab CI project token - -Each project in GitLab CI has it own token. -It can be used to get project commits and builds information. -You can use project token only for certain project. - -### Authentication #3: GitLab CI runners registration token - -This token is not persisted and is generated on each application start. -It can be used only for registering new runners in system. You can find it on -GitLab CI Runners web page https://gitlab-ci.example.com/admin/runners - -### Authentication #4: GitLab CI runner token - -Every GitLab CI runner has it own token that allow it to receive and update -GitLab CI builds. This token exists of internal purposes and should be used only -by runners - -## JSON - -All API requests are serialized using JSON. You don't need to specify -`.json` at the end of API URL. - -## Status codes - -The API is designed to return different status codes according to context and action. In this way if a request results in an error the caller is able to get insight into what went wrong, e.g. status code `400 Bad Request` is returned if a required attribute is missing from the request. The following list gives an overview of how the API functions generally behave. - -API request types: - -- `GET` requests access one or more resources and return the result as JSON -- `POST` requests return `201 Created` if the resource is successfully created and return the newly created resource as JSON -- `GET`, `PUT` and `DELETE` return `200 OK` if the resource is accessed, modified or deleted successfully, the (modified) result is returned as JSON -- `DELETE` requests are designed to be idempotent, meaning a request a resource still returns `200 OK` even it was deleted before or is not available. The reasoning behind it is the user is not really interested if the resource existed before or not. - -The following list shows the possible return codes for API requests. - -Return values: - -- `200 OK` - The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON -- `201 Created` - The `POST` request was successful and the resource is returned as JSON -- `400 Bad Request` - A required attribute of the API request is missing, e.g. the title of an issue is not given -- `401 Unauthorized` - The user is not authenticated, a valid user token is necessary, see above -- `403 Forbidden` - The request is not allowed, e.g. the user is not allowed to delete a project -- `404 Not Found` - A resource could not be accessed, e.g. an ID for a resource could not be found -- `405 Method Not Allowed` - The request is not supported -- `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists -- `422 Unprocessable` - The entity could not be processed -- `500 Server Error` - While handling the request something went wrong on the server side From 616281f6a9a5056c368f62c35fe91f7b19d37210 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 29 Feb 2016 12:00:42 +0100 Subject: [PATCH 003/112] Update CI API docs for builds endpoint for runners [ci skip] --- doc/ci/api/README.md | 2 +- doc/ci/api/builds.md | 104 +++++++++++++++++-------------------------- 2 files changed, 42 insertions(+), 64 deletions(-) diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md index cb860bc43f0..d573bea0f80 100644 --- a/doc/ci/api/README.md +++ b/doc/ci/api/README.md @@ -10,6 +10,6 @@ you will find all relevant information. ## Resources +- [Builds](builds.md) - [Runners](runners.md) - [Commits](commits.md) -- [Builds](builds.md) diff --git a/doc/ci/api/builds.md b/doc/ci/api/builds.md index 018ca22dbbd..b82f2d19b19 100644 --- a/doc/ci/api/builds.md +++ b/doc/ci/api/builds.md @@ -1,85 +1,63 @@ # Builds API -This API used by runners to receive and update builds. +API used by runners to receive and update builds. -__Authentication is done by runner token__ +_**Note:** This API is intended to be used only by Runners as their own +communication channel. For the consumer API see the +[Builds API](../../api/builds.md)._ + +## Authentication + +Unique runner token is required to authenticate. You can provide build token +using a `token` parameter, or by sending `BUILD-TOKEN` header that contains it. + +`token` parameter and `BUILD-TOKEN` header can be interchangeable. ## Builds ### Runs oldest pending build by runner - POST /ci/builds/register + POST /builds/register Parameters: * `token` (required) - The unique token of runner -Returns: - -```json -{ - "id": 48584, - "ref": "0.1.1", - "tag": true, - "sha": "d63117656af6ff57d99e50cc270f854691f335ad", - "status": "success", - "name": "pages", - "token": "9dd60b4f1a439d1765357446c1084c", - "stage": "test", - "project_id": 479, - "project_name": "test", - "commands": "echo commands", - "repo_url": "http://gitlab-ci-token:token@gitlab.example/group/test.git", - "before_sha": "0000000000000000000000000000000000000000", - "allow_git_fetch": false, - "options": { - "image": "docker:image", - "artifacts": { - "paths": [ - "public" - ] - }, - "cache": { - "paths": [ - "vendor" - ] - } - }, - "timeout": 3600, - "variables": [ - { - "key": "CI_BUILD_TAG", - "value": "0.1.1", - "public": true - } - ], - "depends_on_builds": [ - { - "id": 48584, - "ref": "0.1.1", - "tag": true, - "sha": "d63117656af6ff57d99e50cc270f854691f335ad", - "status": "success", - "name": "build", - "token": "9dd60b4f1a439d1765357446c1084c", - "stage": "build", - "project_id": 479, - "project_name": "test", - "artifacts_file": { - "filename": "artifacts.zip", - "size": 0 - } - } - ] -} -``` ### Update details of an existing build - PUT /ci/builds/:id + PUT /builds/:id Parameters: * `id` (required) - The ID of a project * `state` (optional) - The state of a build * `trace` (optional) - The trace of a build + +### Upload artifacts to build + + POST /builds/:id/artifacts + +Parameters: + + * `id` (required) - The ID of a build + * `token` (required) - The build authorization token + * `file` (required) - Artifacts file + +### Download the artifacts file from build + + GET /builds/:id/artifacts + +Parameters: + + * `id` (required) - The ID of a build + * `token` (required) - The build authorization token + +### Remove the artifacts file from build + + DELETE /builds/:id/artifacts + +Parameters: + + * ` id` (required) - The ID of a build + * `token` (required) - The build authorization token From cf73dc34f7ea6908e2d5d65cfc372ecebfbe09f6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 29 Feb 2016 12:12:51 +0100 Subject: [PATCH 004/112] Add CI API prefix to documentation [ci skip] --- doc/ci/api/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md index d573bea0f80..94ef4d2c38d 100644 --- a/doc/ci/api/README.md +++ b/doc/ci/api/README.md @@ -8,6 +8,14 @@ GitLab CI Runners. For consumer API take a look at this [documentation](../../api/README.md) where you will find all relevant information. +## API Prefix + +Current CI API prefix is `/ci/api/v1`. + +You need to prepend this prefix to all examples in this documentation, like: + + GET /ci/api/v1/builds/:id/artifacts + ## Resources - [Builds](builds.md) From 5b3b4ff65e7cc809bdf026f21a9a56649e073e18 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 29 Feb 2016 12:14:46 +0100 Subject: [PATCH 005/112] Update CI API documentation for runners [ci skip] --- doc/ci/api/runners.md | 64 ++++++++----------------------------------- 1 file changed, 11 insertions(+), 53 deletions(-) diff --git a/doc/ci/api/runners.md b/doc/ci/api/runners.md index e9033aeacd5..c4716982357 100644 --- a/doc/ci/api/runners.md +++ b/doc/ci/api/runners.md @@ -1,81 +1,39 @@ # Runners API +API used by runners to register and delete itselves. + _**Note:** This API is intended to be used only by Runners as their own communication channel. For the consumer API see the [new Runners API](../../api/runners.md)._ ## Runners -### Retrieve all runners - -__Authentication is done by GitLab user token & GitLab url__ - -Used to get information about all runners registered on the GitLab CI -instance. - - GET /ci/runners - -Returns: - -```json -[ - { - "id" : 85, - "token" : "12b68e90394084703135" - }, - { - "id" : 86, - "token" : "76bf894e969364709864" - }, -] -``` - ### Register a new runner - -__Authentication is done with a Shared runner registration token or a project Specific runner registration token__ +__Authentication is done with a shared runner registration token or a project +specific runner registration token.__ Used to make GitLab CI aware of available runners. - POST /ci/runners/register + POST /runners/register Parameters: - * `token` (required) - The registration token. It is 2 types of token you can pass here. + * `token` (required) - The registration token. + +It is 2 types of token you can pass here. 1. Shared runner registration token 2. Project specific registration token -Returns: - -```json -{ - "id" : 85, - "token" : "12b68e90394084703135" -} -``` - ### Delete a runner +__Authentication is done by using runner token.__ -__Authentication is done by runner token__ +Used to remove runner. -Used to removing runners. - - DELETE /ci/runners/delete + DELETE /runners/delete Parameters: * `token` (required) - The runner token. - -Returns: - -```json -{ - "id" : 1, - "token" : "d14963981a428f70121777e50643d1", - "created_at" : "2015-02-26T11:39:39.232Z", - "updated_at" : "2015-02-26T11:39:39.232Z", - "description" : "awesome runner" -} -``` From ec7c6a084c5c366f67932bc21e45ef7fff4abac9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 29 Feb 2016 12:16:32 +0100 Subject: [PATCH 006/112] Remove deprecated CI API docs for commits and projects [ci skip] --- doc/api/commits.md | 3 +- doc/ci/api/README.md | 1 - doc/ci/api/commits.md | 108 ----------------------------- doc/ci/api/projects.md | 149 ----------------------------------------- 4 files changed, 1 insertion(+), 260 deletions(-) delete mode 100644 doc/ci/api/commits.md delete mode 100644 doc/ci/api/projects.md diff --git a/doc/api/commits.md b/doc/api/commits.md index e4d436b8e52..6341440c58b 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -213,8 +213,7 @@ Example response: ## Commit status -Since GitLab 8.1, this is the new commit status API. The documentation in -[ci/api/commits](../ci/api/commits.md) is deprecated. +Since GitLab 8.1, this is the new commit status API. ### Get the status of a commit diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md index 94ef4d2c38d..aea808007fc 100644 --- a/doc/ci/api/README.md +++ b/doc/ci/api/README.md @@ -20,4 +20,3 @@ You need to prepend this prefix to all examples in this documentation, like: - [Builds](builds.md) - [Runners](runners.md) -- [Commits](commits.md) diff --git a/doc/ci/api/commits.md b/doc/ci/api/commits.md deleted file mode 100644 index 871de7abcce..00000000000 --- a/doc/ci/api/commits.md +++ /dev/null @@ -1,108 +0,0 @@ -# Commits API - -**DEPRECATED** - -Since GitLab 8.1, there is a new commit status API. Please see the [revised -documentation](../../api/commits.md#commit-status). - ---- - -__Authentication is done by GitLab CI project token__ - -## Commits - -### Retrieve all commits per project - -Get list of commits per project - - GET /ci/commits - -Parameters: - - * `project_id` (required) - The ID of a project - * `project_token` (requires) - Project token - * `page` (optional) - * `per_page` (optional) - items per request (default is 20) - -Returns: - -```json -[{ - "id": 3, - "ref": "master", - "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf", - "project_id": 2, - "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898", - "created_at": "2014-11-05T09:46:35.247Z", - "status": "success", - "finished_at": "2014-11-05T09:46:44.254Z", - "duration": 5.062692165374756, - "git_commit_message": "wow\n", - "git_author_name": "Administrator", - "git_author_email": "admin@example.com", - "builds": [{ - "id": 7, - "project_id": 2, - "ref": "master", - "status": "success", - "finished_at": "2014-11-05T09:46:44.254Z", - "created_at": "2014-11-05T09:46:35.259Z", - "updated_at": "2014-11-05T09:46:44.255Z", - "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf", - "started_at": "2014-11-05T09:46:39.192Z", - "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898", - "runner_id": 1, - "coverage": null, - "commit_id": 3 - }] -}] -``` - -### Create commit - -Inform GitLab CI about new commit you want it to build. - -__If commit already exists in GitLab CI it will not be created__ - - - POST /ci/commits - -Parameters: - - * `project_id` (required) - The ID of a project - * `project_token` (requires) - Project token - * `data` (required) - Push data. For example see comment in `lib/api/commits.rb` - -Returns: - -```json -{ - "id": 3, - "ref": "master", - "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf", - "project_id": 2, - "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898", - "created_at": "2014-11-05T09:46:35.247Z", - "status": "success", - "finished_at": "2014-11-05T09:46:44.254Z", - "duration": 5.062692165374756, - "git_commit_message": "wow\n", - "git_author_name": "Administrator", - "git_author_email": "admin@example.com", - "builds": [{ - "id": 7, - "project_id": 2, - "ref": "master", - "status": "success", - "finished_at": "2014-11-05T09:46:44.254Z", - "created_at": "2014-11-05T09:46:35.259Z", - "updated_at": "2014-11-05T09:46:44.255Z", - "sha": "65617dfc36761baa1f46a7006f2a88916f7f56cf", - "started_at": "2014-11-05T09:46:39.192Z", - "before_sha": "96906f2bceb04c7323f8514aa5ad8cb1313e2898", - "runner_id": 1, - "coverage": null, - "commit_id": 3 - }] -} -``` diff --git a/doc/ci/api/projects.md b/doc/ci/api/projects.md deleted file mode 100644 index fe6b1c01352..00000000000 --- a/doc/ci/api/projects.md +++ /dev/null @@ -1,149 +0,0 @@ -# Projects API - -This API is intended to aid in the setup and configuration of -projects on GitLab CI. - -__Authentication is done by GitLab user token & GitLab url__ - -## Projects - -### List Authorized Projects - -Lists all projects that the authenticated user has access to. - -``` -GET /ci/projects -``` - -Returns: - -```json -[ - { - "id" : 271, - "name" : "gitlabhq", - "timeout" : 1800, - "token" : "iPWx6WM4lhHNedGfBpPJNP", - "default_ref" : "master", - "gitlab_url" : "http://demo.gitlabhq.com/gitlab/gitlab-shell", - "path" : "gitlab/gitlab-shell", - "always_build" : false, - "polling_interval" : null, - "public" : false, - "ssh_url_to_repo" : "git@demo.gitlab.com:gitlab/gitlab-shell.git", - "gitlab_id" : 3 - }, - { - "id" : 272, - "name" : "gitlab-ci", - "timeout" : 1800, - "token" : "iPWx6WM4lhHNedGfBpPJNP", - "default_ref" : "master", - "gitlab_url" : "http://demo.gitlabhq.com/gitlab/gitlab-shell", - "path" : "gitlab/gitlab-shell", - "always_build" : false, - "polling_interval" : null, - "public" : false, - "ssh_url_to_repo" : "git@demo.gitlab.com:gitlab/gitlab-shell.git", - "gitlab_id" : 4 - } -] -``` - -### List Owned Projects - -Lists all projects that the authenticated user owns. - -``` -GET /ci/projects/owned -``` - -Returns: - -```json -[ - { - "id" : 272, - "name" : "gitlab-ci", - "timeout" : 1800, - "token" : "iPWx6WM4lhHNedGfBpPJNP", - "default_ref" : "master", - "gitlab_url" : "http://demo.gitlabhq.com/gitlab/gitlab-shell", - "path" : "gitlab/gitlab-shell", - "always_build" : false, - "polling_interval" : null, - "public" : false, - "ssh_url_to_repo" : "git@demo.gitlab.com:gitlab/gitlab-shell.git", - "gitlab_id" : 4 - } -] -``` - -### Single Project - -Returns information about a single project for which the user is -authorized. - - GET /ci/projects/:id - -Parameters: - - * `id` (required) - The ID of the GitLab CI project - -### Create Project - -Creates a GitLab CI project using GitLab project details. - - POST /ci/projects - -Parameters: - - * `name` (required) - The name of the project - * `gitlab_id` (required) - The ID of the project on the GitLab instance - * `default_ref` (optional) - The branch to run on (default to `master`) - -### Update Project - -Updates a GitLab CI project using GitLab project details that the -authenticated user has access to. - - PUT /ci/projects/:id - -Parameters: - - * `name` - The name of the project - * `default_ref` - The branch to run on (default to `master`) - -### Remove Project - -Removes a GitLab CI project that the authenticated user has access to. - - DELETE /ci/projects/:id - -Parameters: - - * `id` (required) - The ID of the GitLab CI project - -### Link Project to Runner - -Links a runner to a project so that it can make builds (only via -authorized user). - - POST /ci/projects/:id/runners/:runner_id - -Parameters: - - * `id` (required) - The ID of the GitLab CI project - * `runner_id` (required) - The ID of the GitLab CI runner - -### Remove Project from Runner - -Removes a runner from a project so that it can not make builds (only -via authorized user). - - DELETE /ci/projects/:id/runners/:runner_id - -Parameters: - - * `id` (required) - The ID of the GitLab CI project - * `runner_id` (required) - The ID of the GitLab CI runner \ No newline at end of file From 74b963268c781ab714983a5c88e40f737c24c84b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 1 Mar 2016 10:57:12 +0100 Subject: [PATCH 007/112] Update format of documentation for CI API --- doc/ci/api/builds.md | 28 +++++++++++++++++++--------- doc/ci/api/runners.md | 35 +++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/doc/ci/api/builds.md b/doc/ci/api/builds.md index b82f2d19b19..d100e261178 100644 --- a/doc/ci/api/builds.md +++ b/doc/ci/api/builds.md @@ -8,35 +8,45 @@ communication channel. For the consumer API see the ## Authentication -Unique runner token is required to authenticate. You can provide build token -using a `token` parameter, or by sending `BUILD-TOKEN` header that contains it. +This API uses two types of authentication: -`token` parameter and `BUILD-TOKEN` header can be interchangeable. +1. Unique runner's token + + Token assigned to runner after it has been registered. + +2. Using build authorization token + + This is project's CI token that can be found in Continuous Integration + project settings. + + Build authorization token can be passed as a parameter or a value of + `BUILD-TOKEN` header. This method are interchangeable. ## Builds ### Runs oldest pending build by runner - POST /builds/register + POST /ci/api/v1/builds/register Parameters: - * `token` (required) - The unique token of runner + * `token` (required) - Unique runner token ### Update details of an existing build - PUT /builds/:id + PUT /ci/api/v1/builds/:id Parameters: * `id` (required) - The ID of a project + * `token` (required) - Unique runner token * `state` (optional) - The state of a build * `trace` (optional) - The trace of a build ### Upload artifacts to build - POST /builds/:id/artifacts + POST /ci/api/v1/builds/:id/artifacts Parameters: @@ -46,7 +56,7 @@ Parameters: ### Download the artifacts file from build - GET /builds/:id/artifacts + GET /ci/api/v1/builds/:id/artifacts Parameters: @@ -55,7 +65,7 @@ Parameters: ### Remove the artifacts file from build - DELETE /builds/:id/artifacts + DELETE /ci/api/v1/builds/:id/artifacts Parameters: diff --git a/doc/ci/api/runners.md b/doc/ci/api/runners.md index c4716982357..2f01da4bd76 100644 --- a/doc/ci/api/runners.md +++ b/doc/ci/api/runners.md @@ -1,39 +1,46 @@ # Runners API -API used by runners to register and delete itselves. +API used by runners to register and delete themselves. _**Note:** This API is intended to be used only by Runners as their own communication channel. For the consumer API see the [new Runners API](../../api/runners.md)._ +## Authentication + +This API uses two types of authentication: + +1. Unique runner's token + + Token assigned to runner after it has been registered. + +2. Using runners' registration token + + This is a token that can be found in project's settings. + It can be also found in Admin area » Runners settings. + + There are two types of tokens you can pass - shared runner registration + token or project specific registration token. + ## Runners ### Register a new runner -__Authentication is done with a shared runner registration token or a project -specific runner registration token.__ - Used to make GitLab CI aware of available runners. - POST /runners/register + POST /ci/api/v1/runners/register Parameters: - * `token` (required) - The registration token. + * `token` (required) - Registration token -It is 2 types of token you can pass here. - -1. Shared runner registration token -2. Project specific registration token ### Delete a runner -__Authentication is done by using runner token.__ - Used to remove runner. - DELETE /runners/delete + DELETE /ci/api/v1/runners/delete Parameters: - * `token` (required) - The runner token. + * `token` (required) - Unique runner token From 4ed40db9cefc35d06a13adeed55a76021cd8522c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Tue, 1 Mar 2016 19:17:22 +0000 Subject: [PATCH 008/112] update slack instructions --- doc/integration/slack.md | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/doc/integration/slack.md b/doc/integration/slack.md index ecbe0d3e887..f6ba80f46d5 100644 --- a/doc/integration/slack.md +++ b/doc/integration/slack.md @@ -2,19 +2,11 @@ ## On Slack -To enable Slack integration you must create an Incoming WebHooks integration on Slack; +To enable Slack integration you must create an Incoming WebHooks integration on Slack: 1. [Sign in to Slack](https://slack.com/signin) -1. Select **Apps & Custom Integrations** from the dropdown next to your team name. - -1. Click the **Configure** link (right-upper corner). - -1. Select the **Custom integrations** tab. - -1. Click the **Incoming WebHooks** row. - -1. Click the **Add configuration** button. +1. Visit [Incoming WebHooks](https://my.slack.com/services/new/incoming-webhook/) 1. Choose the channel name you want to send notifications to. From 9c593a51c377f474f1aac1ed16b1dc8eafc6f6d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 2 Mar 2016 10:33:38 +0000 Subject: [PATCH 009/112] Remove duplicate 'cache_store' configuration in test environment --- config/environments/test.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/environments/test.rb b/config/environments/test.rb index d6842affa6c..f96ac6f9753 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -7,8 +7,6 @@ Rails.application.configure do # and recreated between test runs. Don't rely on the data there! config.cache_classes = false - config.cache_store = :null_store - # Configure static asset server for tests with Cache-Control for performance config.serve_static_files = true config.static_cache_control = "public, max-age=3600" From 65ba4da925361e69f59fc87eaf908e933079f2c3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 25 Feb 2016 08:37:49 +0100 Subject: [PATCH 010/112] Add support for keyword arguments in label reference method --- app/models/label.rb | 9 +++------ app/services/system_note_service.rb | 2 +- spec/fixtures/markdown.md.erb | 2 +- spec/lib/banzai/filter/label_reference_filter_spec.rb | 2 +- spec/models/label_spec.rb | 4 ++-- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/app/models/label.rb b/app/models/label.rb index c34f4e4ba60..02683a08dd6 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -66,16 +66,13 @@ class Label < ActiveRecord::Base # # format - Symbol format to use (default: :id, optional: :name) # - # Note that its argument differs from other objects implementing Referable. If - # a non-Symbol argument is given (such as a Project), it will default to :id. - # # Examples: # - # Label.first.to_reference # => "~1" - # Label.first.to_reference(:name) # => "~\"bug\"" + # Label.first.to_reference # => "~1" + # Label.first.to_reference(format: :name) # => "~\"bug\"" # # Returns a String - def to_reference(format = :id) + def to_reference(_from_project = nil, format: :id) if format == :name && !name.include?('"') %(#{self.class.reference_prefix}"#{name}") else diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index edced010811..58a861ee08e 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -66,7 +66,7 @@ class SystemNoteService def self.change_label(noteable, project, author, added_labels, removed_labels) labels_count = added_labels.count + removed_labels.count - references = ->(label) { label.to_reference(:id) } + references = ->(label) { label.to_reference(format: :id) } added_labels = added_labels.map(&references).join(' ') removed_labels = removed_labels.map(&references).join(' ') diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index fe6d42acee2..1772cc3f6a4 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -209,7 +209,7 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Label by ID: <%= simple_label.to_reference %> - Label by name: <%= Label.reference_prefix %><%= simple_label.name %> -- Label by name in quotes: <%= label.to_reference(:name) %> +- Label by name in quotes: <%= label.to_reference(format: :name) %> - Ignored in code: `<%= simple_label.to_reference %>` - Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link) - Link to label by reference: [Label](<%= label.to_reference %>) diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index b46ccc47605..9d9e5cbac3d 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -111,7 +111,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do context 'String-based multi-word references in quotes' do let(:label) { create(:label, name: 'gfm references', project: project) } - let(:reference) { label.to_reference(:name) } + let(:reference) { label.to_reference(format: :name) } it 'links to a valid reference' do doc = reference_filter("See #{reference}") diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 696fbf7e0aa..fd133e1ca1b 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -65,12 +65,12 @@ describe Label, models: true do context 'using name' do it 'returns a String reference to the object' do - expect(label.to_reference(:name)).to eq %(~"#{label.name}") + expect(label.to_reference(format: :name)).to eq %(~"#{label.name}") end it 'uses id when name contains double quote' do label = create(:label, name: %q{"irony"}) - expect(label.to_reference(:name)).to eq "~#{label.id}" + expect(label.to_reference(format: :name)).to eq "~#{label.id}" end end end From acd877c73e302438acabeaccfcd0fc5dfcd7a894 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 25 Feb 2016 10:14:12 +0100 Subject: [PATCH 011/112] Add cross project references support for label model --- app/models/label.rb | 24 ++++++++++++++++++++---- spec/models/label_spec.rb | 26 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/app/models/label.rb b/app/models/label.rb index 02683a08dd6..445f22ee1e0 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -70,16 +70,20 @@ class Label < ActiveRecord::Base # # Label.first.to_reference # => "~1" # Label.first.to_reference(format: :name) # => "~\"bug\"" + # Label.first.to_reference(project) # => "gitlab-org/gitlab-ce~1" # # Returns a String - def to_reference(_from_project = nil, format: :id) - if format == :name && !name.include?('"') - %(#{self.class.reference_prefix}"#{name}") + def to_reference(from_project = nil, format: :id) + reference = label_format_reference(format) + + if cross_project_reference?(from_project) + project.to_reference + reference else - "#{self.class.reference_prefix}#{id}" + reference end end + def open_issues_count issues.opened.count end @@ -95,4 +99,16 @@ class Label < ActiveRecord::Base def template? template end + + private + + def label_format_reference(format = :id) + raise StandardError, 'Unknown format' unless [:id, :name].include?(format) + + if format == :name && !name.include?('"') + %(#{self.class.reference_prefix}"#{name}") + else + "#{self.class.reference_prefix}#{id}" + end + end end diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index fd133e1ca1b..0614ca1e7c9 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -59,7 +59,6 @@ describe Label, models: true do context 'using id' do it 'returns a String reference to the object' do expect(label.to_reference).to eq "~#{label.id}" - expect(label.to_reference(double('project'))).to eq "~#{label.id}" end end @@ -73,5 +72,30 @@ describe Label, models: true do expect(label.to_reference(format: :name)).to eq "~#{label.id}" end end + + context 'using invalid format' do + it 'raises error' do + expect { label.to_reference(format: :invalid) } + .to raise_error StandardError, /Unknown format/ + end + end + + context 'cross project reference' do + let(:project) { create(:project) } + + context 'using name' do + it 'returns cross reference with label name' do + expect(label.to_reference(project, format: :name)) + .to eq %Q(#{label.project.to_reference}~"#{label.name}") + end + end + + context 'using id' do + it 'returns cross reference with label id' do + expect(label.to_reference(project, format: :id)) + .to eq %Q(#{label.project.to_reference}~#{label.id}) + end + end + end end end From dbc7bf7fe14249fdc1168c84bdfaad07bfbbbec0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 25 Feb 2016 12:45:38 +0100 Subject: [PATCH 012/112] Fix bug with return value in abstract reference filter When `object_class.link_reference_pattern` was `nil` because object being processed do not implemement link references `call` method returned `nil` instead of returning document, even if document has been processed in for basic reference pattern. --- lib/banzai/filter/abstract_reference_filter.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index cdbaecf8d90..34c38913474 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -94,6 +94,8 @@ module Banzai object_link_filter(link, object_class.link_reference_pattern, link_text: text) end end + + doc end # Replace references (like `!123` for merge requests) in text with links From a472c1bfd0b5c09e2c9b9da5ff0de3c90aaeda2f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 25 Feb 2016 13:49:42 +0100 Subject: [PATCH 013/112] Add support for cross project references for labels --- app/models/label.rb | 7 +- lib/banzai/filter/label_reference_filter.rb | 79 ++++++------------- .../filter/label_reference_filter_spec.rb | 19 +++++ 3 files changed, 49 insertions(+), 56 deletions(-) diff --git a/app/models/label.rb b/app/models/label.rb index 445f22ee1e0..c6abd6c9712 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -51,7 +51,8 @@ class Label < ActiveRecord::Base # Pattern used to extract label references from text def self.reference_pattern %r{ - #{reference_prefix} + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)} (?: (?\d+) | # Integer-based label ID, or (? @@ -62,6 +63,10 @@ class Label < ActiveRecord::Base }x end + def self.link_reference_pattern + nil + end + # Returns the String necessary to reference this Label in Markdown # # format - Symbol format to use (default: :id, optional: :name) diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb index 95e7d209119..ae5d4122e22 100644 --- a/lib/banzai/filter/label_reference_filter.rb +++ b/lib/banzai/filter/label_reference_filter.rb @@ -1,22 +1,18 @@ module Banzai module Filter # HTML filter that replaces label references with links. - class LabelReferenceFilter < ReferenceFilter - # Public: Find label references in text - # - # LabelReferenceFilter.references_in(text) do |match, id, name| - # "#{Label.find(id)}" - # end - # - # text - String text to search. - # - # Yields the String match, an optional Integer label ID, and an optional - # String label name. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(Label.reference_pattern) do |match| - yield match, $~[:label_id].to_i, $~[:label_name] + class LabelReferenceFilter < AbstractReferenceFilter + def self.object_class + Label + end + + def find_object(project, id) + project.labels.find(id) + end + + def self.references_in(text, pattern = Label.reference_pattern) + text.gsub(pattern) do |match| + yield match, $~[:label_id].to_i, $~[:label_name], $~[:project], $~ end end @@ -24,55 +20,28 @@ module Banzai { label: LazyReference.new(Label, node.attr("data-label")) } end - def call - replace_text_nodes_matching(Label.reference_pattern) do |content| - label_link_filter(content) - end + def references_in(text, pattern = Label.reference_pattern) + text.gsub(pattern) do |match| + project = project_from_ref($~[:project]) + params = label_params($~[:label_id].to_i, $~[:label_name]) + label = project.labels.find_by(params) - replace_link_nodes_with_href(Label.reference_pattern) do |link, text| - label_link_filter(link, link_text: text) - end - end - - # Replace label references in text with links to the label specified. - # - # text - String text to replace references in. - # - # Returns a String with label references replaced with links. All links - # have `gfm` and `gfm-label` class names attached for styling. - def label_link_filter(text, link_text: nil) - project = context[:project] - - self.class.references_in(text) do |match, id, name| - params = label_params(id, name) - - if label = project.labels.find_by(params) - url = url_for_label(project, label) - klass = reference_class(:label) - data = data_attribute( - original: link_text || match, - project: project.id, - label: label.id - ) - - text = link_text || render_colored_label(label) - - %(#{escape_once(text)}) + if label + yield match, label.id, $~[:project], $~ else match end end end - def url_for_label(project, label) + def url_for_object(label, project) h = Gitlab::Application.routes.url_helpers - h.namespace_project_issues_url( project.namespace, project, label_name: label.name, - only_path: context[:only_path]) + h.namespace_project_issues_url(project.namespace, project, label_name: label.name, + only_path: context[:only_path]) end - def render_colored_label(label) - LabelsHelper.render_colored_label(label) + def object_link_text(object, _matches) + LabelsHelper.render_colored_label(object) end # Parameters to pass to `Label.find_by` based on the given arguments diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 9d9e5cbac3d..96a403f2694 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -176,4 +176,23 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do expect(result[:references][:label]).to eq [label] end end + + describe 'cross project label references' do + let(:another_project) { create(:empty_project, :public) } + let(:label) { create(:label, project: another_project, color: '#00ff00') } + let(:reference) { label.to_reference(project) } + + let!(:result) { reference_filter("See #{reference}") } + + it 'points to referenced project issues page' do + expect(result.css('a').first.attr('href')) + .to eq urls.namespace_project_issues_url(another_project.namespace, + another_project, + label_name: label.name) + end + + it 'has valid color' do + expect(result.css('a span').first.attr('style')).to match /background-color: #00ff00/ + end + end end From 7183a7da6dbe5d2faf2ae03dfe4652ca6ed6dfde Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 25 Feb 2016 14:26:41 +0100 Subject: [PATCH 014/112] Add additional text prefix in cross project labels --- app/helpers/labels_helper.rb | 12 +++++++++--- app/models/label.rb | 3 ++- lib/banzai/filter/label_reference_filter.rb | 8 ++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 1c7fcc13b42..122f50081e4 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -50,7 +50,7 @@ module LabelsHelper @project.labels.pluck(:title) end - def render_colored_label(label) + def render_colored_label(label, label_prefix = '') label_color = label.color || Label::DEFAULT_COLOR text_color = text_color_for_bg(label_color) @@ -58,11 +58,16 @@ module LabelsHelper # by LabelReferenceFilter span = %() + - escape_once(label.name) + '' + label_prefix + escape_once(label.name) + '' span.html_safe end + def render_colored_cross_project_label(label) + label_prefix = "#{label.project.path_with_namespace} » " + render_colored_label(label, label_prefix) + end + def suggested_colors [ '#0033CC', @@ -119,5 +124,6 @@ module LabelsHelper end # Required for Banzai::Filter::LabelReferenceFilter - module_function :render_colored_label, :text_color_for_bg, :escape_once + module_function :render_colored_label, :render_colored_cross_project_label, + :text_color_for_bg, :escape_once end diff --git a/app/models/label.rb b/app/models/label.rb index c6abd6c9712..1bb4f35cea2 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -67,6 +67,7 @@ class Label < ActiveRecord::Base nil end + ## # Returns the String necessary to reference this Label in Markdown # # format - Symbol format to use (default: :id, optional: :name) @@ -78,6 +79,7 @@ class Label < ActiveRecord::Base # Label.first.to_reference(project) # => "gitlab-org/gitlab-ce~1" # # Returns a String + # def to_reference(from_project = nil, format: :id) reference = label_format_reference(format) @@ -88,7 +90,6 @@ class Label < ActiveRecord::Base end end - def open_issues_count issues.opened.count end diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb index ae5d4122e22..85fec970ebd 100644 --- a/lib/banzai/filter/label_reference_filter.rb +++ b/lib/banzai/filter/label_reference_filter.rb @@ -40,8 +40,12 @@ module Banzai only_path: context[:only_path]) end - def object_link_text(object, _matches) - LabelsHelper.render_colored_label(object) + def object_link_text(object, matches) + if context[:project] == object.project + LabelsHelper.render_colored_label(object) + else + LabelsHelper.render_colored_cross_project_label(object) + end end # Parameters to pass to `Label.find_by` based on the given arguments From 62dcdef969fe29f43abfbe2653e73ab09b2d1bf6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 26 Feb 2016 10:52:00 +0100 Subject: [PATCH 015/112] Improve cross project label look and feel --- app/helpers/labels_helper.rb | 11 ++++++----- spec/lib/banzai/filter/label_reference_filter_spec.rb | 10 ++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 122f50081e4..37fc1dab876 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -50,22 +50,23 @@ module LabelsHelper @project.labels.pluck(:title) end - def render_colored_label(label, label_prefix = '') + def render_colored_label(label, label_suffix = '') label_color = label.color || Label::DEFAULT_COLOR text_color = text_color_for_bg(label_color) # Intentionally not using content_tag here so that this method can be called # by LabelReferenceFilter span = %() + - label_prefix + escape_once(label.name) + '' + %(style="background-color: #{label_color}; color: #{text_color}">) + + %(#{escape_once(label.name)}#{label_suffix}) span.html_safe end def render_colored_cross_project_label(label) - label_prefix = "#{label.project.path_with_namespace} » " - render_colored_label(label, label_prefix) + label_suffix = label.project.name + label_suffix = " « #{label_suffix}" + render_colored_label(label, label_suffix) end def suggested_colors diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 96a403f2694..8786095fbe4 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -178,7 +178,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do end describe 'cross project label references' do - let(:another_project) { create(:empty_project, :public) } + let(:project_name) { 'Some project' } + let(:another_project) { create(:empty_project, :public, name: project_name) } let(:label) { create(:label, project: another_project, color: '#00ff00') } let(:reference) { label.to_reference(project) } @@ -192,7 +193,12 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do end it 'has valid color' do - expect(result.css('a span').first.attr('style')).to match /background-color: #00ff00/ + expect(result.css('a span').first.attr('style')) + .to match /background-color: #00ff00/ + end + + it 'contains cross project content' do + expect(result.css('a').first.text).to eq "#{label.name} « #{project_name}" end end end From 53c351a2a573cb448eafdc8ed7e187225f6aa012 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 1 Mar 2016 11:16:10 +0100 Subject: [PATCH 016/112] Add Changelog entry for cross-project label references --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 7b837a46509..8575befa432 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ v 8.6.0 (unreleased) - Don't load all of GitLab in mail_room - Strip leading and trailing spaces in URL validator (evuez) - Return empty array instead of 404 when commit has no statuses in commit status API + - Add support for cross-project label references - Update documentation to reflect Guest role not being enforced on internal projects - Allow search for logged out users From d49c8e8e3d0f0dee7886d421c91684d92f89c78e Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 1 Mar 2016 11:39:49 +0100 Subject: [PATCH 017/112] Mention cross-project labels in markdown documentation --- app/models/label.rb | 4 ++++ doc/markdown/markdown.md | 1 + 2 files changed, 5 insertions(+) diff --git a/app/models/label.rb b/app/models/label.rb index 1bb4f35cea2..f8817d9dc54 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -48,7 +48,11 @@ class Label < ActiveRecord::Base '~' end + ## # Pattern used to extract label references from text + # + # This pattern supports cross-project references. + # def self.reference_pattern %r{ (#{Project.reference_pattern})? diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index c400cdac64d..cbf57db5684 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -207,6 +207,7 @@ GFM also recognizes certain cross-project references: | `namespace/project$123` | snippet | | `namespace/project@9ba12248` | specific commit | | `namespace/project@9ba12248...b19a04f5` | commit range comparison | +| `namespace/project~"Some label"` | issues with given label | ## Task Lists From 34769efa790ca39718c6ab218e8018a501f861ab Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Mar 2016 09:31:07 +0100 Subject: [PATCH 018/112] Change content of cross project label --- app/helpers/labels_helper.rb | 4 ++-- spec/lib/banzai/filter/label_reference_filter_spec.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 37fc1dab876..93806ac199e 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -64,8 +64,8 @@ module LabelsHelper end def render_colored_cross_project_label(label) - label_suffix = label.project.name - label_suffix = " « #{label_suffix}" + label_suffix = label.project.name_with_namespace + label_suffix = " in #{label_suffix}" render_colored_label(label, label_suffix) end diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 8786095fbe4..e2d21f53b7e 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -178,8 +178,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do end describe 'cross project label references' do - let(:project_name) { 'Some project' } - let(:another_project) { create(:empty_project, :public, name: project_name) } + let(:another_project) { create(:empty_project, :public) } + let(:project_name) { another_project.name_with_namespace } let(:label) { create(:label, project: another_project, color: '#00ff00') } let(:reference) { label.to_reference(project) } @@ -198,7 +198,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do end it 'contains cross project content' do - expect(result.css('a').first.text).to eq "#{label.name} « #{project_name}" + expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}" end end end From fa639df5c74c4f41c13e1bbdb897a968b6129621 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Mar 2016 10:39:18 +0100 Subject: [PATCH 019/112] Remove redundant code in cross project labels implementation --- app/models/label.rb | 7 ++++--- lib/banzai/filter/label_reference_filter.rb | 4 ---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/models/label.rb b/app/models/label.rb index f8817d9dc54..5ff644b8426 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -85,7 +85,8 @@ class Label < ActiveRecord::Base # Returns a String # def to_reference(from_project = nil, format: :id) - reference = label_format_reference(format) + format_reference = label_format_reference(format) + reference = "#{self.class.reference_prefix}#{format_reference}" if cross_project_reference?(from_project) project.to_reference + reference @@ -116,9 +117,9 @@ class Label < ActiveRecord::Base raise StandardError, 'Unknown format' unless [:id, :name].include?(format) if format == :name && !name.include?('"') - %(#{self.class.reference_prefix}"#{name}") + %("#{name}") else - "#{self.class.reference_prefix}#{id}" + id end end end diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb index 85fec970ebd..8147e5ed3c7 100644 --- a/lib/banzai/filter/label_reference_filter.rb +++ b/lib/banzai/filter/label_reference_filter.rb @@ -16,10 +16,6 @@ module Banzai end end - def self.referenced_by(node) - { label: LazyReference.new(Label, node.attr("data-label")) } - end - def references_in(text, pattern = Label.reference_pattern) text.gsub(pattern) do |match| project = project_from_ref($~[:project]) From 566ea8543d971c4b811b2939b0677c5db52feab3 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Fri, 25 Sep 2015 18:09:08 +0200 Subject: [PATCH 020/112] Tests: extract a merge_request_path helper --- features/steps/shared/paths.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 6432a786bfc..f7812a707c4 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -388,13 +388,11 @@ module SharedPaths end step 'I visit merge request page "Bug NS-04"' do - mr = MergeRequest.find_by(title: "Bug NS-04") - visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) + visit merge_request_path("Bug NS-04") end step 'I visit merge request page "Bug NS-05"' do - mr = MergeRequest.find_by(title: "Bug NS-05") - visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) + visit merge_request_path("Bug NS-05") end step 'I visit merge request page "Bug CO-01"' do @@ -503,6 +501,11 @@ module SharedPaths Project.find_by!(name: 'Shop') end + def merge_request_path(title) + mr = MergeRequest.find_by(title: title) + namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) + end + # ---------------------------------------- # Errors # ---------------------------------------- From 543845f7efe0b70926ea699eaf1e413fa878b285 Mon Sep 17 00:00:00 2001 From: Pierre de La Morinerie Date: Thu, 4 Feb 2016 19:23:58 +0100 Subject: [PATCH 021/112] Indicate how much an MR branch diverges from the target branch --- CHANGELOG | 1 + app/models/merge_request.rb | 23 +++++++ .../projects/merge_requests/_show.html.haml | 2 + features/project/merge_requests.feature | 10 +++ features/steps/project/merge_requests.rb | 29 ++++++++- features/steps/shared/paths.rb | 8 +++ spec/factories/merge_requests.rb | 10 +++ spec/models/merge_request_spec.rb | 64 +++++++++++++++++++ 8 files changed, 146 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 69a1927b765..97ffbe67eec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.6.0 (unreleased) - Fix issue when pushing to projects ending in .wiki - Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - Don't load all of GitLab in mail_room + - Indicate how much an MR diverged from the target branch (Pierre de La Morinerie) - Strip leading and trailing spaces in URL validator (evuez) - Return empty array instead of 404 when commit has no statuses in commit status API - Update documentation to reflect Guest role not being enforced on internal projects diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1543ef311d7..f89bb6e9e4b 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -524,6 +524,29 @@ class MergeRequest < ActiveRecord::Base end end + def diverged_commits_count + cache = Rails.cache.read(:"merge_request_#{id}_diverged_commits") + + if cache.blank? || cache[:source_sha] != source_sha || cache[:target_sha] != target_sha + cache = { + source_sha: source_sha, + target_sha: target_sha, + diverged_commits_count: compute_diverged_commits_count + } + Rails.cache.write(:"merge_request_#{id}_diverged_commits", cache) + end + + cache[:diverged_commits_count] + end + + def compute_diverged_commits_count + Gitlab::Git::Commit.between(target_project.repository.raw_repository, source_sha, target_sha).size + end + + def diverged_from_target_branch? + diverged_commits_count > 0 + end + def ci_commit @ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project end diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index d7bc26e24b9..44e25e034e4 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -34,6 +34,8 @@ %span into = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do = @merge_request.target_branch + - 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" = render "projects/merge_requests/widget/show.html.haml" diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 495f25f28e7..a69089f00c4 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -26,6 +26,16 @@ Feature: Project Merge Requests When I visit project "Shop" merge requests page Then I should see "other_branch" branch + Scenario: I should not see the numbers of diverged commits if the branch is rebased on the target + Given project "Shop" have "Bug NS-07" open merge request with rebased branch + When I visit merge request page "Bug NS-07" + Then I should not see the diverged commits count + + Scenario: I should see the numbers of diverged commits if the branch diverged from the target + Given project "Shop" have "Bug NS-08" open merge request with diverged branch + When I visit merge request page "Bug NS-08" + Then I should see the diverged commits count + Scenario: I should see rejected merge requests Given I click link "Closed" Then I should see "Feature NS-03" in merge requests diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index dde864f5180..8bf423cc64b 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -60,7 +60,6 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps expect(page).not_to have_content "Feature NS-03" end - step 'I should not see "Bug NS-04" in merge requests' do expect(page).not_to have_content "Bug NS-04" end @@ -121,6 +120,22 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps author: project.users.first) end + step 'project "Shop" have "Bug NS-07" open merge request with rebased branch' do + create(:merge_request, :rebased, + title: "Bug NS-07", + source_project: project, + target_project: project, + author: project.users.first) + end + + step 'project "Shop" have "Bug NS-08" open merge request with diverged branch' do + create(:merge_request, :diverged, + title: "Bug NS-08", + source_project: project, + target_project: project, + author: project.users.first) + end + step 'project "Shop" have "Feature NS-03" closed merge request' do create(:closed_merge_request, title: "Feature NS-03", @@ -490,6 +505,18 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end + step 'I should see the diverged commits count' do + page.within ".mr-source-target" do + expect(page).to have_content /([0-9]+ commits behind)/ + end + end + + step 'I should not see the diverged commits count' do + page.within ".mr-source-target" do + expect(page).not_to have_content /([0-9]+ commit[s]? behind)/ + end + end + step 'I should see "Bug NS-05" at the top' do expect(page.find('ul.content-list.mr-list li.merge-request:first-child')).to have_content("Bug NS-05") end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index f7812a707c4..da9d1503ebc 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -395,6 +395,14 @@ module SharedPaths visit merge_request_path("Bug NS-05") end + step 'I visit merge request page "Bug NS-07"' do + visit merge_request_path("Bug NS-07") + end + + step 'I visit merge request page "Bug NS-08"' do + visit merge_request_path("Bug NS-08") + end + step 'I visit merge request page "Bug CO-01"' do mr = MergeRequest.find_by(title: "Bug CO-01") visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr) diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 00de7bb5294..ca1c636fce4 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -69,6 +69,16 @@ FactoryGirl.define do target_branch "master" end + trait :rebased do + source_branch "markdown" + target_branch "improve/awesome" + end + + trait :diverged do + source_branch "feature" + target_branch "master" + end + trait :merge_when_build_succeeds do merge_when_build_succeeds true merge_user author diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index c51f34034d7..59c40922abb 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -274,6 +274,70 @@ describe MergeRequest, models: true do end end + describe '#diverged_commits_count' do + let(:project) { create(:project) } + let(:fork_project) { create(:project, forked_from_project: project) } + + context 'diverged on same repository' do + subject(:merge_request_with_divergence) { create(:merge_request, :diverged, source_project: project, target_project: project) } + + it 'counts commits that are on target branch but not on source branch' do + expect(subject.diverged_commits_count).to eq(5) + end + end + + context 'diverged on fork' do + subject(:merge_request_fork_with_divergence) { create(:merge_request, :diverged, source_project: fork_project, target_project: project) } + + it 'counts commits that are on target branch but not on source branch' do + expect(subject.diverged_commits_count).to eq(5) + end + end + + context 'rebased on fork' do + subject(:merge_request_rebased) { create(:merge_request, :rebased, source_project: fork_project, target_project: project) } + + it 'counts commits that are on target branch but not on source branch' do + expect(subject.diverged_commits_count).to eq(0) + end + end + + describe 'caching' do + before(:example) do + allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + end + + it 'caches the output' do + expect(subject).to receive(:compute_diverged_commits_count). + once. + and_return(2) + + subject.diverged_commits_count + subject.diverged_commits_count + end + + it 'invalidates the cache when the source sha changes' do + expect(subject).to receive(:compute_diverged_commits_count). + twice. + and_return(2) + + subject.diverged_commits_count + allow(subject).to receive(:source_sha).and_return('123abc') + subject.diverged_commits_count + end + + it 'invalidates the cache when the target sha changes' do + expect(subject).to receive(:compute_diverged_commits_count). + twice. + and_return(2) + + subject.diverged_commits_count + allow(subject).to receive(:target_sha).and_return('123abc') + subject.diverged_commits_count + end + end + end + it_behaves_like 'an editable mentionable' do subject { create(:merge_request) } From 21a05328ffd5cb9130ae516faa7dd672cacba90c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Mar 2016 15:19:27 +0200 Subject: [PATCH 022/112] Security: Fix issue auto closing --- app/services/git_push_service.rb | 4 +++- app/services/merge_requests/post_merge_service.rb | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 9ba200f7bde..b50a7a4217c 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -96,7 +96,9 @@ class GitPushService < BaseService # a different branch. closed_issues = commit.closes_issues(current_user) closed_issues.each do |issue| - Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit) + if can?(current_user, :update_issue, issue) + Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit) + end end end diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb index 8f25c5e2496..ebb67c7db65 100644 --- a/app/services/merge_requests/post_merge_service.rb +++ b/app/services/merge_requests/post_merge_service.rb @@ -21,7 +21,9 @@ module MergeRequests closed_issues = merge_request.closes_issues(current_user) closed_issues.each do |issue| - Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request) + if can?(current_user, :update_issue, issue) + Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request) + end end end From cf232411ae37b68c9d6ab20deac3fc8d99b44124 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Thu, 3 Mar 2016 11:21:32 -0500 Subject: [PATCH 023/112] Added Troubleshooting information for most used services. --- doc/integration/README.md | 31 +++++++ doc/integration/ldap.md | 27 ++++++ doc/integration/saml.md | 40 ++++++++- doc/project_services/jira.md | 13 +++ doc/workflow/lfs/lfs_administration.md | 8 +- .../lfs/manage_large_binaries_with_git_lfs.md | 85 +++++++++++++------ 6 files changed, 173 insertions(+), 31 deletions(-) diff --git a/doc/integration/README.md b/doc/integration/README.md index 281eea8363d..7c8f785a61f 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -39,3 +39,34 @@ please see the [project_services directory][projects-code]. [jenkins]: http://doc.gitlab.com/ee/integration/jenkins.html [Project Service]: ../project_services/project_services.md [projects-code]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/models/project_services + +## SSL certificate errors + +When trying to integrate GitLab with services that are using self-signed certificates, +it is very likely that SSL certificate errors will occur on different parts of the +application, most likely Sidekiq. There are 2 approaches you can take to solve this: + +1. Add the root certificate to the trusted chain of the OS. +1. If using Omnibus, you can add the certificate to GitLab's trusted certificates. + +**OS main trusted chain** + +This [resource](http://kb.kerio.com/product/kerio-connect/server-configuration/ssl-certificates/adding-trusted-root-certificates-to-the-server-1605.html) +has all the information you need to add a certificate to the main trusted chain. + +This [answer](http://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu) +at SuperUser also has relevant information. + +**Omnibus Trusted Chain** + +It is enough to concatenate the certificate to the main trusted certificate: + +```bash +cat jira.pem >> /opt/gitlab/embedded/ssl/certs/cacert.pem +``` + +After that restart GitLab with: + +```bash +sudo gitlab-ctl restart +``` diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md index f256477196b..ac15a2cdffc 100644 --- a/doc/integration/ldap.md +++ b/doc/integration/ldap.md @@ -204,3 +204,30 @@ When setting `method: ssl`, the underlying authentication method used by `omniauth-ldap` is `simple_tls`. This method establishes TLS encryption with the LDAP server before any LDAP-protocol data is exchanged but no validation of the LDAP server's SSL certificate is performed. + +## Troubleshooting + +### Common problems + +**Invalid credentials when logging in** + +Make sure the user you are binding with has enough permissions to read the user's +tree and traverse it. + +Also make sure that the `user_filter` is not blocking otherwise valid users. + +To make sure that the LDAP settings are correct and GitLab can see your users, +execute the following command: + +For Omnibus installations: + +```bash +sudo gitlab-rake gitlab:ldap:check +``` + +For installations from source: + +```bash +sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production +``` + diff --git a/doc/integration/saml.md b/doc/integration/saml.md index c84113556cd..16e47bb99b0 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -133,12 +133,18 @@ will be returned to GitLab and will be signed in. ## Troubleshooting +### Common problems + +**500 error after login** + If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page, this likely indicates that GitLab could not get the email address for the SAML user. Make sure the IdP provides a claim containing the user's email address, using claim name `email` or `mail`. +**Redirect back to login screen with no evident error** + If after signing in into your SAML server you are redirected back to the sign in page and no error is displayed, check your `production.log` file. It will most likely contain the message `Can't verify CSRF token authenticity`. This means that there is an error during @@ -147,4 +153,36 @@ the SAML request, but this error never reaches GitLab due to the CSRF check. To bypass this you can add `skip_before_action :verify_authenticity_token` to the `omniauth_callbacks_controller.rb` file. This will allow the error to hit GitLab, where it can then be seen in the usual logs, or as a flash message in the login -screen. \ No newline at end of file +screen. + +**Invalid audience** + +This error means that the IdP doesn't recognize GitLab as a valid sender and +receiver of SAML requests. Make sure to add the GitLab callback URL to the approved +audiences of the IdP server. + +**Missing claims** + +The IdP server needs to pass certain information in order for GitLab to either +create an account, or match the login information to an existing account. `email` +is the minimum amount of information that needs to be passed. If the IdP server +is not providing this information, all SAML requests will fail. + +Make sure this information is provided. + +**Key validation error, Digest mismatch or Fingerprint mismatch** + +These errors all come from a similar place, the SAML certificate. SAML requests +need to be validated using a fingerprint, a certificate or a validator. + +For this you need take the following into account: + +- If no certificate is provided in the settings, a fingerprint or fingerprint + validator needs to be provided and the response from the server must contain + a certificate (``) +- If a certificate is provided in the settings, it is no longer necessary for + the request to contain one. In this case the fingerprint or fingerprint + validators are optional + +Make sure that one of the above described scenarios is valid, or the requests will +fail with one of the mentioned errors. \ No newline at end of file diff --git a/doc/project_services/jira.md b/doc/project_services/jira.md index 7c12557a321..623ec857cbe 100644 --- a/doc/project_services/jira.md +++ b/doc/project_services/jira.md @@ -219,3 +219,16 @@ You can see from the above image that there are four references to GitLab: [JIRA Core]: https://www.atlassian.com/software/jira/core "The JIRA Core website" [jira-ce]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2146 "MR - Backport JIRA service" [8_3_post]: https://about.gitlab.com/2015/12/22/gitlab-8-3-released/ "GitLab 8.3 release post" + +## Troubleshooting + +**GitLab is unable to comment on a ticket** + +Make sure that the user you set up for GitLab to communicate with JIRA has the +correct access permission to post comments on a ticket and to also transition the +ticket, if you'd like GitLab to also take care of closing them. + +**GitLab is unable to close a ticket** + +Make sure the the `Transition ID` you set within the JIRA settings matches the +one your project needs to close a ticket. diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md index 5076b2697a3..36cb9da2380 100644 --- a/doc/workflow/lfs/lfs_administration.md +++ b/doc/workflow/lfs/lfs_administration.md @@ -9,7 +9,8 @@ Documentation on how to use Git LFS are under [Managing large binary files with ## Configuration -Git LFS objects can be large in size. By default, they are stored on the server GitLab is installed on. +Git LFS objects can be large in size. By default, they are stored on the server +GitLab is installed on. There are two configuration options to help GitLab server administrators: @@ -37,5 +38,8 @@ In `config/gitlab.yml`: ## Known limitations -* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported +* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) + is not supported * Currently, removing LFS objects from GitLab Git LFS storage is not supported +* LFS authentications via SSH is not supported for the time being +* Only compatible with the GitLFS client versions 1.1.0 or 1.0.2. diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index b59e92cb317..ba91685a20b 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -1,17 +1,21 @@ # Git LFS -Managing large files such as audio, video and graphics files has always been one of the shortcomings of Git. -The general recommendation is to not have Git repositories larger than 1GB to preserve performance. +Managing large files such as audio, video and graphics files has always been one +of the shortcomings of Git. The general recommendation is to not have Git repositories +larger than 1GB to preserve performance. -GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain -environments it is not always convenient to use different commands to differentiate between the large files and regular ones. +GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) +(EE only), however in certain environments it is not always convenient to use +different commands to differentiate between the large files and regular ones. -Git LFS makes this simpler for the end user by removing the requirement to learn new commands. +Git LFS makes this simpler for the end user by removing the requirement to +learn new commands. ## How it works -Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests. -Once the request is authorized, Git LFS client receives instructions from where to fetch or where to push the large file. +Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication +to authorize client requests. Once the request is authorized, Git LFS client receives +instructions from where to fetch or where to push the large file. ## GitLab server configuration @@ -24,15 +28,19 @@ Documentation for GitLab instance administrators is under [LFS administration do ## Known limitations -* Git LFS v1 original API is not supported since it was deprecated early in LFS development +* Git LFS v1 original API is not supported since it was deprecated early in LFS + development * When SSH is set as a remote, Git LFS objects still go through HTTPS -* Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended -* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting) +* Any Git LFS request will ask for HTTPS credentials to be provided so good Git + credentials store is recommended +* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have + to add the URL to Git config manually (see #troubleshooting) ## Using Git LFS -Lets take a look at the workflow when you need to check large files into your Git repository with Git LFS: -For example, if you want to upload a very large file and check it into your Git repository: +Lets take a look at the workflow when you need to check large files into your Git +repository with Git LFS. For example, if you want to upload a very large file and +check it into your Git repository: ```bash git clone git@gitlab.example.com:group/project.git @@ -40,7 +48,8 @@ git lfs init # initialize the Git LFS project project git lfs track "*.iso" # select the file extensions that you want to treat as large files ``` -Once a certain file extension is marked for tracking as a LFS object you can use Git as usual without having to redo the command to track a file with the same extension: +Once a certain file extension is marked for tracking as a LFS object you can use +Git as usual without having to redo the command to track a file with the same extension: ```bash cp ~/tmp/debian.iso ./ # copy a large file into the current directory @@ -49,13 +58,17 @@ git commit -am "Added Debian iso" # commit the file meta data git push origin master # sync the git repo and large file to the GitLab server ``` -Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication. +Cloning the repository works the same as before. Git automatically detects the +LFS-tracked files and clones them via HTTP. If you performed the git clone +command with a SSH URL, you have to enter your GitLab credentials for HTTP +authentication. ```bash git clone git@gitlab.example.com:group/project.git ``` -If you already cloned the repository and you want to get the latest LFS object that are on the remote repository, eg. from branch `master`: +If you already cloned the repository and you want to get the latest LFS object +that are on the remote repository, eg. from branch `master`: ```bash git lfs fetch master @@ -73,8 +86,8 @@ Check if you have permissions to push to the project or fetch from the project. * Project is not allowed to access the LFS object -LFS object you are trying to push to the project or fetch from the project is not available to the project anymore. -Probably the object was removed from the server. +LFS object you are trying to push to the project or fetch from the project is not +available to the project anymore. Probably the object was removed from the server. * Local git repository is using deprecated LFS API @@ -89,16 +102,26 @@ git lfs logs last If the status `error 501` is shown, it is because: -* Git LFS support is not enabled on the GitLab server. Check with your GitLab administrator why Git LFS is not enabled on the server. See [LFS administration documentation](lfs_administration.md) for instructions on how to enable LFS support. +* Git LFS support is not enabled on the GitLab server. Check with your GitLab + administrator why Git LFS is not enabled on the server. See + [LFS administration documentation](lfs_administration.md) for instructions + on how to enable LFS support. -* Git LFS client version is not supported by GitLab server. Check your Git LFS version with `git lfs version`. Check the Git config of the project for traces of deprecated API with `git lfs -l`. If `batch = false` is set in the config, remove the line and try to update your Git LFS client. Only version 1.0.1 and newer are supported. +* Git LFS client version is not supported by GitLab server. Check your Git LFS + version with `git lfs version`. Check the Git config of the project for traces + of deprecated API with `git lfs -l`. If `batch = false` is set in the config, + remove the line and try to update your Git LFS client. Only version 1.0.1 and + newer are supported. ### getsockopt: connection refused -If you push a LFS object to a project and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, -the LFS client is trying to reach GitLab through HTTPS. However, your GitLab instance is being served on HTTP. +If you push a LFS object to a project and you receive an error similar to: +`Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, +the LFS client is trying to reach GitLab through HTTPS. However, your GitLab +instance is being served on HTTP. -This behaviour is caused by Git LFS using HTTPS connections by default when a `lfsurl` is not set in the Git config. +This behaviour is caused by Git LFS using HTTPS connections by default when a +`lfsurl` is not set in the Git config. To prevent this from happening, set the lfs url in project Git config: @@ -109,18 +132,24 @@ git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs/o ### Credentials are always required when pushing an object -Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing the LFS object on every push for every object, user HTTPS credentials are required. +Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing +the LFS object on every push for every object, user HTTPS credentials are required. -By default, Git has support for remembering the credentials for each repository you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials). +By default, Git has support for remembering the credentials for each repository +you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials). -For example, you can tell Git to remember the password for a period of time in which you expect to push the objects: +For example, you can tell Git to remember the password for a period of time in +which you expect to push the objects: ```bash git config --global credential.helper 'cache --timeout=3600' ``` -This will remember the credentials for an hour after which Git operations will require re-authentication. +This will remember the credentials for an hour after which Git operations will +require re-authentication. -If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, you can use `wincred` or Microsoft's [Git Credential Manager for Windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases). +If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. +For Windows, you can use `wincred` or Microsoft's [Git Credential Manager for Windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases). -More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). \ No newline at end of file +More details about various methods of storing the user credentials can be found +on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). \ No newline at end of file From fc90d9e5896cdcccedb697fd4536f126d10f3f8e Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 3 Mar 2016 17:59:47 +0100 Subject: [PATCH 024/112] Tell clients/proxies to cache raw blob requests --- .../projects/avatars_controller.rb | 2 ++ app/controllers/projects/raw_controller.rb | 2 ++ app/helpers/blob_helper.rb | 25 +++++++++++++++++++ app/models/project.rb | 7 +++--- app/views/projects/blob/_image.html.haml | 2 +- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index b64dbbd89ce..6de7888888f 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -7,6 +7,8 @@ class Projects::AvatarsController < Projects::ApplicationController @blob = @repository.blob_at_branch('master', @project.avatar_in_git) if @blob headers['X-Content-Type-Options'] = 'nosniff' + set_cache_headers + check_etag! headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) headers['Content-Disposition'] = 'inline' headers['Content-Type'] = safe_content_type(@blob) diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index d9723acb1d9..b6ff08262d7 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -12,6 +12,8 @@ class Projects::RawController < Projects::ApplicationController if @blob headers['X-Content-Type-Options'] = 'nosniff' + check_etag! + set_cache_headers if @blob.lfs_pointer? send_lfs_object diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 7f63a2e2cb4..adb56e49c62 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -152,4 +152,29 @@ module BlobHelper 'application/octet-stream' end end + + def set_cache_headers + if @project.visibility_level == Project::PUBLIC + cache_control = 'public, ' + else + cache_control = 'private, ' + end + + if @ref && @commit && @ref == @commit.id + # This is a link to a commit by its commit SHA. That means that the blob + # is immutable. + cache_control << 'max-age=600' # 10 minutes + else + # A branch or tag points at this blob. That means that the expected blob + # value may change over time. + cache_control << 'max-age=60' # 1 minute + end + + headers['Cache-Control'] = cache_control + headers['ETag'] = @blob.id + end + + def check_etag! + stale?(etag: @blob.id) + end end diff --git a/app/models/project.rb b/app/models/project.rb index 148eab692ff..aba48f87be3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -56,6 +56,7 @@ class Project < ActiveRecord::Base extend Gitlab::ConfigHelper UNKNOWN_IMPORT_URL = 'http://unknown.git' + AVATAR_BRANCH = 'master' default_value_for :archived, false default_value_for :visibility_level, gitlab_config_features.visibility_level @@ -544,9 +545,9 @@ class Project < ActiveRecord::Base end def avatar_in_git - @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png') - @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg') - @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif') + @avatar_file ||= 'logo.png' if repository.blob_at_branch(AVATAR_BRANCH, 'logo.png') + @avatar_file ||= 'logo.jpg' if repository.blob_at_branch(AVATAR_BRANCH, 'logo.jpg') + @avatar_file ||= 'logo.gif' if repository.blob_at_branch(AVATAR_BRANCH, 'logo.gif') @avatar_file end diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml index 3c11b97921f..18caddabd39 100644 --- a/app/views/projects/blob/_image.html.haml +++ b/app/views/projects/blob/_image.html.haml @@ -6,4 +6,4 @@ - blob = sanitize_svg(blob) %img{src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}"} - else - %img{src: namespace_project_raw_path(@project.namespace, @project, @id)} + %img{src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path))} From b0adc07b3ca30430549886c3b9906c24cc9d2c6a Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Thu, 3 Mar 2016 19:16:38 +0000 Subject: [PATCH 025/112] add link to web hooks [skip ci] --- doc/hooks/custom_hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md index 0f2665a3bf7..5a4b2f43ba7 100644 --- a/doc/hooks/custom_hooks.md +++ b/doc/hooks/custom_hooks.md @@ -2,7 +2,7 @@ **Note: Custom git hooks must be configured on the filesystem of the GitLab server. Only GitLab server administrators will be able to complete these tasks. -Please explore webhooks as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).** +Please explore [web hooks](doc/web_hooks/web_hooks.md) as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).** Git natively supports hooks that are executed on different actions. Examples of server-side git hooks include pre-receive, post-receive, and update. From b0d24ab1b52a78bf855a70886113b79707036029 Mon Sep 17 00:00:00 2001 From: evuez Date: Wed, 24 Feb 2016 13:14:37 +0100 Subject: [PATCH 026/112] Add fields to GET /users/* API endpoints for admins Added fields are last_sign_in_at and confirmed_at. They are available for GET /users/ and GET /users/:id for admins. Closes #840 --- CHANGELOG | 1 + doc/api/users.md | 2 ++ lib/api/entities.rb | 2 ++ spec/requests/api/users_spec.rb | 2 ++ 4 files changed, 7 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index d61c5e74f3a..0bbcd6b867e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.6.0 (unreleased) - Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - Don't load all of GitLab in mail_room - Strip leading and trailing spaces in URL validator (evuez) + - Add "last_sign_in_at" and "confirmed_at" to GET /users/* API endpoints for admins (evuez) - Return empty array instead of 404 when commit has no statuses in commit status API - Update documentation to reflect Guest role not being enforced on internal projects - Allow search for logged out users diff --git a/doc/api/users.md b/doc/api/users.md index b7fc903825e..82c57a2fd43 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -151,6 +151,8 @@ Parameters: "name": "John Smith", "state": "active", "created_at": "2012-05-23T08:00:58Z", + "confirmed_at": "2012-05-23T08:00:58Z", + "last_sign_in_at": "2015-03-23T08:00:58Z", "bio": null, "skype": "", "linkedin": "", diff --git a/lib/api/entities.rb b/lib/api/entities.rb index b021db8fa5b..0779fb881a0 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -23,6 +23,8 @@ module API end class UserFull < User + expose :last_sign_in_at + expose :confirmed_at expose :email expose :theme_id, :color_scheme_id, :projects_limit, :current_sign_in_at expose :identities, using: Entities::Identity diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index b82c5c7685f..96e8c8c51f8 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -47,6 +47,8 @@ describe API::API, api: true do expect(json_response.first.keys).to include 'identities' expect(json_response.first.keys).to include 'can_create_project' expect(json_response.first.keys).to include 'two_factor_enabled' + expect(json_response.first.keys).to include 'last_sign_in_at' + expect(json_response.first.keys).to include 'confirmed_at' end end end From 97093a52984f1f4682b5d3432f7666f3c806c4ca Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 4 Mar 2016 15:17:12 +0100 Subject: [PATCH 027/112] Show at most 100 commits in the web UI When rendering 1 commit takes over 5ms (on my laptop: around 9ms), it saves seconds in rendering time to show 100 instead of 500 commits. --- app/models/merge_request_diff.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index df08d3a6dfb..33884118595 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -17,7 +17,7 @@ class MergeRequestDiff < ActiveRecord::Base include Sortable # Prevent store of diff if commits amount more then 500 - COMMITS_SAFE_SIZE = 500 + COMMITS_SAFE_SIZE = 100 belongs_to :merge_request From 5eeea4b7c69d29e8e8371b3c63f25938dcca7bb6 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 4 Mar 2016 15:20:30 +0100 Subject: [PATCH 028/112] Limit the number of commits shown in MRs This prevents timeouts when creating a MR with 1000s of commits. --- app/views/projects/commits/_commits.html.haml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index 6c631228002..e708b2df540 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -1,7 +1,13 @@ - unless defined?(project) - project = @project +- if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE + - commits = @commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE) + - overflow = true +- else + - commits = @commits + - overflow = false -- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits| +- commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits| .row.commits-row .col-md-2.hidden-xs.hidden-sm %h5.commits-row-date @@ -13,3 +19,7 @@ %ul.bordered-list = render commits, project: project %hr.lists-separator + +- if overflow + .alert.alert-warning + Not shown: #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} more commits From f943156b5e4cf5e00ea5373de55deb993c72662d Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 4 Mar 2016 15:45:58 +0100 Subject: [PATCH 029/112] Put 'hidden commits' logic in CommitsHelper --- app/helpers/commits_helper.rb | 12 ++++++++++++ app/views/projects/commits/_commit_list.html.haml | 11 +++++++---- app/views/projects/commits/_commits.html.haml | 12 ++++-------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index a09e91578b6..4353ceaa757 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -211,4 +211,16 @@ module CommitsHelper def clean(string) Sanitize.clean(string, remove_contents: true) end + + def limited_commits(commits) + if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE + # Not 100% sure we need to decorate but it is idempotent and not so slow + [ + commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), + commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE + ] + else + [commits, 0] + end + end end diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml index ce60fbdf032..5fe4d918833 100644 --- a/app/views/projects/commits/_commit_list.html.haml +++ b/app/views/projects/commits/_commit_list.html.haml @@ -1,11 +1,14 @@ +- commits, hidden = limited_commits(@commits, @project) +- commits = Commit.decorate(commits, @project) + %div.panel.panel-default .panel-heading Commits (#{@commits.count}) - - if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE + - if hidden > 0 %ul.well-list - - Commit.decorate(@commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), @project).each do |commit| + - commits.each do |commit| = render "projects/commits/inline_commit", commit: commit, project: @project %li.warning-row.unstyled - other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues. + other #{hidden} commits hidden to prevent performance issues. - else - %ul.well-list= render Commit.decorate(@commits, @project), project: @project + %ul.well-list= render commits, project: @project diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index e708b2df540..998251094a4 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -1,11 +1,7 @@ - unless defined?(project) - project = @project -- if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - - commits = @commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE) - - overflow = true -- else - - commits = @commits - - overflow = false + +- commits, hidden = limited_commits(@commits) - commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits| .row.commits-row @@ -20,6 +16,6 @@ = render commits, project: project %hr.lists-separator -- if overflow +- if hidden > 0 .alert.alert-warning - Not shown: #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} more commits + Not shown: #{hidden} more commits From 4cb3a4a73eb9f650bf2f16a10030861b1b7b9997 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 4 Mar 2016 15:47:11 +0100 Subject: [PATCH 030/112] Remove outdated comment --- app/helpers/commits_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 4353ceaa757..f994c9e6170 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -214,7 +214,6 @@ module CommitsHelper def limited_commits(commits) if commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE - # Not 100% sure we need to decorate but it is idempotent and not so slow [ commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE), commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE From acb211215c30244612eae6219539ced4a071b0dc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Mar 2016 16:11:33 +0100 Subject: [PATCH 031/112] Add option to show archived projects on dahsboard Signed-off-by: Dmitriy Zaporozhets --- app/controllers/concerns/filter_projects.rb | 15 ++++++++++++ .../dashboard/projects_controller.rb | 24 +++++++------------ .../explore/projects_controller.rb | 11 ++++----- .../explore/projects/_dropdown.html.haml | 8 +++++++ 4 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 app/controllers/concerns/filter_projects.rb diff --git a/app/controllers/concerns/filter_projects.rb b/app/controllers/concerns/filter_projects.rb new file mode 100644 index 00000000000..f63b703d101 --- /dev/null +++ b/app/controllers/concerns/filter_projects.rb @@ -0,0 +1,15 @@ +# == FilterProjects +# +# Controller concern to handle projects filtering +# * by name +# * by archived state +# +module FilterProjects + extend ActiveSupport::Concern + + def filter_projects(projects) + projects = projects.search(params[:filter_projects]) if params[:filter_projects].present? + projects = projects.non_archived if params[:archived].blank? + projects + end +end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index dc880b634e5..fc51c3241af 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -1,18 +1,15 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController + include FilterProjects + before_action :event_filter def index - @projects = current_user.authorized_projects.sorted_by_activity.non_archived - @projects = @projects.sort(@sort = params[:sort]) + @projects = current_user.authorized_projects.sorted_by_activity + @projects = filter_projects(@projects) @projects = @projects.includes(:namespace) + @projects = @projects.sort(@sort = params[:sort]) + @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? - terms = params[:filter_projects] - - if terms.present? - @projects = @projects.search(terms) - end - - @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank? @last_push = current_user.recent_push respond_to do |format| @@ -32,16 +29,11 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController def starred @projects = current_user.starred_projects.sorted_by_activity + @projects = filter_projects(@projects) @projects = @projects.includes(:namespace, :forked_from_project, :tags) @projects = @projects.sort(@sort = params[:sort]) + @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? - terms = params[:filter_projects] - - if terms.present? - @projects = @projects.search(terms) - end - - @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank? @last_push = current_user.recent_push @groups = [] diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index a384f3004db..5b811db3068 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -1,12 +1,12 @@ class Explore::ProjectsController < Explore::ApplicationController + include FilterProjects + def index @projects = ProjectsFinder.new.execute(current_user) @tags = @projects.tags_on(:tags) @projects = @projects.tagged_with(params[:tag]) if params[:tag].present? @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? - @projects = @projects.non_archived - @projects = @projects.search(params[:search]) if params[:search].present? - @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present? + @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? @@ -22,8 +22,7 @@ class Explore::ProjectsController < Explore::ApplicationController def trending @projects = TrendingProjectsFinder.new.execute(current_user) - @projects = @projects.non_archived - @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present? + @projects = filter_projects(@projects) @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? respond_to do |format| @@ -38,7 +37,7 @@ class Explore::ProjectsController < Explore::ApplicationController def starred @projects = ProjectsFinder.new.execute(current_user) - @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present? + @projects = filter_projects(@projects) @projects = @projects.reorder('star_count DESC') @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? diff --git a/app/views/explore/projects/_dropdown.html.haml b/app/views/explore/projects/_dropdown.html.haml index a4b4cd8d6c7..8859cf1eb15 100644 --- a/app/views/explore/projects/_dropdown.html.haml +++ b/app/views/explore/projects/_dropdown.html.haml @@ -18,3 +18,11 @@ = sort_title_recently_updated = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do = sort_title_oldest_updated + %li.divider + %li + - if params[:archived].present? + = link_to explore_projects_filter_path(sort: @sort, archived: nil) do + Hide archived projects + - else + = link_to explore_projects_filter_path(sort: @sort, archived: true) do + Show archived projects From bebf34aa47235899b54c54995a8b6a13534f2eab Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Mar 2016 16:42:37 +0100 Subject: [PATCH 032/112] Make projects sort dropdown shared so it can be reused for other pages Signed-off-by: Dmitriy Zaporozhets --- app/helpers/explore_helper.rb | 12 ++---------- app/views/dashboard/_projects_head.html.haml | 2 +- app/views/explore/projects/_filter.html.haml | 10 +++++----- .../projects/_dropdown.html.haml | 14 +++++++------- 4 files changed, 15 insertions(+), 23 deletions(-) rename app/views/{explore => shared}/projects/_dropdown.html.haml (51%) diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb index 3648757428b..337b0aacbb5 100644 --- a/app/helpers/explore_helper.rb +++ b/app/helpers/explore_helper.rb @@ -1,5 +1,5 @@ module ExploreHelper - def explore_projects_filter_path(options={}) + def filter_projects_path(options={}) exist_opts = { sort: params[:sort], scope: params[:scope], @@ -9,15 +9,7 @@ module ExploreHelper } options = exist_opts.merge(options) - - path = if explore_controller? - explore_projects_path - elsif current_action?(:starred) - starred_dashboard_projects_path - else - dashboard_projects_path - end - + path = request.path path << "?#{options.to_param}" path end diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 40f88261c10..9da3fcbd986 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -15,7 +15,7 @@ .nav-controls = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2" - = render 'explore/projects/dropdown' + = render 'shared/projects/dropdown' - if current_user.can_create_project? = link_to new_project_path, class: 'btn btn-new' do = icon('plus') diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index 39e3e8e2738..cd485da5104 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -10,11 +10,11 @@ %b.caret %ul.dropdown-menu %li - = link_to explore_projects_filter_path(visibility_level: nil) do + = link_to filter_projects_path(visibility_level: nil) do Any - Gitlab::VisibilityLevel.values.each do |level| %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' } - = link_to explore_projects_filter_path(visibility_level: level) do + = link_to filter_projects_path(visibility_level: level) do = visibility_level_icon(level) = visibility_level_label(level) @@ -30,11 +30,11 @@ %b.caret %ul.dropdown-menu %li - = link_to explore_projects_filter_path(tag: nil) do + = link_to filter_projects_path(tag: nil) do Any - @tags.each do |tag| %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' } - = link_to explore_projects_filter_path(tag: tag.name) do - %i.fa.fa-tag + = link_to filter_projects_path(tag: tag.name) do + = icon('tag') = tag.name diff --git a/app/views/explore/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml similarity index 51% rename from app/views/explore/projects/_dropdown.html.haml rename to app/views/shared/projects/_dropdown.html.haml index 8859cf1eb15..3d721c40e24 100644 --- a/app/views/explore/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -8,21 +8,21 @@ %b.caret %ul.dropdown-menu.dropdown-menu-align-right %li - = link_to explore_projects_filter_path(sort: sort_value_name) do + = link_to filter_projects_path(sort: sort_value_name) do = sort_title_name - = link_to explore_projects_filter_path(sort: sort_value_recently_created) do + = link_to filter_projects_path(sort: sort_value_recently_created) do = sort_title_recently_created - = link_to explore_projects_filter_path(sort: sort_value_oldest_created) do + = link_to filter_projects_path(sort: sort_value_oldest_created) do = sort_title_oldest_created - = link_to explore_projects_filter_path(sort: sort_value_recently_updated) do + = link_to filter_projects_path(sort: sort_value_recently_updated) do = sort_title_recently_updated - = link_to explore_projects_filter_path(sort: sort_value_oldest_updated) do + = link_to filter_projects_path(sort: sort_value_oldest_updated) do = sort_title_oldest_updated %li.divider %li - if params[:archived].present? - = link_to explore_projects_filter_path(sort: @sort, archived: nil) do + = link_to filter_projects_path(sort: @sort, archived: nil) do Hide archived projects - else - = link_to explore_projects_filter_path(sort: @sort, archived: true) do + = link_to filter_projects_path(sort: @sort, archived: true) do Show archived projects From 2c431f89b1d172bdff326c93d58732a0d0a3f3bc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Mar 2016 16:42:58 +0100 Subject: [PATCH 033/112] Add projects list sort dropdown to group page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups_controller.rb | 6 ++++-- app/views/groups/_projects.html.haml | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index ca5ce1e2046..f05c29e9974 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,4 +1,5 @@ class GroupsController < Groups::ApplicationController + include FilterProjects include IssuesAction include MergeRequestsAction @@ -41,7 +42,8 @@ class GroupsController < Groups::ApplicationController def show @last_push = current_user.recent_push if current_user @projects = @projects.includes(:namespace) - @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present? + @projects = filter_projects(@projects) + @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? respond_to do |format| @@ -98,7 +100,7 @@ class GroupsController < Groups::ApplicationController end def load_projects - @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity.non_archived + @projects ||= ProjectsFinder.new.execute(current_user, group: group).sorted_by_activity end # Dont allow unauthorized access to group diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 794aa57b55a..7cd8e9bea46 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -3,9 +3,10 @@ = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| - if @projects.present? = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false - - if can? current_user, :create_projects, @group - = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do - = icon('plus') - New Project + = render 'shared/projects/dropdown' + - if can? current_user, :create_projects, @group + = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do + = icon('plus') + New Project = render 'shared/projects/list', projects: @projects, stars: false, skip_namespace: true From ee0f5bb511802c6aefe725eed67b366601c9aad8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Mar 2016 16:56:56 +0100 Subject: [PATCH 034/112] Add test for archive toggle feature Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + features/dashboard/archived_projects.feature | 5 +++++ features/steps/dashboard/archived_projects.rb | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 8692f61fe36..0803a6a02b0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.6.0 (unreleased) - Allow search for logged out users - Don't show Issues/MRs from archived projects in Groups view - Increase the notes polling timeout over time (Roberto Dip) + - Add ability to show archived projects on dashboard, explore and group pages v 8.5.4 - Do not cache requests for badges (including builds badge) diff --git a/features/dashboard/archived_projects.feature b/features/dashboard/archived_projects.feature index 69b3a776441..bed9282f1c6 100644 --- a/features/dashboard/archived_projects.feature +++ b/features/dashboard/archived_projects.feature @@ -10,3 +10,8 @@ Feature: Dashboard Archived Projects Scenario: I should see non-archived projects on dashboard Then I should see "Shop" project link And I should not see "Forum" project link + + Scenario: I toggle show of archived projects on dashboard + When I click "Show archived projects" link + Then I should see "Shop" project link + And I should see "Forum" project link diff --git a/features/steps/dashboard/archived_projects.rb b/features/steps/dashboard/archived_projects.rb index 36e092f50c6..6510f8d9b32 100644 --- a/features/steps/dashboard/archived_projects.rb +++ b/features/steps/dashboard/archived_projects.rb @@ -19,4 +19,8 @@ class Spinach::Features::DashboardArchivedProjects < Spinach::FeatureSteps step 'I should see "Forum" project link' do expect(page).to have_link "Forum" end + + step 'I click "Show archived projects" link' do + click_link "Show archived projects" + end end From 38984a59ac6c540b1cb97518d48386f5fc42490c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Mar 2016 17:53:13 +0100 Subject: [PATCH 035/112] Show active sorting method for projects list Signed-off-by: Dmitriy Zaporozhets --- app/helpers/sorting_helper.rb | 10 +++++ app/views/shared/projects/_dropdown.html.haml | 37 ++++++++----------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index f9026b887da..2f2d2721d6d 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -16,6 +16,16 @@ module SortingHelper } end + def projects_sort_options_hash + { + sort_value_name => sort_title_name, + sort_value_recently_updated => sort_title_recently_updated, + sort_value_oldest_updated => sort_title_oldest_updated, + sort_value_recently_created => sort_title_recently_created, + sort_value_oldest_created => sort_title_oldest_created, + } + end + def sort_title_oldest_updated 'Oldest updated' end diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index 3d721c40e24..89569906dba 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -1,28 +1,23 @@ +- @sort ||= sort_value_recently_updated +- archived = params[:archived] .dropdown.inline %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} %span.light - - if @sort.present? - = sort_options_hash[@sort] - - else - = sort_title_recently_updated + = projects_sort_options_hash[@sort] %b.caret %ul.dropdown-menu.dropdown-menu-align-right - %li - = link_to filter_projects_path(sort: sort_value_name) do - = sort_title_name - = link_to filter_projects_path(sort: sort_value_recently_created) do - = sort_title_recently_created - = link_to filter_projects_path(sort: sort_value_oldest_created) do - = sort_title_oldest_created - = link_to filter_projects_path(sort: sort_value_recently_updated) do - = sort_title_recently_updated - = link_to filter_projects_path(sort: sort_value_oldest_updated) do - = sort_title_oldest_updated + - projects_sort_options_hash.each do |value, title| + %li + = link_to filter_projects_path(sort: value, archived: archived) do + = icon('check') if @sort == value + = title + %li.divider %li - - if params[:archived].present? - = link_to filter_projects_path(sort: @sort, archived: nil) do - Hide archived projects - - else - = link_to filter_projects_path(sort: @sort, archived: true) do - Show archived projects + = link_to filter_projects_path(sort: @sort, archived: nil) do + = icon('check') unless params[:archived].present? + Hide archived projects + %li + = link_to filter_projects_path(sort: @sort, archived: true) do + = icon('check') if params[:archived].present? + Show archived projects From 619321dc8d02aac16576d8407238acef0069f3ba Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Mar 2016 19:24:47 +0100 Subject: [PATCH 036/112] Fix test Signed-off-by: Dmitriy Zaporozhets --- features/explore/projects.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/explore/projects.feature b/features/explore/projects.feature index 7df6b6f09ba..092e18d1b86 100644 --- a/features/explore/projects.feature +++ b/features/explore/projects.feature @@ -140,4 +140,4 @@ Feature: Explore Projects When I visit the explore starred projects Then I should see project "Community" And I should see project "Internal" - And I should see project "Archive" + And I should not see project "Archive" From 62067c5b0f9bc7764a88b9bc618d21cae4f90875 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Mar 2016 20:12:00 +0100 Subject: [PATCH 037/112] Align projects sort checkbox Signed-off-by: Dmitriy Zaporozhets --- .../stylesheets/framework/dropdowns.scss | 19 +++++++++++++++++++ app/views/shared/projects/_dropdown.html.haml | 4 +++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index d4878b333f9..d2c7cdc3e92 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -100,3 +100,22 @@ font-size: 13px; line-height: 22px; } + +.dropdown-with-checkmark { + li > .legend { + font-size: 13px; + color: $secondary-text; + margin-bottom: 5px; + } + + li > a { + padding-left: 20px; + line-height: 28px; + + i { + position: absolute; + left: 1px; + line-height: 28px; + } + } +} diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index 89569906dba..daaac092dce 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -5,7 +5,9 @@ %span.light = projects_sort_options_hash[@sort] %b.caret - %ul.dropdown-menu.dropdown-menu-align-right + %ul.dropdown-menu.dropdown-menu-align-right.dropdown-with-checkmark + %li + .legend Sort by - projects_sort_options_hash.each do |value, title| %li = link_to filter_projects_path(sort: value, archived: archived) do From a056dfa9a077def4c3ffb958d3f86f7c9d7c2096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Mon, 22 Feb 2016 19:08:00 -0500 Subject: [PATCH 038/112] Refactor GlobalMilestone queries. Make methods return ActiveRecord Relations instead of Arrays. --- app/models/concerns/issuable.rb | 1 + app/models/global_milestone.rb | 26 +++++++++---------- app/models/merge_request.rb | 1 - app/models/project.rb | 1 + .../dashboard/milestones/_milestone.html.haml | 2 +- app/views/dashboard/milestones/show.html.haml | 2 +- .../groups/milestones/_milestone.html.haml | 2 +- app/views/groups/milestones/show.html.haml | 2 +- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 286d6655861..a3c4a3d2776 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -29,6 +29,7 @@ module Issuable scope :assigned, -> { where("assignee_id IS NOT NULL") } scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } + scope :of_milestones, ->(ids) { where(milestone_id: ids) } scope :opened, -> { with_state(:opened, :reopened) } scope :only_opened, -> { with_state(:opened) } scope :only_reopened, -> { with_state(:reopened) } diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 7ee276255a0..e4dd90b631e 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -28,27 +28,27 @@ class GlobalMilestone end def projects - milestones.map { |milestone| milestone.project } + @projects ||= Project.for_milestones(milestones.map(&:id)) end - def issue_count - milestones.map { |milestone| milestone.issues.count }.sum + def issues_count + issues.count end def merge_requests_count - milestones.map { |milestone| milestone.merge_requests.count }.sum + merge_requests.count end def open_items_count - milestones.map { |milestone| milestone.open_items_count }.sum + opened_issues.count + opened_merge_requests.count end def closed_items_count - milestones.map { |milestone| milestone.closed_items_count }.sum + closed_issues.count + closed_merge_requests.count end def total_items_count - milestones.map { |milestone| milestone.total_items_count }.sum + issues_count + merge_requests_count end def percent_complete @@ -76,11 +76,11 @@ class GlobalMilestone end def issues - @issues ||= milestones.map(&:issues).flatten.group_by(&:state) + @issues ||= Issue.of_milestones(milestones.map(&:id)) end def merge_requests - @merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state) + @merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)) end def participants @@ -88,19 +88,19 @@ class GlobalMilestone end def opened_issues - issues.values_at("opened", "reopened").compact.flatten + issues.opened end def closed_issues - issues['closed'] + issues.closed end def opened_merge_requests - merge_requests.values_at("opened", "reopened").compact.flatten + merge_requests.opened end def closed_merge_requests - merge_requests.values_at("closed", "merged", "locked").compact.flatten + merge_requests.with_states(:closed, :merged, :locked) end def complete? diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 025b522cf66..f575494e2bf 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -139,7 +139,6 @@ class MergeRequest < ActiveRecord::Base scope :of_projects, ->(ids) { where(target_project_id: ids) } scope :opened, -> { with_states(:opened, :reopened) } scope :merged, -> { with_state(:merged) } - scope :closed, -> { with_state(:closed) } scope :closed_and_merged, -> { with_states(:closed, :merged) } scope :join_project, -> { joins(:target_project) } diff --git a/app/models/project.rb b/app/models/project.rb index 148eab692ff..3a28d5d7fee 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -215,6 +215,7 @@ class Project < ActiveRecord::Base scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } scope :non_archived, -> { where(archived: false) } + scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids) } state_machine :import_status, initial: :none do event :import_start do diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml index 7c882a32702..ea6c304d7de 100644 --- a/app/views/dashboard/milestones/_milestone.html.haml +++ b/app/views/dashboard/milestones/_milestone.html.haml @@ -8,7 +8,7 @@ .row .col-sm-6 = link_to issues_dashboard_path(milestone_title: milestone.title) do - = pluralize milestone.issue_count, 'Issue' + = pluralize milestone.issues_count, 'Issue' · = link_to merge_requests_dashboard_path(milestone_title: milestone.title) do = pluralize milestone.merge_requests_count, 'Merge Request' diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 3810267577c..bbe0b0905ce 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -52,7 +52,7 @@ %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues - %span.badge= @milestone.issue_count + %span.badge= @milestone.issues_count %li = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do Merge Requests diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml index a20bf75bc39..50558d7dce8 100644 --- a/app/views/groups/milestones/_milestone.html.haml +++ b/app/views/groups/milestones/_milestone.html.haml @@ -8,7 +8,7 @@ .row .col-sm-6 = link_to issues_group_path(@group, milestone_title: milestone.title) do - = pluralize milestone.issue_count, 'Issue' + = pluralize milestone.issues_count, 'Issue' · = link_to merge_requests_group_path(@group, milestone_title: milestone.title) do = pluralize milestone.merge_requests_count, 'Merge Request' diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 1233da85524..405df1d3433 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -58,7 +58,7 @@ %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues - %span.badge= @milestone.issue_count + %span.badge= @milestone.issues_count %li = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do Merge Requests From 70028d36bee51a9ed86d20fe5d6c895772cc476a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Mon, 22 Feb 2016 20:39:36 -0500 Subject: [PATCH 039/112] Recator Issues Tab into a custom partial. --- app/views/groups/milestones/show.html.haml | 15 ++------------- app/views/projects/milestones/show.html.haml | 9 +-------- .../milestones/_issue.html.haml | 5 +++-- .../milestones/_issues.html.haml | 2 +- app/views/shared/milestones/_issues_tab.html.haml | 7 +++++++ 5 files changed, 14 insertions(+), 24 deletions(-) rename app/views/{projects => shared}/milestones/_issue.html.haml (63%) rename app/views/{projects => shared}/milestones/_issues.html.haml (80%) create mode 100644 app/views/shared/milestones/_issues_tab.html.haml diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 405df1d3433..0114e68b19d 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -68,20 +68,9 @@ Participants %span.badge= @milestone.participants.count -.tab-content +.tab-content.milestone-content .tab-pane.active#tab-issues - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All issues in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'issues', title: "Open", issues: @milestone.opened_issues - .col-md-6 - = render 'issues', title: "Closed", issues: @milestone.closed_issues + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests .gray-content-block.middle-block diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 2cae1ac4e2c..2322946894e 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -94,14 +94,7 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - .row.prepend-top-default - .col-md-4 - = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned') - .col-md-4 - = render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned, id: 'ongoing') - .col-md-4 - = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed') - + = render 'shared/milestones/issues_tab', unassigned: @issues.opened.unassigned, assigned: @issues.opened.assigned, closed: @issues.closed .tab-pane#tab-merge-requests .row.prepend-top-default .col-md-3 diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml similarity index 63% rename from app/views/projects/milestones/_issue.html.haml rename to app/views/shared/milestones/_issue.html.haml index ca51b8c745d..07880404c9a 100644 --- a/app/views/projects/milestones/_issue.html.haml +++ b/app/views/shared/milestones/_issue.html.haml @@ -1,8 +1,9 @@ +- project = issue.project %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %span - = link_to_gfm issue.title, [@project.namespace.becomes(Namespace), @project, issue], title: issue.title + = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title .issue-detail - = link_to [@project.namespace.becomes(Namespace), @project, issue] do + = link_to [project.namespace.becomes(Namespace), project, issue] do %span.issue-number ##{issue.iid} - issue.labels.each do |label| = render_colored_label(label) diff --git a/app/views/projects/milestones/_issues.html.haml b/app/views/shared/milestones/_issues.html.haml similarity index 80% rename from app/views/projects/milestones/_issues.html.haml rename to app/views/shared/milestones/_issues.html.haml index 6f8a341e478..e1b5c2eb66c 100644 --- a/app/views/projects/milestones/_issues.html.haml +++ b/app/views/shared/milestones/_issues.html.haml @@ -4,4 +4,4 @@ .pull-right= issues.size %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id } - issues.sort_by(&:position).each do |issue| - = render 'issue', issue: issue + = render 'shared/milestones/issue', issue: issue diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml new file mode 100644 index 00000000000..3e98a809ad5 --- /dev/null +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -0,0 +1,7 @@ +.row.prepend-top-default + .col-md-4 + = render('shared/milestones/issues', title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned') + .col-md-4 + = render('shared/milestones/issues', title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing') + .col-md-4 + = render('shared/milestones/issues', title: 'Completed Issues (closed)', issues: closed, id: 'closed') From 37d92d0b7b91c678ce71393e73aa94860051fd5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 11:53:59 -0500 Subject: [PATCH 040/112] Refactor Merge Requests tab into a custom partial --- app/views/groups/milestones/show.html.haml | 13 +------------ app/views/projects/milestones/show.html.haml | 15 +-------------- .../milestones/_merge_request.html.haml | 6 ++++-- .../milestones/_merge_requests.html.haml | 2 +- .../shared/milestones/_merge_requests_tab.haml | 13 +++++++++++++ 5 files changed, 20 insertions(+), 29 deletions(-) rename app/views/{projects => shared}/milestones/_merge_request.html.haml (58%) rename app/views/{projects => shared}/milestones/_merge_requests.html.haml (73%) create mode 100644 app/views/shared/milestones/_merge_requests_tab.haml diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 0114e68b19d..d9899fbd43d 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -73,18 +73,7 @@ = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Merge Requests', merge_requests_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All merge requests in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests - .col-md-6 - = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.closed_merge_requests, merged: @milestone.merge_requests.merged .tab-pane#tab-participants .gray-content-block.middle-block diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 2322946894e..02f330cce23 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -96,20 +96,7 @@ .tab-pane.active#tab-issues = render 'shared/milestones/issues_tab', unassigned: @issues.opened.unassigned, assigned: @issues.opened.assigned, closed: @issues.closed .tab-pane#tab-merge-requests - .row.prepend-top-default - .col-md-3 - = render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned') - .col-md-3 - = render('merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: @merge_requests.opened.assigned, id: 'ongoing') - .col-md-3 - = render('merge_requests', title: 'Rejected (closed)', merge_requests: @merge_requests.closed, id: 'closed') - .col-md-3 - .panel.panel-primary - .panel-heading Merged - %ul.well-list - - @merge_requests.merged.each do |merge_request| - = render 'merge_request', merge_request: merge_request - + = render 'shared/milestones/merge_requests_tab', unassigned: @merge_requests.opened.unassigned, assigned: @merge_requests.opened.assigned, closed: @merge_requests.closed, merged: @merge_requests.merged .tab-pane#tab-participants %ul.bordered-list - @users.each do |user| diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml similarity index 58% rename from app/views/projects/milestones/_merge_request.html.haml rename to app/views/shared/milestones/_merge_request.html.haml index a1033607c5d..a58dadb4f84 100644 --- a/app/views/projects/milestones/_merge_request.html.haml +++ b/app/views/shared/milestones/_merge_request.html.haml @@ -1,8 +1,10 @@ +- project = @project || merge_request.project + %li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } %span.str-truncated - = link_to [@project.namespace.becomes(Namespace), @project, merge_request] do + = link_to [project.namespace.becomes(Namespace), project, merge_request] do %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [@project.namespace.becomes(Namespace), @project, merge_request], title: merge_request.title + = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title .pull-right.assignee-icon - if merge_request.assignee = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/projects/milestones/_merge_requests.html.haml b/app/views/shared/milestones/_merge_requests.html.haml similarity index 73% rename from app/views/projects/milestones/_merge_requests.html.haml rename to app/views/shared/milestones/_merge_requests.html.haml index 9a5a02af215..c8df6c2e280 100644 --- a/app/views/projects/milestones/_merge_requests.html.haml +++ b/app/views/shared/milestones/_merge_requests.html.haml @@ -2,4 +2,4 @@ .panel-heading= title %ul{ class: "well-list merge_requests-sortable-list", id: "merge_requests-list-#{id}", "data-state" => id } - merge_requests.sort_by(&:position).each do |merge_request| - = render 'merge_request', merge_request: merge_request + = render 'shared/milestones/merge_request', merge_request: merge_request diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml new file mode 100644 index 00000000000..5797aeb8295 --- /dev/null +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -0,0 +1,13 @@ +.row.prepend-top-default + .col-md-3 + = render('shared/milestones/merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned') + .col-md-3 + = render('shared/milestones/merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing') + .col-md-3 + = render('shared/milestones/merge_requests', title: 'Rejected (closed)', merge_requests: closed, id: 'closed') + .col-md-3 + .panel.panel-primary + .panel-heading Merged + %ul.well-list + - merged.each do |merge_request| + = render 'shared/milestones/merge_request', merge_request: merge_request From e805becfe838b6ac351a15a01d58e355a0b1763b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 12:15:19 -0500 Subject: [PATCH 041/112] Eager load Issues/MRs project for Milestone. With this change we avoid doing N+1 queries when viewing Milestone's Issues/MRs from a Group context. --- app/models/global_milestone.rb | 4 ++-- app/views/groups/milestones/show.html.haml | 2 +- app/views/shared/milestones/_issue.html.haml | 4 +++- app/views/shared/milestones/_merge_request.html.haml | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index e4dd90b631e..40193a6f050 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -76,11 +76,11 @@ class GlobalMilestone end def issues - @issues ||= Issue.of_milestones(milestones.map(&:id)) + @issues ||= Issue.of_milestones(milestones.map(&:id)).includes(:project) end def merge_requests - @merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)) + @merge_requests ||= MergeRequest.of_milestones(milestones.map(&:id)).includes(:target_project) end def participants diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index d9899fbd43d..c6016179ba6 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -73,7 +73,7 @@ = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.closed_merge_requests, merged: @milestone.merge_requests.merged + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged .tab-pane#tab-participants .gray-content-block.middle-block diff --git a/app/views/shared/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml index 07880404c9a..be55c5b6fe0 100644 --- a/app/views/shared/milestones/_issue.html.haml +++ b/app/views/shared/milestones/_issue.html.haml @@ -1,4 +1,6 @@ -- project = issue.project +-# @project is present when viewing Project's milestone +- project = @project || issue.project + %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %span = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title diff --git a/app/views/shared/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml index a58dadb4f84..66e2a2955da 100644 --- a/app/views/shared/milestones/_merge_request.html.haml +++ b/app/views/shared/milestones/_merge_request.html.haml @@ -1,3 +1,4 @@ +-# @project is present when viewing Project's milestone - project = @project || merge_request.project %li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } From 834b5d49ecb486065e3ecbf8b36becb416eff366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 12:45:16 -0500 Subject: [PATCH 042/112] Refactor Merge Requests tab into a custom partial --- app/views/groups/milestones/show.html.haml | 13 +------------ app/views/projects/milestones/show.html.haml | 10 +--------- .../shared/milestones/_participants_tab.html.haml | 8 ++++++++ 3 files changed, 10 insertions(+), 21 deletions(-) create mode 100644 app/views/shared/milestones/_participants_tab.html.haml diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index c6016179ba6..0a15f49ff12 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -76,15 +76,4 @@ = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged .tab-pane#tab-participants - .gray-content-block.middle-block - .oneline - All participants to this milestone - - %ul.bordered-list - - @milestone.participants.each do |user| - %li - = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user, 32), class: "avatar s32" - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username + = render 'shared/milestones/participants_tab', users: @milestone.participants diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 02f330cce23..6ca66885eb4 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -98,15 +98,7 @@ .tab-pane#tab-merge-requests = render 'shared/milestones/merge_requests_tab', unassigned: @merge_requests.opened.unassigned, assigned: @merge_requests.opened.assigned, closed: @merge_requests.closed, merged: @merge_requests.merged .tab-pane#tab-participants - %ul.bordered-list - - @users.each do |user| - %li - = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user, 32), class: "avatar s32" - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username - + = render 'shared/milestones/participants_tab', users: @users .tab-pane#tab-labels %ul.bordered-list.manage-labels-list - @labels.each do |label| diff --git a/app/views/shared/milestones/_participants_tab.html.haml b/app/views/shared/milestones/_participants_tab.html.haml new file mode 100644 index 00000000000..67ae85ac276 --- /dev/null +++ b/app/views/shared/milestones/_participants_tab.html.haml @@ -0,0 +1,8 @@ +%ul.bordered-list + - users.each do |user| + %li + = link_to user, title: user.name, class: "darken" do + = image_tag avatar_icon(user, 32), class: "avatar s32" + %strong= truncate(user.name, lenght: 40) + %br + %small.cgray= user.username From ed4808555877c668366d98a5408937712ad10d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 16:22:36 -0500 Subject: [PATCH 043/112] Refactor Merge Requests tab into a custom partial * Add Labels tab to Groups * Add decorator for label so it's aware of Milestones. --- app/decorators/label_with_milestone.rb | 19 +++++++++++++++++++ app/models/global_milestone.rb | 6 ++++++ app/views/groups/milestones/show.html.haml | 8 ++++++-- app/views/projects/milestones/show.html.haml | 14 +------------- .../shared/milestones/_labels_tab.html.haml | 15 +++++++++++++++ config/application.rb | 1 + 6 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 app/decorators/label_with_milestone.rb create mode 100644 app/views/shared/milestones/_labels_tab.html.haml diff --git a/app/decorators/label_with_milestone.rb b/app/decorators/label_with_milestone.rb new file mode 100644 index 00000000000..a70a4e2f50d --- /dev/null +++ b/app/decorators/label_with_milestone.rb @@ -0,0 +1,19 @@ +class LabelWithMilestone + attr_reader :milestone + + def initialize(label, milestone) + @label, @milestone = label, milestone + end + + def method_missing(meth, *args) + if @label.respond_to?(meth) + @label.send(meth, *args) + else + super + end + end + + def respond_to?(meth) + @label.respond_to?(meth) + end +end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 40193a6f050..e13aaf16732 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -87,6 +87,12 @@ class GlobalMilestone @participants ||= milestones.map(&:participants).flatten.compact.uniq end + def labels + @labels ||= milestones.map do |ms| + ms.labels.map { |label| LabelWithMilestone.new(label, ms) } + end.flatten.sort_by!(&:title) + end + def opened_issues issues.opened end diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 0a15f49ff12..7ffa3f2d518 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -67,13 +67,17 @@ = link_to '#tab-participants', 'data-toggle' => 'tab' do Participants %span.badge= @milestone.participants.count + %li + = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do + Labels + %span.badge= @milestone.labels.count .tab-content.milestone-content .tab-pane.active#tab-issues = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues - .tab-pane#tab-merge-requests = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged - .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @milestone.participants + .tab-pane#tab-labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 6ca66885eb4..4aa1a53e87e 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -100,16 +100,4 @@ .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @users .tab-pane#tab-labels - %ul.bordered-list.manage-labels-list - - @labels.each do |label| - %li - = render_colored_label(label) - - args = [@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title, label_name: label.title] - - options = args.extract_options! - - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do - = pluralize label.open_issues_count, 'open issue' - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'closed')) do - = pluralize label.closed_issues_count, 'closed issue' + = render 'shared/milestones/labels_tab', labels: @labels diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml new file mode 100644 index 00000000000..bb6ce0afb40 --- /dev/null +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -0,0 +1,15 @@ +%ul.bordered-list.manage-labels-list + - labels.each do |label| + - milestone = @milestone.is_a?(Milestone) ? @milestone : label.milestone + + %li + = render_colored_label(label) + - args = [milestone.project.namespace, milestone.project, milestone_title: milestone.title, label_name: label.title] + - options = args.extract_options! + + %span.issues-count + = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do + = pluralize label.open_issues_count, 'open issue' + %span.issues-count + = link_to namespace_project_issues_path(*args, options.merge(state: 'closed')) do + = pluralize label.closed_issues_count, 'closed issue' diff --git a/config/application.rb b/config/application.rb index 28684a3e578..fee8637a4cb 100644 --- a/config/application.rb +++ b/config/application.rb @@ -15,6 +15,7 @@ module Gitlab # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths.push(*%W(#{config.root}/lib + #{config.root}/app/decorators #{config.root}/app/models/hooks #{config.root}/app/models/concerns #{config.root}/app/models/project_services From 32f8fc1f9a493dca41f5b67a15bb7ff37e5d78c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 16:35:36 -0500 Subject: [PATCH 044/112] Refactor Milestone view for Dashboard. --- .../dashboard/milestones/_issue.html.haml | 10 ---- .../dashboard/milestones/_issues.html.haml | 6 --- .../milestones/_merge_request.html.haml | 10 ---- .../milestones/_merge_requests.html.haml | 6 --- app/views/dashboard/milestones/show.html.haml | 48 ++++--------------- 5 files changed, 10 insertions(+), 70 deletions(-) delete mode 100644 app/views/dashboard/milestones/_issue.html.haml delete mode 100644 app/views/dashboard/milestones/_issues.html.haml delete mode 100644 app/views/dashboard/milestones/_merge_request.html.haml delete mode 100644 app/views/dashboard/milestones/_merge_requests.html.haml diff --git a/app/views/dashboard/milestones/_issue.html.haml b/app/views/dashboard/milestones/_issue.html.haml deleted file mode 100644 index 1408ebdd5dc..00000000000 --- a/app/views/dashboard/milestones/_issue.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid } - %span.milestone-row - - project = issue.project - %strong #{project.name_with_namespace} · - = link_to [project.namespace.becomes(Namespace), project, issue] do - %span.cgray ##{issue.iid} - = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title - .pull-right.assignee-icon - - if issue.assignee - = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/_issues.html.haml b/app/views/dashboard/milestones/_issues.html.haml deleted file mode 100644 index 9f350b772bd..00000000000 --- a/app/views/dashboard/milestones/_issues.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list issues-sortable-list" } - - if issues - - issues.each do |issue| - = render 'issue', issue: issue diff --git a/app/views/dashboard/milestones/_merge_request.html.haml b/app/views/dashboard/milestones/_merge_request.html.haml deleted file mode 100644 index 77c46de030b..00000000000 --- a/app/views/dashboard/milestones/_merge_request.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid } - %span.milestone-row - - project = merge_request.project - %strong #{project.name_with_namespace} · - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .pull-right.assignee-icon - - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16" diff --git a/app/views/dashboard/milestones/_merge_requests.html.haml b/app/views/dashboard/milestones/_merge_requests.html.haml deleted file mode 100644 index 50057e2c636..00000000000 --- a/app/views/dashboard/milestones/_merge_requests.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list merge_requests-sortable-list" } - - if merge_requests - - merge_requests.each do |merge_request| - = render 'merge_request', merge_request: merge_request diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index bbe0b0905ce..3e9e1204408 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -61,45 +61,17 @@ = link_to '#tab-participants', 'data-toggle' => 'tab' do Participants %span.badge= @milestone.participants.count + %li + = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do + Labels + %span.badge= @milestone.labels.count -.tab-content +.tab-content.milestone-content .tab-pane.active#tab-issues - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All issues in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'issues', title: "Open", issues: @milestone.opened_issues - .col-md-6 - = render 'issues', title: "Closed", issues: @milestone.closed_issues - + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues .tab-pane#tab-merge-requests - .gray-content-block.middle-block - .pull-right - = link_to 'Browse Merge Requests', merge_requests_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" - - .oneline - All merge requests in this milestone - - .row.prepend-top-default - .col-md-6 - = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests - .col-md-6 - = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests - + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged .tab-pane#tab-participants - .gray-content-block.middle-block - .oneline - All participants to this milestone - %ul.bordered-list - - @milestone.participants.each do |user| - %li - = link_to user, title: user.name, class: "darken" do - = image_tag avatar_icon(user, 32), class: "avatar s32" - %strong= truncate(user.name, lenght: 40) - %br - %small.cgray= user.username + = render 'shared/milestones/participants_tab', users: @milestone.participants + .tab-pane#tab-labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels From e0a18829ee7f1d681500002a2b6bda122b64b4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 19:31:59 -0500 Subject: [PATCH 045/112] Show project name for Issues tab in Group and Dashboard context. --- app/views/dashboard/milestones/show.html.haml | 6 +++--- app/views/groups/milestones/show.html.haml | 6 +++--- app/views/shared/milestones/_issue.html.haml | 4 ++++ app/views/shared/milestones/_issues.html.haml | 2 +- app/views/shared/milestones/_issues_tab.html.haml | 9 ++++++--- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 3e9e1204408..18cf6fcbfd3 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -68,10 +68,10 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_full_project_name: true .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_full_project_name: true .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @milestone.participants .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_full_project_name: true diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 7ffa3f2d518..021425cad4f 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -74,10 +74,10 @@ .tab-content.milestone-content .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues + = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_project_name: true .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged + = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_project_name: true .tab-pane#tab-participants = render 'shared/milestones/participants_tab', users: @milestone.participants .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels + = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_project_name: true diff --git a/app/views/shared/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml index be55c5b6fe0..dbef742ebe8 100644 --- a/app/views/shared/milestones/_issue.html.haml +++ b/app/views/shared/milestones/_issue.html.haml @@ -3,6 +3,10 @@ %li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title .issue-detail = link_to [project.namespace.becomes(Namespace), project, issue] do diff --git a/app/views/shared/milestones/_issues.html.haml b/app/views/shared/milestones/_issues.html.haml index e1b5c2eb66c..04053eb854b 100644 --- a/app/views/shared/milestones/_issues.html.haml +++ b/app/views/shared/milestones/_issues.html.haml @@ -4,4 +4,4 @@ .pull-right= issues.size %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id } - issues.sort_by(&:position).each do |issue| - = render 'shared/milestones/issue', issue: issue + = render 'shared/milestones/issue', issue: issue, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml index 3e98a809ad5..277cd81677b 100644 --- a/app/views/shared/milestones/_issues_tab.html.haml +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -1,7 +1,10 @@ +- args = { show_project_name: local_assigns.fetch(:show_project_name, false), + show_full_project_name: local_assigns.fetch(:show_full_project_name, false) } + .row.prepend-top-default .col-md-4 - = render('shared/milestones/issues', title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned') + = render 'shared/milestones/issues', args.merge({ title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned' }) .col-md-4 - = render('shared/milestones/issues', title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing') + = render 'shared/milestones/issues', args.merge({ title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing' }) .col-md-4 - = render('shared/milestones/issues', title: 'Completed Issues (closed)', issues: closed, id: 'closed') + = render 'shared/milestones/issues', args.merge({ title: 'Completed Issues (closed)', issues: closed, id: 'closed' }) From 7cc102a0e6568fdfa08e63ba3a0b31ced2c30a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 20:12:10 -0500 Subject: [PATCH 046/112] Make Merge Requests tab have the same look as Issues. --- app/assets/stylesheets/pages/milestone.scss | 8 ++++---- app/views/groups/milestones/_issue.html.haml | 10 ---------- app/views/groups/milestones/_issues.html.haml | 6 ------ .../groups/milestones/_merge_request.html.haml | 10 ---------- .../groups/milestones/_merge_requests.html.haml | 6 ------ .../shared/milestones/_merge_request.html.haml | 14 ++++++++++---- .../shared/milestones/_merge_requests.html.haml | 7 +++++-- .../shared/milestones/_merge_requests_tab.haml | 15 +++++++-------- 8 files changed, 26 insertions(+), 50 deletions(-) delete mode 100644 app/views/groups/milestones/_issue.html.haml delete mode 100644 app/views/groups/milestones/_issues.html.haml delete mode 100644 app/views/groups/milestones/_merge_request.html.haml delete mode 100644 app/views/groups/milestones/_merge_requests.html.haml diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index d24adbf67e6..2c2a8e34d50 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -19,7 +19,7 @@ li.milestone { width: 105px; } - .issue-row { + .issue-row, .mr-row { .color-label { border-radius: 2px; padding: 3px !important; @@ -44,11 +44,11 @@ li.milestone { } } -.issues-sortable-list { - .issue-detail { +.issues-sortable-list, .merge_requests-sortable-list { + .issue-detail, .merge-request-detail { display: block; - .issue-number{ + .issue-number, .merge-request-number { color: rgba(0,0,0,0.44); margin-right: 5px; } diff --git a/app/views/groups/milestones/_issue.html.haml b/app/views/groups/milestones/_issue.html.haml deleted file mode 100644 index 9b85d83d6d8..00000000000 --- a/app/views/groups/milestones/_issue.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid } - %span.milestone-row - - project = issue.project - %strong #{project.name} · - = link_to [project.namespace.becomes(Namespace), project, issue] do - %span.cgray ##{issue.iid} - = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title - .pull-right.assignee-icon - - if issue.assignee - = image_tag avatar_icon(issue.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/groups/milestones/_issues.html.haml b/app/views/groups/milestones/_issues.html.haml deleted file mode 100644 index 9f350b772bd..00000000000 --- a/app/views/groups/milestones/_issues.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list issues-sortable-list" } - - if issues - - issues.each do |issue| - = render 'issue', issue: issue diff --git a/app/views/groups/milestones/_merge_request.html.haml b/app/views/groups/milestones/_merge_request.html.haml deleted file mode 100644 index e3aa4aad198..00000000000 --- a/app/views/groups/milestones/_merge_request.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid } - %span.milestone-row - - project = merge_request.project - %strong #{project.name} · - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.cgray ##{merge_request.iid} - = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .pull-right.assignee-icon - - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/groups/milestones/_merge_requests.html.haml b/app/views/groups/milestones/_merge_requests.html.haml deleted file mode 100644 index 50057e2c636..00000000000 --- a/app/views/groups/milestones/_merge_requests.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -.panel.panel-default - .panel-heading= title - %ul{ class: "well-list merge_requests-sortable-list" } - - if merge_requests - - merge_requests.each do |merge_request| - = render 'merge_request', merge_request: merge_request diff --git a/app/views/shared/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml index 66e2a2955da..7b6c6606460 100644 --- a/app/views/shared/milestones/_merge_request.html.haml +++ b/app/views/shared/milestones/_merge_request.html.haml @@ -2,10 +2,16 @@ - project = @project || merge_request.project %li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } - %span.str-truncated - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.cgray ##{merge_request.iid} + %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .pull-right.assignee-icon + .merge-request-detail + = link_to [project.namespace.becomes(Namespace), project, merge_request] do + %span.merge-request-number ##{merge_request.iid} + - merge_request.labels.each do |label| + = render_colored_label(label) - if merge_request.assignee = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/shared/milestones/_merge_requests.html.haml b/app/views/shared/milestones/_merge_requests.html.haml index c8df6c2e280..71991516e90 100644 --- a/app/views/shared/milestones/_merge_requests.html.haml +++ b/app/views/shared/milestones/_merge_requests.html.haml @@ -1,5 +1,8 @@ -.panel.panel-default +- primary = local_assigns.fetch(:primary, false) +- panel_class = primary ? 'panel-primary' : 'panel-default' + +.panel{ class: panel_class } .panel-heading= title %ul{ class: "well-list merge_requests-sortable-list", id: "merge_requests-list-#{id}", "data-state" => id } - merge_requests.sort_by(&:position).each do |merge_request| - = render 'shared/milestones/merge_request', merge_request: merge_request + = render 'shared/milestones/merge_request', merge_request: merge_request, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml index 5797aeb8295..d912e607963 100644 --- a/app/views/shared/milestones/_merge_requests_tab.haml +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -1,13 +1,12 @@ +- args = { show_project_name: local_assigns.fetch(:show_project_name, false), + show_full_project_name: local_assigns.fetch(:show_full_project_name, false) } + .row.prepend-top-default .col-md-3 - = render('shared/milestones/merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned') + = render 'shared/milestones/merge_requests', args.merge({ title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned' }) .col-md-3 - = render('shared/milestones/merge_requests', title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing') + = render 'shared/milestones/merge_requests', args.merge({ title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing' }) .col-md-3 - = render('shared/milestones/merge_requests', title: 'Rejected (closed)', merge_requests: closed, id: 'closed') + = render 'shared/milestones/merge_requests', args.merge({ title: 'Rejected (closed)', merge_requests: closed, id: 'closed' }) .col-md-3 - .panel.panel-primary - .panel-heading Merged - %ul.well-list - - merged.each do |merge_request| - = render 'shared/milestones/merge_request', merge_request: merge_request + = render 'shared/milestones/merge_requests', args.merge({ title: 'Merged', merge_requests: merged, id: 'merged', primary: true }) From b6e5de2cfbc396ae4f59de2c2bb56432e705d695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 21:17:14 -0500 Subject: [PATCH 047/112] Use the same partial when rendering Issues or Merge Requests. --- app/assets/stylesheets/pages/milestone.scss | 6 +++--- app/views/shared/milestones/_issue.html.haml | 17 ----------------- app/views/shared/milestones/_issues.html.haml | 7 ------- .../shared/milestones/_issues_tab.html.haml | 6 +++--- .../shared/milestones/_merge_request.html.haml | 17 ----------------- .../shared/milestones/_merge_requests.html.haml | 8 -------- .../shared/milestones/_merge_requests_tab.haml | 8 ++++---- 7 files changed, 10 insertions(+), 59 deletions(-) delete mode 100644 app/views/shared/milestones/_issue.html.haml delete mode 100644 app/views/shared/milestones/_issues.html.haml delete mode 100644 app/views/shared/milestones/_merge_request.html.haml delete mode 100644 app/views/shared/milestones/_merge_requests.html.haml diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 2c2a8e34d50..300c2fa7e1b 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -19,7 +19,7 @@ li.milestone { width: 105px; } - .issue-row, .mr-row { + .issue-row, .merge_request-row { .color-label { border-radius: 2px; padding: 3px !important; @@ -45,10 +45,10 @@ li.milestone { } .issues-sortable-list, .merge_requests-sortable-list { - .issue-detail, .merge-request-detail { + .issue-detail, .merge_request-detail { display: block; - .issue-number, .merge-request-number { + .issue-number, .merge_request-number { color: rgba(0,0,0,0.44); margin-right: 5px; } diff --git a/app/views/shared/milestones/_issue.html.haml b/app/views/shared/milestones/_issue.html.haml deleted file mode 100644 index dbef742ebe8..00000000000 --- a/app/views/shared/milestones/_issue.html.haml +++ /dev/null @@ -1,17 +0,0 @@ --# @project is present when viewing Project's milestone -- project = @project || issue.project - -%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) } - %span - - if show_project_name - %strong #{project.name} · - - elsif show_full_project_name - %strong #{project.name_with_namespace} · - = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title - .issue-detail - = link_to [project.namespace.becomes(Namespace), project, issue] do - %span.issue-number ##{issue.iid} - - issue.labels.each do |label| - = render_colored_label(label) - - if issue.assignee - = image_tag avatar_icon(issue.assignee, 16), class: "avatar s24", alt: '' diff --git a/app/views/shared/milestones/_issues.html.haml b/app/views/shared/milestones/_issues.html.haml deleted file mode 100644 index 04053eb854b..00000000000 --- a/app/views/shared/milestones/_issues.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.panel.panel-default - .panel-heading - = title - .pull-right= issues.size - %ul{ class: "well-list issues-sortable-list", id: "issues-list-#{id}", "data-state" => id } - - issues.sort_by(&:position).each do |issue| - = render 'shared/milestones/issue', issue: issue, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml index 277cd81677b..44a221114bb 100644 --- a/app/views/shared/milestones/_issues_tab.html.haml +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -3,8 +3,8 @@ .row.prepend-top-default .col-md-4 - = render 'shared/milestones/issues', args.merge({ title: 'Unstarted Issues (open and unassigned)', issues: unassigned, id: 'unassigned' }) + = render 'shared/milestones/records', args.merge({ title: 'Unstarted Issues (open and unassigned)', records: unassigned, id: 'unassigned', show_counter: true }) .col-md-4 - = render 'shared/milestones/issues', args.merge({ title: 'Ongoing Issues (open and assigned)', issues: assigned, id: 'ongoing' }) + = render 'shared/milestones/records', args.merge({ title: 'Ongoing Issues (open and assigned)', records: assigned, id: 'ongoing', show_counter: true }) .col-md-4 - = render 'shared/milestones/issues', args.merge({ title: 'Completed Issues (closed)', issues: closed, id: 'closed' }) + = render 'shared/milestones/records', args.merge({ title: 'Completed Issues (closed)', records: closed, id: 'closed', show_counter: true }) diff --git a/app/views/shared/milestones/_merge_request.html.haml b/app/views/shared/milestones/_merge_request.html.haml deleted file mode 100644 index 7b6c6606460..00000000000 --- a/app/views/shared/milestones/_merge_request.html.haml +++ /dev/null @@ -1,17 +0,0 @@ --# @project is present when viewing Project's milestone -- project = @project || merge_request.project - -%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) } - %span - - if show_project_name - %strong #{project.name} · - - elsif show_full_project_name - %strong #{project.name_with_namespace} · - = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title - .merge-request-detail - = link_to [project.namespace.becomes(Namespace), project, merge_request] do - %span.merge-request-number ##{merge_request.iid} - - merge_request.labels.each do |label| - = render_colored_label(label) - - if merge_request.assignee - = image_tag avatar_icon(merge_request.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/shared/milestones/_merge_requests.html.haml b/app/views/shared/milestones/_merge_requests.html.haml deleted file mode 100644 index 71991516e90..00000000000 --- a/app/views/shared/milestones/_merge_requests.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -- primary = local_assigns.fetch(:primary, false) -- panel_class = primary ? 'panel-primary' : 'panel-default' - -.panel{ class: panel_class } - .panel-heading= title - %ul{ class: "well-list merge_requests-sortable-list", id: "merge_requests-list-#{id}", "data-state" => id } - - merge_requests.sort_by(&:position).each do |merge_request| - = render 'shared/milestones/merge_request', merge_request: merge_request, show_project_name: show_project_name, show_full_project_name: show_full_project_name diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml index d912e607963..15b2876c32d 100644 --- a/app/views/shared/milestones/_merge_requests_tab.haml +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -3,10 +3,10 @@ .row.prepend-top-default .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Work in progress (open and unassigned)', merge_requests: unassigned, id: 'unassigned' }) + = render 'shared/milestones/records', args.merge({ title: 'Work in progress (open and unassigned)', records: unassigned, id: 'unassigned' }) .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Waiting for merge (open and assigned)', merge_requests: assigned, id: 'ongoing' }) + = render 'shared/milestones/records', args.merge({ title: 'Waiting for merge (open and assigned)', records: assigned, id: 'ongoing' }) .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Rejected (closed)', merge_requests: closed, id: 'closed' }) + = render 'shared/milestones/records', args.merge({ title: 'Rejected (closed)', records: closed, id: 'closed' }) .col-md-3 - = render 'shared/milestones/merge_requests', args.merge({ title: 'Merged', merge_requests: merged, id: 'merged', primary: true }) + = render 'shared/milestones/records', args.merge({ title: 'Merged', records: merged, id: 'merged', primary: true }) From 3c157b6bf7fa97cba769ecacd11cdcdba43adf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 22:21:04 -0500 Subject: [PATCH 048/112] Show Project name on Labels tab for Group and Dashboard context. --- app/views/shared/milestones/_labels_tab.html.haml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index bb6ce0afb40..88e8e4f9006 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -1,12 +1,19 @@ +- show_project_name = local_assigns.fetch(:show_project_name, false) +- show_full_project_name = local_assigns.fetch(:show_full_project_name, false) + %ul.bordered-list.manage-labels-list - labels.each do |label| - milestone = @milestone.is_a?(Milestone) ? @milestone : label.milestone %li = render_colored_label(label) + - if show_project_name + %strong · #{milestone.project.name} + - elsif show_full_project_name + %strong · #{milestone.project.name_with_namespace} + - args = [milestone.project.namespace, milestone.project, milestone_title: milestone.title, label_name: label.title] - options = args.extract_options! - %span.issues-count = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do = pluralize label.open_issues_count, 'open issue' From 96058605c39ed2a7f6fdbdbbc3da05fdbe170681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 23 Feb 2016 23:02:32 -0500 Subject: [PATCH 049/112] Show some stats about Milestone according to the new UI. --- app/views/dashboard/milestones/show.html.haml | 18 +++++++++++++----- app/views/groups/milestones/show.html.haml | 18 +++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 18cf6fcbfd3..88edc025c62 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -41,11 +41,19 @@ = milestone.expires_at .context - %p.lead - Progress: - #{@milestone.closed_items_count} closed - – - #{@milestone.open_items_count} open + .milestone-summary + %h4 Progress + %strong= @milestone.issues.size + issues: + %span.milestone-stat + %strong= @milestone.opened_issues.size + open and + %strong= @milestone.closed_issues.size + closed + %span.milestone-stat + %strong== #{@milestone.percent_complete}% + complete + = milestone_progress_bar(@milestone) %ul.nav-links.no-top.no-bottom diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 021425cad4f..bd61e50a5bd 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -47,11 +47,19 @@ = milestone.expires_at .context - %p.lead - Progress: - #{@milestone.closed_items_count} closed - – - #{@milestone.open_items_count} open + .milestone-summary + %h4 Progress + %strong= @milestone.issues.size + issues: + %span.milestone-stat + %strong= @milestone.opened_issues.size + open and + %strong= @milestone.closed_issues.size + closed + %span.milestone-stat + %strong== #{@milestone.percent_complete}% + complete + = milestone_progress_bar(@milestone) %ul.nav-links.no-top.no-bottom From 3f2d82485ed84247d88cde6423c9df517c8962b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 24 Feb 2016 14:05:28 -0500 Subject: [PATCH 050/112] Add missing partials! --- app/views/shared/milestones/_record.html.haml | 17 +++++++++++++++++ app/views/shared/milestones/_records.html.haml | 14 ++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 app/views/shared/milestones/_record.html.haml create mode 100644 app/views/shared/milestones/_records.html.haml diff --git a/app/views/shared/milestones/_record.html.haml b/app/views/shared/milestones/_record.html.haml new file mode 100644 index 00000000000..a753dd78a6a --- /dev/null +++ b/app/views/shared/milestones/_record.html.haml @@ -0,0 +1,17 @@ +-# @project is present when viewing Project's milestone +- project = @project || record.project + +%li{ id: dom_id(record, 'sortable'), class: "#{dom_class(record)}-row", 'data-iid' => record.iid, 'data-url' => polymorphic_path(record) } + %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · + = link_to_gfm record.title, [project.namespace.becomes(Namespace), project, record], title: record.title + %div{class: "#{dom_class(record)}-detail"} + = link_to [project.namespace.becomes(Namespace), project, record] do + %span{ class: "#{dom_class(record)}-number" } ##{record.iid} + - record.labels.each do |label| + = render_colored_label(label) + - if record.assignee + = image_tag avatar_icon(record.assignee, 16), class: "avatar s16", alt: '' diff --git a/app/views/shared/milestones/_records.html.haml b/app/views/shared/milestones/_records.html.haml new file mode 100644 index 00000000000..29fd6aaac9b --- /dev/null +++ b/app/views/shared/milestones/_records.html.haml @@ -0,0 +1,14 @@ +- show_counter = local_assigns.fetch(:show_counter, false) +- primary = local_assigns.fetch(:primary, false) +- panel_class = primary ? 'panel-primary' : 'panel-default' + +.panel{ class: panel_class } + .panel-heading + = title + - if show_counter + .pull-right= records.size + + - class_prefix = dom_class(records).pluralize + %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id } + - records.sort_by(&:position).each do |record| + = render 'shared/milestones/record', record: record, show_project_name: show_project_name, show_full_project_name: show_full_project_name From baa782ac9a29ba8fe162287511cbc9e4810fc4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 24 Feb 2016 15:37:40 -0500 Subject: [PATCH 051/112] Add some spinach specs. --- features/group/milestones.feature | 18 ++++++++++ features/steps/group/milestones.rb | 55 ++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/features/group/milestones.feature b/features/group/milestones.feature index 62ea66a783c..a097e07583a 100644 --- a/features/group/milestones.feature +++ b/features/group/milestones.feature @@ -28,3 +28,21 @@ Feature: Group Milestones And I fill milestone name When I press create mileston button Then milestone in each project should be created + + Scenario: I should see Issues listed with labels + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + And I click on one group milestone + Then I should see the "bug" label + And I should see the "feature" label + And I should see the project name in the Issue row + + Scenario: I should see the Labels tab + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + And I click on one group milestone + And I click on the "Labels" tab + Then I should see the list of labels + And I should see the project name in the Label row diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index 2363ad797fa..ce4e52181ce 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -24,6 +24,9 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end step 'I click on one group milestone' do + milestones = Milestone.where(title: 'GL-113') + @global_milestone = GlobalMilestone.new('GL-113', milestones) + click_link 'GL-113' end @@ -33,7 +36,7 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps step 'I should see group milestone with all issues and MRs assigned to that milestone' do expect(page).to have_content('Milestone GL-113') - expect(page).to have_content('Progress: 0 closed – 3 open') + expect(page).to have_content('3 issues: 3 open and 0 closed') issue = Milestone.find_by(name: 'GL-113').issues.first expect(page).to have_link(issue.title, href: namespace_project_issue_path(issue.project.namespace, issue.project, issue)) end @@ -60,6 +63,47 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end end + step 'I should see the "bug" label' do + page.within('#tab-issues') do + expect(page).to have_content 'bug' + end + end + + step 'I should see the "feature" label' do + page.within('#tab-issues') do + expect(page).to have_content 'bug' + end + end + + step 'I should see the project name in the Issue row' do + page.within('#tab-issues') do + @global_milestone.projects.each do |project| + expect(page).to have_content project.name + end + end + end + + step 'I click on the "Labels" tab' do + page.within('.nav-links') do + page.find(:xpath, "//a[@href='#tab-labels']").click + end + end + + step 'I should see the list of labels' do + page.within('#tab-labels') do + expect(page).to have_content 'bug' + expect(page).to have_content 'feature' + end + end + + step 'I should see the project name in the Label row' do + page.within('#tab-labels') do + @global_milestone.projects.each do |project| + expect(page).to have_content project.name + end + end + end + private def group_milestone @@ -68,6 +112,10 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| project = create :project, path: path, group: group milestone = create :milestone, title: "Version 7.2", project: project + + create(:label, project: project, title: 'bug') + create(:label, project: project, title: 'feature') + create :issue, project: project, assignee: current_user, @@ -80,11 +128,14 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps due_date: '2114-08-20', description: 'Lorem Ipsum is simply dummy text' - create :issue, + issue = create :issue, project: project, assignee: current_user, author: current_user, milestone: milestone + + issue.labels << project.labels.find_by(title: 'bug') + issue.labels << project.labels.find_by(title: 'feature') end end end From c91554de09cb2b19e1403fdf50f691004e6befdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 24 Feb 2016 17:45:49 -0500 Subject: [PATCH 052/112] Add link with filter by milestone for labels and avatar. Closes #13628 --- app/views/shared/milestones/_record.html.haml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/views/shared/milestones/_record.html.haml b/app/views/shared/milestones/_record.html.haml index a753dd78a6a..f82f2132bb6 100644 --- a/app/views/shared/milestones/_record.html.haml +++ b/app/views/shared/milestones/_record.html.haml @@ -1,5 +1,7 @@ -# @project is present when viewing Project's milestone - project = @project || record.project +- record_type = record.class.table_name +- base_url_args = [project.namespace.becomes(Namespace), project, record_type] %li{ id: dom_id(record, 'sortable'), class: "#{dom_class(record)}-row", 'data-iid' => record.iid, 'data-url' => polymorphic_path(record) } %span @@ -11,7 +13,11 @@ %div{class: "#{dom_class(record)}-detail"} = link_to [project.namespace.becomes(Namespace), project, record] do %span{ class: "#{dom_class(record)}-number" } ##{record.iid} + - record.labels.each do |label| - = render_colored_label(label) + %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) }< + = render_colored_label(label) + - if record.assignee - = image_tag avatar_icon(record.assignee, 16), class: "avatar s16", alt: '' + %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: record.assignee_id, state: 'all' }) } + = image_tag(avatar_icon(record.assignee, 16), class: "avatar s16", alt: '') From 7a311c23494b26d5212cac98318273fc9d2c69d1 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Sat, 5 Mar 2016 11:51:56 +0000 Subject: [PATCH 053/112] Updated project list dropdown to use dropdown classes --- .../stylesheets/framework/dropdowns.scss | 28 ++++--------------- app/views/shared/projects/_dropdown.html.haml | 15 ++++------ 2 files changed, 11 insertions(+), 32 deletions(-) diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index d2c7cdc3e92..3dc524ccca4 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -81,8 +81,9 @@ &::before { content: "\f00c"; position: absolute; - left: 4px; - top: 8px; + left: 5px; + top: 50%; + margin-top: -7px; font: normal normal normal 14px/1 FontAwesome; font-size: inherit; text-rendering: auto; @@ -94,28 +95,9 @@ } .dropdown-header { - padding-left: 10px; - padding-right: 10px; + padding-left: 5px; + padding-right: 5px; color: $dropdown-header-color; font-size: 13px; line-height: 22px; } - -.dropdown-with-checkmark { - li > .legend { - font-size: 13px; - color: $secondary-text; - margin-bottom: 5px; - } - - li > a { - padding-left: 20px; - line-height: 28px; - - i { - position: absolute; - left: 1px; - line-height: 28px; - } - } -} diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index daaac092dce..e7e04621ff4 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -5,21 +5,18 @@ %span.light = projects_sort_options_hash[@sort] %b.caret - %ul.dropdown-menu.dropdown-menu-align-right.dropdown-with-checkmark - %li - .legend Sort by + %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable + %li.dropdown-header + Sort by - projects_sort_options_hash.each do |value, title| %li - = link_to filter_projects_path(sort: value, archived: archived) do - = icon('check') if @sort == value + = link_to filter_projects_path(sort: value, archived: archived), class: ("is-active" if @sort == value) do = title %li.divider %li - = link_to filter_projects_path(sort: @sort, archived: nil) do - = icon('check') unless params[:archived].present? + = link_to filter_projects_path(sort: @sort, archived: nil), class: ("is-active" unless params[:archived].present?) do Hide archived projects %li - = link_to filter_projects_path(sort: @sort, archived: true) do - = icon('check') if params[:archived].present? + = link_to filter_projects_path(sort: @sort, archived: true), class: ("is-active" if params[:archived].present?) do Show archived projects From b3f533c3a770dd6359ec8ab08a7e562e3311b209 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 5 Mar 2016 16:01:05 +0100 Subject: [PATCH 054/112] Escape project's name in cross project label --- app/helpers/labels_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 93806ac199e..89a054289e8 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -65,7 +65,7 @@ module LabelsHelper def render_colored_cross_project_label(label) label_suffix = label.project.name_with_namespace - label_suffix = " in #{label_suffix}" + label_suffix = " in #{escape_once(label_suffix)}" render_colored_label(label, label_suffix) end From 836d5930332797192094ce4a3c8083e96f7e8c53 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 5 Mar 2016 17:17:49 -0500 Subject: [PATCH 055/112] Remove `Snippet#expires_at` This was removed from the interface in https://github.com/gitlabhq/gitlabhq/pull/6027 but its implementation lingered around for two years. --- app/finders/snippets_finder.rb | 6 +++--- app/helpers/snippets_helper.rb | 10 ---------- app/models/personal_snippet.rb | 1 - app/models/project_snippet.rb | 3 --- app/models/snippet.rb | 7 ------- .../20160305220806_remove_expires_at_from_snippets.rb | 5 +++++ db/schema.rb | 4 +--- doc/api/notes.md | 1 - doc/api/project_snippets.md | 1 - doc/web_hooks/web_hooks.md | 1 - lib/api/entities.rb | 2 +- spec/models/project_snippet_spec.rb | 1 - spec/models/snippet_spec.rb | 1 - 13 files changed, 10 insertions(+), 33 deletions(-) create mode 100644 db/migrate/20160305220806_remove_expires_at_from_snippets.rb diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index 07b5759443b..a41172816b8 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -4,7 +4,7 @@ class SnippetsFinder case filter when :all then - snippets(current_user).fresh.non_expired + snippets(current_user).fresh when :by_user then by_user(current_user, params[:user], params[:scope]) when :by_project @@ -27,7 +27,7 @@ class SnippetsFinder end def by_user(current_user, user, scope) - snippets = user.snippets.fresh.non_expired + snippets = user.snippets.fresh return snippets.are_public unless current_user @@ -48,7 +48,7 @@ class SnippetsFinder end def by_project(current_user, project) - snippets = project.snippets.fresh.non_expired + snippets = project.snippets.fresh if current_user if project.team.member?(current_user.id) diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 41ae4048992..0a5a8eb5aee 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -1,14 +1,4 @@ module SnippetsHelper - def lifetime_select_options - options = [ - ['forever', nil], - ['1 day', "#{Date.current + 1.day}"], - ['1 week', "#{Date.current + 1.week}"], - ['1 month', "#{Date.current + 1.month}"] - ] - options_for_select(options) - end - def reliable_snippet_path(snippet) if snippet.project_id? namespace_project_snippet_path(snippet.project.namespace, diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb index 9cee3b70cb3..452f3913eef 100644 --- a/app/models/personal_snippet.rb +++ b/app/models/personal_snippet.rb @@ -10,7 +10,6 @@ # created_at :datetime # updated_at :datetime # file_name :string(255) -# expires_at :datetime # type :string(255) # visibility_level :integer default(0), not null # diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb index 9e2c1b0e18e..1f7d85a5f3d 100644 --- a/app/models/project_snippet.rb +++ b/app/models/project_snippet.rb @@ -10,7 +10,6 @@ # created_at :datetime # updated_at :datetime # file_name :string(255) -# expires_at :datetime # type :string(255) # visibility_level :integer default(0), not null # @@ -23,6 +22,4 @@ class ProjectSnippet < Snippet # Scopes scope :fresh, -> { order("created_at DESC") } - scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) } - scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) } end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index f876be7a4c8..dd3925c7a7d 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -10,7 +10,6 @@ # created_at :datetime # updated_at :datetime # file_name :string(255) -# expires_at :datetime # type :string(255) # visibility_level :integer default(0), not null # @@ -46,8 +45,6 @@ class Snippet < ActiveRecord::Base scope :are_public, -> { where(visibility_level: Snippet::PUBLIC) } scope :public_and_internal, -> { where(visibility_level: [Snippet::PUBLIC, Snippet::INTERNAL]) } scope :fresh, -> { order("created_at DESC") } - scope :expired, -> { where(["expires_at IS NOT NULL AND expires_at < ?", Time.current]) } - scope :non_expired, -> { where(["expires_at IS NULL OR expires_at > ?", Time.current]) } participant :author, :notes @@ -111,10 +108,6 @@ class Snippet < ActiveRecord::Base nil end - def expired? - expires_at && expires_at < Time.current - end - def visibility_level_field visibility_level end diff --git a/db/migrate/20160305220806_remove_expires_at_from_snippets.rb b/db/migrate/20160305220806_remove_expires_at_from_snippets.rb new file mode 100644 index 00000000000..fc12b5b09e6 --- /dev/null +++ b/db/migrate/20160305220806_remove_expires_at_from_snippets.rb @@ -0,0 +1,5 @@ +class RemoveExpiresAtFromSnippets < ActiveRecord::Migration + def change + remove_column :snippets, :expires_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index 71d9257a31e..2d6b9b5a4cc 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: 20160222153918) do +ActiveRecord::Schema.define(version: 20160305220806) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -777,7 +777,6 @@ ActiveRecord::Schema.define(version: 20160222153918) do t.datetime "created_at" t.datetime "updated_at" t.string "file_name" - t.datetime "expires_at" t.string "type" t.integer "visibility_level", default: 0, null: false end @@ -785,7 +784,6 @@ ActiveRecord::Schema.define(version: 20160222153918) do add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree add_index "snippets", ["created_at", "id"], name: "index_snippets_on_created_at_and_id", using: :btree add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree - add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree add_index "snippets", ["updated_at"], name: "index_snippets_on_updated_at", using: :btree add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree diff --git a/doc/api/notes.md b/doc/api/notes.md index d4d63e825ab..85d4f0bafa2 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -145,7 +145,6 @@ Parameters: "state": "active", "created_at": "2013-09-30T13:46:01Z" }, - "expires_at": null, "updated_at": "2013-10-02T07:34:20Z", "created_at": "2013-10-02T07:34:20Z" } diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md index a7acf37b5bc..fb802102e3a 100644 --- a/doc/api/project_snippets.md +++ b/doc/api/project_snippets.md @@ -51,7 +51,6 @@ Parameters: "state": "active", "created_at": "2012-05-23T08:00:58Z" }, - "expires_at": null, "updated_at": "2012-06-28T10:52:04Z", "created_at": "2012-06-28T10:52:04Z" } diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index b82306bd1da..e2b53c45ab1 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -582,7 +582,6 @@ X-Gitlab-Event: Note Hook "created_at": "2015-04-09 02:40:38 UTC", "updated_at": "2015-04-09 02:40:38 UTC", "file_name": "test.rb", - "expires_at": null, "type": "ProjectSnippet", "visibility_level": 0 } diff --git a/lib/api/entities.rb b/lib/api/entities.rb index b021db8fa5b..aa3d8fe8437 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -141,7 +141,7 @@ module API class ProjectSnippet < Grape::Entity expose :id, :title, :file_name expose :author, using: Entities::UserBasic - expose :expires_at, :updated_at, :created_at + expose :updated_at, :created_at end class ProjectEntity < Grape::Entity diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index cc92eb0bd9f..e0feb606f78 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -10,7 +10,6 @@ # created_at :datetime # updated_at :datetime # file_name :string(255) -# expires_at :datetime # type :string(255) # visibility_level :integer default(0), not null # diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index eb2dbbdc5a4..7e5b5499aea 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -10,7 +10,6 @@ # created_at :datetime # updated_at :datetime # file_name :string(255) -# expires_at :datetime # type :string(255) # visibility_level :integer default(0), not null # From 042b047fc87ba4f76178383f9b5738cfbb9eb16c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 6 Mar 2016 16:34:43 -0500 Subject: [PATCH 056/112] Remove unused `another_email` factory --- spec/factories/emails.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb index ed072213153..9794772ac7d 100644 --- a/spec/factories/emails.rb +++ b/spec/factories/emails.rb @@ -1,14 +1,6 @@ FactoryGirl.define do factory :email do user - email do - FFaker::Internet.email('alias') - end - - factory :another_email do - email do - FFaker::Internet.email('another.alias') - end - end + email { FFaker::Internet.email('alias') } end end From de944c914b2a780a5e968cc32ef64cddd2b82c9b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 6 Mar 2016 16:53:22 -0500 Subject: [PATCH 057/112] Add traits for each access_level to ProjectMember factory --- .../project/merge_requests/acceptance.rb | 2 +- .../steps/project/merge_requests/revert.rb | 2 +- spec/factories/project_members.rb | 22 ++++++++++++++++++- spec/requests/api/branches_spec.rb | 4 ++-- spec/requests/api/builds_spec.rb | 4 ++-- spec/requests/api/commit_status_spec.rb | 10 ++++----- spec/requests/api/commits_spec.rb | 4 ++-- spec/requests/api/fork_spec.rb | 2 +- spec/requests/api/project_members_spec.rb | 4 ++-- spec/requests/api/projects_spec.rb | 4 ++-- spec/requests/api/repositories_spec.rb | 4 ++-- spec/requests/api/runners_spec.rb | 6 ++--- spec/requests/api/tags_spec.rb | 4 ++-- spec/requests/api/triggers_spec.rb | 4 ++-- spec/requests/api/variables_spec.rb | 4 ++-- 15 files changed, 50 insertions(+), 30 deletions(-) diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb index 2685f5fd6b4..4fda0731e2f 100644 --- a/features/steps/project/merge_requests/acceptance.rb +++ b/features/steps/project/merge_requests/acceptance.rb @@ -29,7 +29,7 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps step 'There is an open Merge Request' do @user = create(:user) @project = create(:project, :public) - @project_member = create(:project_member, user: @user, project: @project, access_level: ProjectMember::DEVELOPER) + @project_member = create(:project_member, :developer, user: @user, project: @project) @merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project) end diff --git a/features/steps/project/merge_requests/revert.rb b/features/steps/project/merge_requests/revert.rb index c5a4cfce6f0..efbc4831ce1 100644 --- a/features/steps/project/merge_requests/revert.rb +++ b/features/steps/project/merge_requests/revert.rb @@ -36,7 +36,7 @@ class Spinach::Features::RevertMergeRequests < Spinach::FeatureSteps step 'There is an open Merge Request' do @user = create(:user) @project = create(:project, :public) - @project_member = create(:project_member, user: @user, project: @project, access_level: ProjectMember::DEVELOPER) + @project_member = create(:project_member, :developer, user: @user, project: @project) @merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project) end diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb index 70fb7595c74..cf3659ba275 100644 --- a/spec/factories/project_members.rb +++ b/spec/factories/project_members.rb @@ -2,6 +2,26 @@ FactoryGirl.define do factory :project_member do user project - access_level { ProjectMember::MASTER } + master + + trait :guest do + access_level ProjectMember::GUEST + end + + trait :reporter do + access_level ProjectMember::REPORTER + end + + trait :developer do + access_level ProjectMember::DEVELOPER + end + + trait :master do + access_level ProjectMember::MASTER + end + + trait :owner do + access_level ProjectMember::OWNER + end end end diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 36461e84c3a..55582aa53d2 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -7,8 +7,8 @@ describe API::API, api: true do let(:user) { create(:user) } let(:user2) { create(:user) } let!(:project) { create(:project, creator_id: user.id) } - let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:guest) { create(:project_member, :guest, user: user2, project: project) } let!(:branch_name) { 'feature' } let!(:branch_sha) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 175ee861a71..967c34800d0 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -7,8 +7,8 @@ describe API::API, api: true do let(:api_user) { user } let(:user2) { create(:user) } let!(:project) { create(:project, creator_id: user.id) } - let!(:developer) { create(:project_member, user: user, project: project, access_level: ProjectMember::DEVELOPER) } - let!(:reporter) { create(:project_member, user: user2, project: project, access_level: ProjectMember::REPORTER) } + let!(:developer) { create(:project_member, :developer, user: user, project: project) } + let!(:reporter) { create(:project_member, :reporter, user: user2, project: project) } let(:commit) { create(:ci_commit, project: project)} let(:build) { create(:ci_build, commit: commit) } diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb index 8017ed97d88..429a24109fd 100644 --- a/spec/requests/api/commit_status_spec.rb +++ b/spec/requests/api/commit_status_spec.rb @@ -6,9 +6,9 @@ describe API::CommitStatus, api: true do let!(:project) { create(:project) } let(:commit) { project.repository.commit } let(:commit_status) { create(:commit_status, commit: ci_commit) } - let(:guest) { create_user(ProjectMember::GUEST) } - let(:reporter) { create_user(ProjectMember::REPORTER) } - let(:developer) { create_user(ProjectMember::DEVELOPER) } + let(:guest) { create_user(:guest) } + let(:reporter) { create_user(:reporter) } + let(:developer) { create_user(:developer) } let(:sha) { commit.id } @@ -201,9 +201,9 @@ describe API::CommitStatus, api: true do end end - def create_user(access_level) + def create_user(access_level_trait) user = create(:user) - create(:project_member, user: user, project: project, access_level: access_level) + create(:project_member, access_level_trait, user: user, project: project) user end end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 49acc3368f4..7ff21175c1b 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -6,8 +6,8 @@ describe API::API, api: true do let(:user) { create(:user) } let(:user2) { create(:user) } let!(:project) { create(:project, creator_id: user.id) } - let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:guest) { create(:project_member, :guest, user: user2, project: project) } let!(:note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'a comment on a commit') } let!(:another_note) { create(:note_on_commit, author: user, project: project, commit_id: project.repository.commit.id, note: 'another comment on a commit') } diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb index 3fe7efff5ba..fa94e03ec32 100644 --- a/spec/requests/api/fork_spec.rb +++ b/spec/requests/api/fork_spec.rb @@ -12,7 +12,7 @@ describe API::API, api: true do end let(:project_user2) do - create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) + create(:project_member, :guest, user: user2, project: project) end describe 'POST /projects/fork/:id' do diff --git a/spec/requests/api/project_members_spec.rb b/spec/requests/api/project_members_spec.rb index 6358f6a2a4a..4301588b16a 100644 --- a/spec/requests/api/project_members_spec.rb +++ b/spec/requests/api/project_members_spec.rb @@ -6,8 +6,8 @@ describe API::API, api: true do let(:user2) { create(:user) } let(:user3) { create(:user) } let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } - let(:project_member) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let(:project_member2) { create(:project_member, user: user3, project: project, access_level: ProjectMember::DEVELOPER) } + let(:project_member) { create(:project_member, :master, user: user, project: project) } + let(:project_member2) { create(:project_member, :developer, user: user3, project: project) } describe "GET /projects/:id/members" do before { project_member } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 2a310f3834d..9f2365a4832 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -12,8 +12,8 @@ describe API::API, api: true do let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) } let(:project3) { create(:project, path: 'project3', creator_id: user.id, namespace: user.namespace) } let(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } - let(:project_member) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let(:project_member2) { create(:project_member, user: user3, project: project, access_level: ProjectMember::DEVELOPER) } + let(:project_member) { create(:project_member, :master, user: user, project: project) } + let(:project_member2) { create(:project_member, :developer, user: user3, project: project) } let(:user4) { create(:user) } let(:project3) do create(:project, diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 0ae63b0afec..7cf4a01d76b 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -9,8 +9,8 @@ describe API::API, api: true do let(:user) { create(:user) } let(:user2) { create(:user) } let!(:project) { create(:project, creator_id: user.id) } - let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:guest) { create(:project_member, :guest, user: user2, project: project) } describe "GET /projects/:id/repository/tree" do context "authorized user" do diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb index 78484747d6a..3af61d4b335 100644 --- a/spec/requests/api/runners_spec.rb +++ b/spec/requests/api/runners_spec.rb @@ -28,9 +28,9 @@ describe API::Runners, api: true do before do # Set project access for users - create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) - create(:project_member, user: user, project: project2, access_level: ProjectMember::MASTER) - create(:project_member, user: user2, project: project, access_level: ProjectMember::REPORTER) + create(:project_member, :master, user: user, project: project) + create(:project_member, :master, user: user, project: project2) + create(:project_member, :reporter, user: user2, project: project) end describe 'GET /runners' do diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index f966e38cd3e..a15be07ed57 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -8,8 +8,8 @@ describe API::API, api: true do let(:user) { create(:user) } let(:user2) { create(:user) } let!(:project) { create(:project, creator_id: user.id) } - let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:guest) { create(:project_member, :guest, user: user2, project: project) } describe "GET /projects/:id/repository/tags" do let(:tag_name) { project.repository.tag_names.sort.reverse.first } diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 2a86b60bc4d..0510b77a39b 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -8,8 +8,8 @@ describe API::API do let!(:trigger_token) { 'secure_token' } let!(:trigger_token_2) { 'secure_token_2' } let!(:project) { create(:project, creator_id: user.id) } - let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let!(:developer) { create(:project_member, user: user2, project: project, access_level: ProjectMember::DEVELOPER) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:developer) { create(:project_member, :developer, user: user2, project: project) } let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) } let!(:trigger2) { create(:ci_trigger, project: project, token: trigger_token_2) } let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') } diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index 9744729ba0c..b1e1053d037 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -6,8 +6,8 @@ describe API::API, api: true do let(:user) { create(:user) } let(:user2) { create(:user) } let!(:project) { create(:project, creator_id: user.id) } - let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } - let!(:developer) { create(:project_member, user: user2, project: project, access_level: ProjectMember::DEVELOPER) } + let!(:master) { create(:project_member, :master, user: user, project: project) } + let!(:developer) { create(:project_member, :developer, user: user2, project: project) } let!(:variable) { create(:ci_variable, project: project) } describe 'GET /projects/:id/variables' do From 7612fdbaa86b7541595f60387dda77f145173a21 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 6 Mar 2016 16:55:17 -0500 Subject: [PATCH 058/112] Remove unnecessary attribute definitions from Service factory --- spec/factories/services.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/factories/services.rb b/spec/factories/services.rb index f7c285cff3a..9de78d68280 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -1,7 +1,5 @@ FactoryGirl.define do factory :service do - type "" - title "GitLab CI" project end end From 95b06a62c0db5f8c285a1d24fa1994e10c70ff27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Sun, 6 Mar 2016 23:07:19 -0500 Subject: [PATCH 059/112] Updates from last code review. --- CHANGELOG | 1 + app/assets/javascripts/dispatcher.js.coffee | 2 +- app/assets/javascripts/milestone.js.coffee | 7 +- app/assets/stylesheets/pages/milestone.scss | 14 +-- .../projects/milestones_controller.rb | 4 - app/decorators/label_with_milestone.rb | 19 ---- app/finders/issuable_finder.rb | 6 +- app/helpers/milestones_helper.rb | 26 ++++++ app/models/concerns/issuable.rb | 2 + app/models/concerns/milestoneish.rb | 25 +++++ app/models/global_label.rb | 7 +- app/models/global_milestone.rb | 53 +---------- app/models/merge_request.rb | 1 - app/models/milestone.rb | 27 +----- app/models/project.rb | 2 +- .../dashboard/milestones/_milestone.html.haml | 31 ++----- app/views/dashboard/milestones/show.html.haml | 86 +---------------- .../groups/milestones/_milestone.html.haml | 34 +------ app/views/groups/milestones/show.html.haml | 93 +------------------ .../projects/milestones/_milestone.html.haml | 36 +------ app/views/projects/milestones/show.html.haml | 59 +----------- .../shared/milestones/_issuable.html.haml | 25 +++++ ...records.html.haml => _issuables.html.haml} | 10 +- .../shared/milestones/_issues_tab.html.haml | 6 +- .../shared/milestones/_labels_tab.html.haml | 30 +++--- .../milestones/_merge_requests_tab.haml | 8 +- .../shared/milestones/_milestone.html.haml | 45 +++++++++ app/views/shared/milestones/_record.html.haml | 23 ----- .../shared/milestones/_summary.html.haml | 28 ++++++ app/views/shared/milestones/_tabs.html.haml | 30 ++++++ app/views/shared/milestones/_top.html.haml | 58 ++++++++++++ config/application.rb | 1 - features/group/milestones.feature | 1 - features/steps/group/milestones.rb | 8 -- features/steps/project/issues/milestones.rb | 2 +- spec/models/milestone_spec.rb | 3 +- 36 files changed, 317 insertions(+), 496 deletions(-) delete mode 100644 app/decorators/label_with_milestone.rb create mode 100644 app/models/concerns/milestoneish.rb create mode 100644 app/views/shared/milestones/_issuable.html.haml rename app/views/shared/milestones/{_records.html.haml => _issuables.html.haml} (52%) create mode 100644 app/views/shared/milestones/_milestone.html.haml delete mode 100644 app/views/shared/milestones/_record.html.haml create mode 100644 app/views/shared/milestones/_summary.html.haml create mode 100644 app/views/shared/milestones/_tabs.html.haml create mode 100644 app/views/shared/milestones/_top.html.haml diff --git a/CHANGELOG b/CHANGELOG index 8692f61fe36..bba032036e3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.6.0 (unreleased) - Allow search for logged out users - Don't show Issues/MRs from archived projects in Groups view - Increase the notes polling timeout over time (Roberto Dip) + - Show labels in dashboard and group milestone views v 8.5.4 - Do not cache requests for badges (including builds badge) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index d7feb5d5c87..54b28f2dd8d 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -23,7 +23,7 @@ class Dispatcher new Issue() shortcut_handler = new ShortcutsIssuable() new ZenMode() - when 'projects:milestones:show' + when 'projects:milestones:show', 'groups:milestones:show', 'dashboard:milestones:show' new Milestone() when 'projects:milestones:new', 'projects:milestones:edit' new ZenMode() diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee index e6d8518bec8..0037a3a21c2 100644 --- a/app/assets/javascripts/milestone.js.coffee +++ b/app/assets/javascripts/milestone.js.coffee @@ -69,7 +69,7 @@ class @Milestone @bindIssuesSorting() @bindMergeRequestSorting() - @bindTabsSwitching + @bindTabsSwitching() bindIssuesSorting: -> $("#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed").sortable( @@ -104,7 +104,7 @@ class @Milestone ).disableSelection() - bindMergeRequestSorting: -> + bindTabsSwitching: -> $('a[data-toggle="tab"]').on 'show.bs.tab', (e) -> currentTabClass = $(e.target).data('show') previousTabClass = $(e.relatedTarget).data('show') @@ -112,7 +112,8 @@ class @Milestone $(previousTabClass).hide() $(currentTabClass).removeClass('hidden') $(currentTabClass).show() - + + bindMergeRequestSorting: -> $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").sortable( connectWith: ".merge_requests-sortable-list", dropOnEmpty: true, diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss index 300c2fa7e1b..d0e72a4422c 100644 --- a/app/assets/stylesheets/pages/milestone.scss +++ b/app/assets/stylesheets/pages/milestone.scss @@ -19,10 +19,11 @@ li.milestone { width: 105px; } - .issue-row, .merge_request-row { + .issuable-row { .color-label { border-radius: 2px; padding: 3px !important; + margin-right: 7px; } // Issue title @@ -45,19 +46,14 @@ li.milestone { } .issues-sortable-list, .merge_requests-sortable-list { - .issue-detail, .merge_request-detail { + .issuable-detail { display: block; + margin-top: 7px; - .issue-number, .merge_request-number { + .issuable-number { color: rgba(0,0,0,0.44); margin-right: 5px; } - .color-label { - padding: 6px 10px; - margin-right: 7px; - margin-top: 10px; - } - .avatar { float: none; } diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 21f30f278c8..da46731d945 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -32,10 +32,6 @@ class Projects::MilestonesController < Projects::ApplicationController end def show - @issues = @milestone.issues - @users = @milestone.participants.uniq - @merge_requests = @milestone.merge_requests - @labels = @milestone.labels end def create diff --git a/app/decorators/label_with_milestone.rb b/app/decorators/label_with_milestone.rb deleted file mode 100644 index a70a4e2f50d..00000000000 --- a/app/decorators/label_with_milestone.rb +++ /dev/null @@ -1,19 +0,0 @@ -class LabelWithMilestone - attr_reader :milestone - - def initialize(label, milestone) - @label, @milestone = label, milestone - end - - def method_missing(meth, *args) - if @label.respond_to?(meth) - @label.send(meth, *args) - else - super - end - end - - def respond_to?(meth) - @label.respond_to?(meth) - end -end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index f7240edd618..c88a420b412 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -263,11 +263,9 @@ class IssuableFinder def by_label(items) if labels? if filter_by_no_label? - items = items. - joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id"). - where(label_links: { id: nil }) + items = items.without_label else - items = items.joins(:labels).where(labels: { title: label_names }) + items = items.with_label(label_names) if projects items = items.where(labels: { project_id: projects }) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 7de81d8dfdb..e3e7daa49c5 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -9,6 +9,32 @@ module MilestonesHelper end end + def milestones_label_path(opts = {}) + if @project + namespace_project_issues_path(@project.namespace, @project, opts) + elsif @group + issues_group_path(@group, opts) + else + issues_dashboard_path(opts) + end + end + + def milestones_browse_issuables_path(milestone, type:) + opts = { milestone_title: milestone.title } + + if @project + polymorphic_path([@project.namespace.becomes(Namespace), @project, type], opts) + elsif @group + polymorphic_url([type, @group], opts) + else + polymorphic_url([type, :dashboard], opts) + end + end + + def milestone_issues_by_label_count(milestone, label, state:) + milestone.issues.with_label(label.title).send(state).size + end + def milestone_progress_bar(milestone) options = { class: 'progress-bar progress-bar-success', diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index a3c4a3d2776..27b97944e38 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -36,6 +36,8 @@ module Issuable scope :closed, -> { with_state(:closed) } scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') } scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') } + scope :with_label, ->(title) { joins(:labels).where(labels: { title: title }) } + scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :join_project, -> { joins(:project) } scope :references_project, -> { references(:project) } diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb new file mode 100644 index 00000000000..d67df7c1d9c --- /dev/null +++ b/app/models/concerns/milestoneish.rb @@ -0,0 +1,25 @@ +module Milestoneish + def closed_items_count + issues.closed.size + merge_requests.closed_and_merged.size + end + + def total_items_count + issues.size + merge_requests.size + end + + def complete? + total_items_count == closed_items_count + end + + def percent_complete + ((closed_items_count * 100) / total_items_count).abs + rescue ZeroDivisionError + 0 + end + + def remaining_days + return 0 if !due_date || expired? + + (due_date - Date.today).to_i + end +end diff --git a/app/models/global_label.rb b/app/models/global_label.rb index 0171f7d54b7..ddd4bad5c21 100644 --- a/app/models/global_label.rb +++ b/app/models/global_label.rb @@ -2,16 +2,19 @@ class GlobalLabel attr_accessor :title, :labels alias_attribute :name, :title + delegate :color, :description, to: :@first_label + def self.build_collection(labels) labels = labels.group_by(&:title) - labels.map do |title, label| - new(title, label) + labels.map do |title, labels| + new(title, labels) end end def initialize(title, labels) @title = title @labels = labels + @first_label = labels.find { |lbl| lbl.description.present? } || labels.first end end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index e13aaf16732..97bd79af083 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -1,4 +1,6 @@ class GlobalMilestone + include Milestoneish + attr_accessor :title, :milestones alias_attribute :name, :title @@ -31,32 +33,6 @@ class GlobalMilestone @projects ||= Project.for_milestones(milestones.map(&:id)) end - def issues_count - issues.count - end - - def merge_requests_count - merge_requests.count - end - - def open_items_count - opened_issues.count + opened_merge_requests.count - end - - def closed_items_count - closed_issues.count + closed_merge_requests.count - end - - def total_items_count - issues_count + merge_requests_count - end - - def percent_complete - ((closed_items_count * 100) / total_items_count).abs - rescue ZeroDivisionError - 0 - end - def state state = milestones.map { |milestone| milestone.state } @@ -88,29 +64,8 @@ class GlobalMilestone end def labels - @labels ||= milestones.map do |ms| - ms.labels.map { |label| LabelWithMilestone.new(label, ms) } - end.flatten.sort_by!(&:title) - end - - def opened_issues - issues.opened - end - - def closed_issues - issues.closed - end - - def opened_merge_requests - merge_requests.opened - end - - def closed_merge_requests - merge_requests.with_states(:closed, :merged, :locked) - end - - def complete? - total_items_count == closed_items_count + @labels ||= GlobalLabel.build_collection(milestones.map(&:labels).flatten) + .sort_by!(&:title) end def due_date diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index f575494e2bf..0c1a47b3f6a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -137,7 +137,6 @@ class MergeRequest < ActiveRecord::Base scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) } scope :of_projects, ->(ids) { where(target_project_id: ids) } - scope :opened, -> { with_states(:opened, :reopened) } scope :merged, -> { with_state(:merged) } scope :closed_and_merged, -> { with_states(:closed, :merged) } diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 7dc2f909b2f..e3969f32dd6 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -24,12 +24,13 @@ class Milestone < ActiveRecord::Base include Sortable include Referable include StripAttribute + include Milestoneish belongs_to :project has_many :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :merge_requests - has_many :participants, through: :issues, source: :assignee + has_many :participants, -> { distinct.reorder('users.name') }, through: :issues, source: :assignee scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } @@ -92,30 +93,6 @@ class Milestone < ActiveRecord::Base end end - def open_items_count - self.issues.opened.count + self.merge_requests.opened.count - end - - def closed_items_count - self.issues.closed.count + self.merge_requests.closed_and_merged.count - end - - def total_items_count - self.issues.count + self.merge_requests.count - end - - def percent_complete - ((closed_items_count * 100) / total_items_count).abs - rescue ZeroDivisionError - 0 - end - - def remaining_days - return 0 if !due_date || expired? - - (due_date - Date.today).to_i - end - def expires_at if due_date if due_date.past? diff --git a/app/models/project.rb b/app/models/project.rb index 3a28d5d7fee..3235a1cee50 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -215,7 +215,7 @@ class Project < ActiveRecord::Base scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } scope :non_archived, -> { where(archived: false) } - scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids) } + scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } state_machine :import_status, initial: :none do event :import_start do diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml index ea6c304d7de..6173ca6ab9b 100644 --- a/app/views/dashboard/milestones/_milestone.html.haml +++ b/app/views/dashboard/milestones/_milestone.html.haml @@ -1,25 +1,6 @@ -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) } - .row - .col-sm-6 - %strong - = link_to_gfm truncate(milestone.title, length: 100), dashboard_milestone_path(milestone.safe_title, title: milestone.title) - .col-sm-6 - .pull-right.light #{milestone.percent_complete}% complete - .row - .col-sm-6 - = link_to issues_dashboard_path(milestone_title: milestone.title) do - = pluralize milestone.issues_count, 'Issue' - · - = link_to merge_requests_dashboard_path(milestone_title: milestone.title) do - = pluralize milestone.merge_requests_count, 'Merge Request' - .col-sm-6 - = milestone_progress_bar(milestone) - .row - .col-sm-6 - .expiration - = render 'shared/milestone_expired', milestone: milestone - .projects - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label.label-gray - = milestone.project.name_with_namespace += render 'shared/milestones/milestone', + milestone_path: dashboard_milestone_path(milestone.safe_title, title: milestone.title), + issues_path: issues_dashboard_path(milestone_title: milestone.title), + merge_requests_path: merge_requests_dashboard_path(milestone_title: milestone.title), + milestone: milestone, + dashboard: true diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 88edc025c62..60c84a26420 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -1,85 +1,5 @@ -- page_title @milestone.title, "Milestones" - header_title "Milestones", dashboard_milestones_path -.detail-page-header - .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - %span.identifier - Milestone #{@milestone.title} - -.detail-page-description.gray-content-block.second-block - %h2.title - = markdown escape_once(@milestone.title), pipeline: :single_line - -- if @milestone.complete? && @milestone.active? - .alert.alert-success.prepend-top-default - %span All issues for this milestone are closed. Navigate to the project to close the milestone. - -.table-holder - %table.table - %thead - %tr - %th Project - %th Open issues - %th State - %th Due date - - @milestone.milestones.each do |milestone| - %tr - %td - = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - %td - = milestone.issues.opened.count - %td - - if milestone.closed? - Closed - - else - Open - %td - = milestone.expires_at - -.context - .milestone-summary - %h4 Progress - %strong= @milestone.issues.size - issues: - %span.milestone-stat - %strong= @milestone.opened_issues.size - open and - %strong= @milestone.closed_issues.size - closed - %span.milestone-stat - %strong== #{@milestone.percent_complete}% - complete - - = milestone_progress_bar(@milestone) - -%ul.nav-links.no-top.no-bottom - %li.active - = link_to '#tab-issues', 'data-toggle' => 'tab' do - Issues - %span.badge= @milestone.issues_count - %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do - Merge Requests - %span.badge= @milestone.merge_requests_count - %li - = link_to '#tab-participants', 'data-toggle' => 'tab' do - Participants - %span.badge= @milestone.participants.count - %li - = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Labels - %span.badge= @milestone.labels.count - -.tab-content.milestone-content - .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_full_project_name: true - .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_full_project_name: true - .tab-pane#tab-participants - = render 'shared/milestones/participants_tab', users: @milestone.participants - .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_full_project_name: true += render 'shared/milestones/top', milestone: @milestone += render 'shared/milestones/summary', milestone: @milestone += render 'shared/milestones/tabs', milestone: @milestone, show_full_project_name: true diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml index 50558d7dce8..4c4e0a26728 100644 --- a/app/views/groups/milestones/_milestone.html.haml +++ b/app/views/groups/milestones/_milestone.html.haml @@ -1,29 +1,5 @@ -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) } - .row - .col-sm-6 - %strong - = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title) - .col-sm-6 - .pull-right.light #{milestone.percent_complete}% complete - .row - .col-sm-6 - = link_to issues_group_path(@group, milestone_title: milestone.title) do - = pluralize milestone.issues_count, 'Issue' - · - = link_to merge_requests_group_path(@group, milestone_title: milestone.title) do - = pluralize milestone.merge_requests_count, 'Merge Request' - .col-sm-6 - = milestone_progress_bar(milestone) - .row - .col-sm-6 - %div - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label.label-gray - = milestone.project.name - .col-sm-6 - - if can?(current_user, :admin_milestones, @group) - - if milestone.closed? - = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen" - - else - = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-xs btn-close" += render 'shared/milestones/milestone', + milestone_path: group_milestone_path(@group, milestone.safe_title, title: milestone.title), + issues_path: issues_group_path(@group, milestone_title: milestone.title), + merge_requests_path: merge_requests_group_path(@group, milestone_title: milestone.title), + milestone: milestone diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index bd61e50a5bd..fb6f0da28f8 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,91 +1,4 @@ -- page_title @milestone.title, "Milestones" = render "header_title" - -.detail-page-header - .status-box{ class: "status-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - %span.identifier - Milestone #{@milestone.title} - .pull-right - - if can?(current_user, :admin_milestones, @group) - - if @milestone.active? - = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" - - else - = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" - -.detail-page-description.gray-content-block.second-block - %h2.title - = markdown escape_once(@milestone.title), pipeline: :single_line - -- if @milestone.complete? && @milestone.active? - .alert.alert-success.prepend-top-default - %span All issues for this milestone are closed. You may close the milestone now. - -.table-holder - %table.table - %thead - %tr - %th Project - %th Open issues - %th State - %th Due date - - @milestone.milestones.each do |milestone| - %tr - %td - = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - %td - = milestone.issues.opened.count - %td - - if milestone.closed? - Closed - - else - Open - %td - = milestone.expires_at - -.context - .milestone-summary - %h4 Progress - %strong= @milestone.issues.size - issues: - %span.milestone-stat - %strong= @milestone.opened_issues.size - open and - %strong= @milestone.closed_issues.size - closed - %span.milestone-stat - %strong== #{@milestone.percent_complete}% - complete - - = milestone_progress_bar(@milestone) - -%ul.nav-links.no-top.no-bottom - %li.active - = link_to '#tab-issues', 'data-toggle' => 'tab' do - Issues - %span.badge= @milestone.issues_count - %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do - Merge Requests - %span.badge= @milestone.merge_requests_count - %li - = link_to '#tab-participants', 'data-toggle' => 'tab' do - Participants - %span.badge= @milestone.participants.count - %li - = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Labels - %span.badge= @milestone.labels.count - -.tab-content.milestone-content - .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @milestone.opened_issues.unassigned, assigned: @milestone.opened_issues.assigned, closed: @milestone.closed_issues, show_project_name: true - .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @milestone.opened_merge_requests.unassigned, assigned: @milestone.opened_merge_requests.assigned, closed: @milestone.merge_requests.closed, merged: @milestone.merge_requests.merged, show_project_name: true - .tab-pane#tab-participants - = render 'shared/milestones/participants_tab', users: @milestone.participants - .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @milestone.labels, show_project_name: true += render 'shared/milestones/top', milestone: @milestone, group: @group += render 'shared/milestones/summary', milestone: @milestone += render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 67d95ab0364..77b566db6b6 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -1,31 +1,5 @@ -%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) } - .row - .col-sm-6 - %strong - = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - - .col-sm-6 - .pull-right.light #{milestone.percent_complete}% complete - .row - .col-sm-6 - = link_to namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title) do - = pluralize milestone.issues.count, 'Issue' - · - = link_to namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title) do - = pluralize milestone.merge_requests.count, 'Merge Request' - .col-sm-6 - = milestone_progress_bar(milestone) - - .row - .col-sm-6 - = render 'shared/milestone_expired', milestone: milestone - .col-sm-6 - - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? - = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs" do - = icon('pencil-square-o') - Edit - \ - = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close" - = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do - = icon('trash-o') - Delete += render 'shared/milestones/milestone', + milestone_path: namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), + issues_path: namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title), + merge_requests_path: namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_title: milestone.title), + milestone: milestone diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 4aa1a53e87e..b4597043a27 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -42,62 +42,9 @@ = preserve do = markdown @milestone.description -- if @milestone.issues.any? && @milestone.can_be_closed? +- if @milestone.complete? && @milestone.active? .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close milestone now. -.context.prepend-top-default - .milestone-summary - %h4 Progress - %strong= @milestone.issues.count - issues: - %span.milestone-stat - %strong= @milestone.open_items_count - open and - %strong= @milestone.closed_items_count - closed - %span.milestone-stat - %strong== #{@milestone.percent_complete}% - complete - %span.milestone-stat - %span.remaining-days= milestone_remaining_days(@milestone) - %span.pull-right.tab-issues-buttons - - if can?(current_user, :create_issue, @project) - = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do - %i.fa.fa-plus - New Issue - - if can?(current_user, :read_issue, @project) - = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" - %span.pull-right.tab-merge-requests-buttons.hidden - - if can?(current_user, :read_merge_request, @project) - = link_to 'Browse Merge Requests', namespace_project_merge_requests_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" - - = milestone_progress_bar(@milestone) - -%ul.nav-links.no-top.no-bottom - %li.active - = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Issues - %span.badge= @issues.count - %li - = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do - Merge Requests - %span.badge= @merge_requests.count - %li - = link_to '#tab-participants', 'data-toggle' => 'tab' do - Participants - %span.badge= @users.count - %li - = link_to '#tab-labels', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do - Labels - %span.badge= @labels.count - -.tab-content.milestone-content - .tab-pane.active#tab-issues - = render 'shared/milestones/issues_tab', unassigned: @issues.opened.unassigned, assigned: @issues.opened.assigned, closed: @issues.closed - .tab-pane#tab-merge-requests - = render 'shared/milestones/merge_requests_tab', unassigned: @merge_requests.opened.unassigned, assigned: @merge_requests.opened.assigned, closed: @merge_requests.closed, merged: @merge_requests.merged - .tab-pane#tab-participants - = render 'shared/milestones/participants_tab', users: @users - .tab-pane#tab-labels - = render 'shared/milestones/labels_tab', labels: @labels += render 'shared/milestones/summary', milestone: @milestone, project: @project += render 'shared/milestones/tabs', milestone: @milestone diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml new file mode 100644 index 00000000000..f7c6fc14adf --- /dev/null +++ b/app/views/shared/milestones/_issuable.html.haml @@ -0,0 +1,25 @@ +-# @project is present when viewing Project's milestone +- project = @project || issuable.project +- assignee = issuable.assignee +- issuable_type = issuable.class.table_name +- base_url_args = [project.namespace.becomes(Namespace), project, issuable_type] + +%li{ id: dom_id(issuable, 'sortable'), class: "issuable-row", 'data-iid' => issuable.iid, 'data-url' => polymorphic_path(issuable) } + %span + - if show_project_name + %strong #{project.name} · + - elsif show_full_project_name + %strong #{project.name_with_namespace} · + = link_to_gfm issuable.title, [project.namespace.becomes(Namespace), project, issuable], title: issuable.title + %div{class: 'issuable-detail'} + = link_to [project.namespace.becomes(Namespace), project, issuable] do + %span{ class: 'issuable-number' }>= issuable.to_reference + + - issuable.labels.each do |label| + = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do + - render_colored_label(label) + + - if assignee + = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), + class: 'has_tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do + - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/shared/milestones/_records.html.haml b/app/views/shared/milestones/_issuables.html.haml similarity index 52% rename from app/views/shared/milestones/_records.html.haml rename to app/views/shared/milestones/_issuables.html.haml index 29fd6aaac9b..8619939dde7 100644 --- a/app/views/shared/milestones/_records.html.haml +++ b/app/views/shared/milestones/_issuables.html.haml @@ -6,9 +6,11 @@ .panel-heading = title - if show_counter - .pull-right= records.size + .pull-right= issuables.size - - class_prefix = dom_class(records).pluralize + - class_prefix = dom_class(issuables).pluralize %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id } - - records.sort_by(&:position).each do |record| - = render 'shared/milestones/record', record: record, show_project_name: show_project_name, show_full_project_name: show_full_project_name + = render partial: 'shared/milestones/issuable', + collection: issuables.sort_by(&:position), + as: :issuable, + locals: { show_project_name: show_project_name, show_full_project_name: show_full_project_name } diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml index 44a221114bb..a8db7f8a556 100644 --- a/app/views/shared/milestones/_issues_tab.html.haml +++ b/app/views/shared/milestones/_issues_tab.html.haml @@ -3,8 +3,8 @@ .row.prepend-top-default .col-md-4 - = render 'shared/milestones/records', args.merge({ title: 'Unstarted Issues (open and unassigned)', records: unassigned, id: 'unassigned', show_counter: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Unstarted Issues (open and unassigned)', issuables: issues.opened.unassigned, id: 'unassigned', show_counter: true) .col-md-4 - = render 'shared/milestones/records', args.merge({ title: 'Ongoing Issues (open and assigned)', records: assigned, id: 'ongoing', show_counter: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Ongoing Issues (open and assigned)', issuables: issues.opened.assigned, id: 'ongoing', show_counter: true) .col-md-4 - = render 'shared/milestones/records', args.merge({ title: 'Completed Issues (closed)', records: closed, id: 'closed', show_counter: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Completed Issues (closed)', issuables: issues.closed, id: 'closed', show_counter: true) diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml index 88e8e4f9006..ba27bafd1bc 100644 --- a/app/views/shared/milestones/_labels_tab.html.haml +++ b/app/views/shared/milestones/_labels_tab.html.haml @@ -1,22 +1,18 @@ -- show_project_name = local_assigns.fetch(:show_project_name, false) -- show_full_project_name = local_assigns.fetch(:show_full_project_name, false) - %ul.bordered-list.manage-labels-list - labels.each do |label| - - milestone = @milestone.is_a?(Milestone) ? @milestone : label.milestone + - options = { milestone_title: @milestone.title, label_name: label.title } %li - = render_colored_label(label) - - if show_project_name - %strong · #{milestone.project.name} - - elsif show_full_project_name - %strong · #{milestone.project.name_with_namespace} + %span.label-row + = link_to milestones_label_path(options) do + - render_colored_label(label) + %span.prepend-left-10 + = markdown(label.description, pipeline: :single_line) - - args = [milestone.project.namespace, milestone.project, milestone_title: milestone.title, label_name: label.title] - - options = args.extract_options! - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'opened')) do - = pluralize label.open_issues_count, 'open issue' - %span.issues-count - = link_to namespace_project_issues_path(*args, options.merge(state: 'closed')) do - = pluralize label.closed_issues_count, 'closed issue' + .pull-right + %strong.issues-count + = link_to milestones_label_path(options.merge(state: 'opened')) do + - pluralize milestone_issues_by_label_count(@milestone, label, state: :opened), 'open issue' + %strong.issues-count + = link_to milestones_label_path(options.merge(state: 'closed')) do + - pluralize milestone_issues_by_label_count(@milestone, label, state: :closed), 'closed issue' diff --git a/app/views/shared/milestones/_merge_requests_tab.haml b/app/views/shared/milestones/_merge_requests_tab.haml index 15b2876c32d..c29d8ee6737 100644 --- a/app/views/shared/milestones/_merge_requests_tab.haml +++ b/app/views/shared/milestones/_merge_requests_tab.haml @@ -3,10 +3,10 @@ .row.prepend-top-default .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Work in progress (open and unassigned)', records: unassigned, id: 'unassigned' }) + = render 'shared/milestones/issuables', args.merge(title: 'Work in progress (open and unassigned)', issuables: merge_requests.opened.unassigned, id: 'unassigned') .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Waiting for merge (open and assigned)', records: assigned, id: 'ongoing' }) + = render 'shared/milestones/issuables', args.merge(title: 'Waiting for merge (open and assigned)', issuables: merge_requests.opened.assigned, id: 'ongoing') .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Rejected (closed)', records: closed, id: 'closed' }) + = render 'shared/milestones/issuables', args.merge(title: 'Rejected (closed)', issuables: merge_requests.closed, id: 'closed') .col-md-3 - = render 'shared/milestones/records', args.merge({ title: 'Merged', records: merged, id: 'merged', primary: true }) + = render 'shared/milestones/issuables', args.merge(title: 'Merged', issuables: merge_requests.merged, id: 'merged', primary: true) diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml new file mode 100644 index 00000000000..f01138af3f0 --- /dev/null +++ b/app/views/shared/milestones/_milestone.html.haml @@ -0,0 +1,45 @@ +- dashboard = local_assigns[:dashboard] +- custom_dom_id = dom_id(@project ? milestone : milestone.milestones.first) + +%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: custom_dom_id } + .row + .col-sm-6 + %strong= link_to_gfm truncate(milestone.title, length: 100), milestone_path + .col-sm-6 + .pull-right.light #{milestone.percent_complete}% complete + .row + .col-sm-6 + = link_to pluralize(milestone.issues.size, 'Issue'), issues_path + · + = link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path + .col-sm-6= milestone_progress_bar(milestone) + - if milestone.is_a?(GlobalMilestone) + .row + .col-sm-6 + .expiration= render('shared/milestone_expired', milestone: milestone) + .projects + - milestone.milestones.each do |milestone| + = link_to milestone_path(milestone) do + %span.label.label-gray + = dashboard ? milestone.project.name_with_namespace : milestone.project.name + - if @group + .col-sm-6 + - if can?(current_user, :admin_milestones, @group) + - if milestone.closed? + = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen" + - else + = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-xs btn-close" + + - if @project + .row + .col-sm-6= render('shared/milestone_expired', milestone: milestone) + .col-sm-6 + - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? + = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs" do + = icon('pencil-square-o') + Edit + \ + = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close" + = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do + = icon('trash-o') + Delete diff --git a/app/views/shared/milestones/_record.html.haml b/app/views/shared/milestones/_record.html.haml deleted file mode 100644 index f82f2132bb6..00000000000 --- a/app/views/shared/milestones/_record.html.haml +++ /dev/null @@ -1,23 +0,0 @@ --# @project is present when viewing Project's milestone -- project = @project || record.project -- record_type = record.class.table_name -- base_url_args = [project.namespace.becomes(Namespace), project, record_type] - -%li{ id: dom_id(record, 'sortable'), class: "#{dom_class(record)}-row", 'data-iid' => record.iid, 'data-url' => polymorphic_path(record) } - %span - - if show_project_name - %strong #{project.name} · - - elsif show_full_project_name - %strong #{project.name_with_namespace} · - = link_to_gfm record.title, [project.namespace.becomes(Namespace), project, record], title: record.title - %div{class: "#{dom_class(record)}-detail"} - = link_to [project.namespace.becomes(Namespace), project, record] do - %span{ class: "#{dom_class(record)}-number" } ##{record.iid} - - - record.labels.each do |label| - %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) }< - = render_colored_label(label) - - - if record.assignee - %a{ href: polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: record.assignee_id, state: 'all' }) } - = image_tag(avatar_icon(record.assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/shared/milestones/_summary.html.haml b/app/views/shared/milestones/_summary.html.haml new file mode 100644 index 00000000000..59d4ae29f79 --- /dev/null +++ b/app/views/shared/milestones/_summary.html.haml @@ -0,0 +1,28 @@ +- project = local_assigns[:project] + +.context.prepend-top-default + .milestone-summary + %h4 Progress + %strong= milestone.issues.size + issues: + %span.milestone-stat + %strong= milestone.issues.opened.size + open and + %strong= milestone.issues.closed.size + closed + %span.milestone-stat + %strong== #{milestone.percent_complete}% + complete + + %span.milestone-stat + %span.remaining-days= milestone_remaining_days(milestone) + %span.pull-right.tab-issues-buttons + - if project && can?(current_user, :create_issue, project) + = link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn btn-grouped", title: "New Issue" do + %i.fa.fa-plus + New Issue + = link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn btn-grouped" + %span.pull-right.tab-merge-requests-buttons.hidden + = link_to 'Browse Merge Requests', milestones_browse_issuables_path(milestone, type: :merge_requests), class: "btn btn-grouped" + + = milestone_progress_bar(milestone) diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml new file mode 100644 index 00000000000..57d7ee85a3b --- /dev/null +++ b/app/views/shared/milestones/_tabs.html.haml @@ -0,0 +1,30 @@ +%ul.nav-links.no-top.no-bottom + %li.active + = link_to '#tab-issues', 'data-toggle' => 'tab', 'data-show' => '.tab-issues-buttons' do + Issues + %span.badge= milestone.issues.size + %li + = link_to '#tab-merge-requests', 'data-toggle' => 'tab', 'data-show' => '.tab-merge-requests-buttons' do + Merge Requests + %span.badge= milestone.merge_requests.size + %li + = link_to '#tab-participants', 'data-toggle' => 'tab' do + Participants + %span.badge= milestone.participants.count + %li + = link_to '#tab-labels', 'data-toggle' => 'tab' do + Labels + %span.badge= milestone.labels.count + +- show_project_name = local_assigns.fetch(:show_project_name, false) +- show_full_project_name = local_assigns.fetch(:show_full_project_name, false) + +.tab-content.milestone-content + .tab-pane.active#tab-issues + = render 'shared/milestones/issues_tab', issues: milestone.issues, show_project_name: show_project_name, show_full_project_name: show_full_project_name + .tab-pane#tab-merge-requests + = render 'shared/milestones/merge_requests_tab', merge_requests: milestone.merge_requests, show_project_name: show_project_name, show_full_project_name: show_full_project_name + .tab-pane#tab-participants + = render 'shared/milestones/participants_tab', users: milestone.participants + .tab-pane#tab-labels + = render 'shared/milestones/labels_tab', labels: milestone.labels diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml new file mode 100644 index 00000000000..4cf1d948b5b --- /dev/null +++ b/app/views/shared/milestones/_top.html.haml @@ -0,0 +1,58 @@ +- page_title milestone.title, "Milestones" + +- group = local_assigns[:group] + +.detail-page-header + .status-box{ class: "status-box-#{milestone.closed? ? 'closed' : 'open'}" } + - if milestone.closed? + Closed + - elsif milestone.expired? + Expired + - else + Open + %span.identifier + Milestone #{milestone.title} + - if milestone.expires_at + %span.creator + · + = milestone.expires_at + - if group + .pull-right + - if can?(current_user, :admin_milestones, group) + - if milestone.active? + = link_to 'Close Milestone', group_milestone_path(group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" + - else + = link_to 'Reopen Milestone', group_milestone_path(group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" + +.detail-page-description.gray-content-block.second-block + %h2.title + = markdown escape_once(milestone.title), pipeline: :single_line + +- if milestone.complete? && milestone.active? + .alert.alert-success.prepend-top-default + - close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.' + %span All issues for this milestone are closed. #{close_msg} + +.table-holder + %table.table + %thead + %tr + %th Project + %th Open issues + %th State + %th Due date + - milestone.milestones.each do |ms| + %tr + %td + - project_name = group ? ms.project.name : ms.project.name_with_namespace + = link_to project_name, namespace_project_milestone_path(ms.project.namespace, ms.project, ms) + %td + = ms.issues.opened.count + %td + - if ms.closed? + Closed + - else + Open + %td + = ms.expires_at + diff --git a/config/application.rb b/config/application.rb index fee8637a4cb..28684a3e578 100644 --- a/config/application.rb +++ b/config/application.rb @@ -15,7 +15,6 @@ module Gitlab # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths.push(*%W(#{config.root}/lib - #{config.root}/app/decorators #{config.root}/app/models/hooks #{config.root}/app/models/concerns #{config.root}/app/models/project_services diff --git a/features/group/milestones.feature b/features/group/milestones.feature index a097e07583a..d6c05df9840 100644 --- a/features/group/milestones.feature +++ b/features/group/milestones.feature @@ -45,4 +45,3 @@ Feature: Group Milestones And I click on one group milestone And I click on the "Labels" tab Then I should see the list of labels - And I should see the project name in the Label row diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index ce4e52181ce..a167d259837 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -96,14 +96,6 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps end end - step 'I should see the project name in the Label row' do - page.within('#tab-labels') do - @global_milestone.projects.each do |project| - expect(page).to have_content project.name - end - end - end - private def group_milestone diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb index e2eda511497..4faa0f4707c 100644 --- a/features/steps/project/issues/milestones.rb +++ b/features/steps/project/issues/milestones.rb @@ -59,7 +59,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps end step 'I should see 3 issues' do - expect(page).to have_selector('#tab-issues li.issue-row', count: 4) + expect(page).to have_selector('#tab-issues li.issuable-row', count: 4) end step 'I click link to remove milestone' do diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 1b1380ce4e2..28f13100d15 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -60,7 +60,7 @@ describe Milestone, models: true do end it "should recover from dividing by zero" do - expect(milestone.issues).to receive(:count).and_return(0) + expect(milestone.issues).to receive(:size).and_return(0) expect(milestone.percent_complete).to eq(0) end end @@ -114,7 +114,6 @@ describe Milestone, models: true do end it { expect(milestone.closed_items_count).to eq(1) } - it { expect(milestone.open_items_count).to eq(2) } it { expect(milestone.total_items_count).to eq(3) } it { expect(milestone.is_empty?).to be_falsey } end From 823633325833a6449be9351cec7d78788274d57f Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 7 Mar 2016 10:41:49 +0100 Subject: [PATCH 060/112] Finish refactor --- app/views/projects/commits/_commit_list.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml index 5fe4d918833..62e5f346777 100644 --- a/app/views/projects/commits/_commit_list.html.haml +++ b/app/views/projects/commits/_commit_list.html.haml @@ -1,4 +1,4 @@ -- commits, hidden = limited_commits(@commits, @project) +- commits, hidden = limited_commits(@commits) - commits = Commit.decorate(commits, @project) %div.panel.panel-default From d51877cac821d17bd79c9c0efbd8e15b7f4c09a3 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 7 Mar 2016 10:42:27 +0100 Subject: [PATCH 061/112] Change "some commits hidden" message --- app/views/projects/commits/_commit_list.html.haml | 2 +- app/views/projects/commits/_commits.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml index 62e5f346777..c0988eca6de 100644 --- a/app/views/projects/commits/_commit_list.html.haml +++ b/app/views/projects/commits/_commit_list.html.haml @@ -9,6 +9,6 @@ - commits.each do |commit| = render "projects/commits/inline_commit", commit: commit, project: @project %li.warning-row.unstyled - other #{hidden} commits hidden to prevent performance issues. + #{number_with_delimiter(hidden)} additional commits have been omitted. - else %ul.well-list= render commits, project: @project diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index 998251094a4..787fe443ac8 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -18,4 +18,4 @@ - if hidden > 0 .alert.alert-warning - Not shown: #{hidden} more commits + #{number_with_delimiter(hidden)} additional commits have been omitted. From a215e2ee8ddaefbfef16669ad0bd8ccd6853e163 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 7 Mar 2016 14:11:38 +0100 Subject: [PATCH 062/112] Revert changes in the Project model --- app/models/project.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index aba48f87be3..148eab692ff 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -56,7 +56,6 @@ class Project < ActiveRecord::Base extend Gitlab::ConfigHelper UNKNOWN_IMPORT_URL = 'http://unknown.git' - AVATAR_BRANCH = 'master' default_value_for :archived, false default_value_for :visibility_level, gitlab_config_features.visibility_level @@ -545,9 +544,9 @@ class Project < ActiveRecord::Base end def avatar_in_git - @avatar_file ||= 'logo.png' if repository.blob_at_branch(AVATAR_BRANCH, 'logo.png') - @avatar_file ||= 'logo.jpg' if repository.blob_at_branch(AVATAR_BRANCH, 'logo.jpg') - @avatar_file ||= 'logo.gif' if repository.blob_at_branch(AVATAR_BRANCH, 'logo.gif') + @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png') + @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg') + @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif') @avatar_file end From 41bc9c463c187396e47b4a942965de6ecddca5a1 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 7 Mar 2016 14:27:53 +0100 Subject: [PATCH 063/112] Refactor caching code --- app/controllers/projects/avatars_controller.rb | 5 +++-- app/controllers/projects/raw_controller.rb | 4 ++-- app/helpers/blob_helper.rb | 17 ++++++++++------- app/models/blob.rb | 3 +++ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 6de7888888f..a6bebc46b06 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -7,8 +7,9 @@ class Projects::AvatarsController < Projects::ApplicationController @blob = @repository.blob_at_branch('master', @project.avatar_in_git) if @blob headers['X-Content-Type-Options'] = 'nosniff' - set_cache_headers - check_etag! + + return if cached_blob? + headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) headers['Content-Disposition'] = 'inline' headers['Content-Type'] = safe_content_type(@blob) diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index b6ff08262d7..10de0e60530 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -12,8 +12,8 @@ class Projects::RawController < Projects::ApplicationController if @blob headers['X-Content-Type-Options'] = 'nosniff' - check_etag! - set_cache_headers + + return if cached_blob? if @blob.lfs_pointer? send_lfs_object diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index adb56e49c62..e5c0ed4b7bd 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -153,7 +153,10 @@ module BlobHelper end end - def set_cache_headers + def cached_blob? + stale = stale?(etag: @blob.id) # The #stale? method sets cache headers. + + # Because we are opionated we set the cache headers ourselves. if @project.visibility_level == Project::PUBLIC cache_control = 'public, ' else @@ -162,19 +165,19 @@ module BlobHelper if @ref && @commit && @ref == @commit.id # This is a link to a commit by its commit SHA. That means that the blob - # is immutable. - cache_control << 'max-age=600' # 10 minutes + # is immutable. The only reason to invalidate the cache is if the commit + # was deleted or if the user lost access to the repository. + max_age = Blob::CACHE_TIME_IMMUTABLE else # A branch or tag points at this blob. That means that the expected blob # value may change over time. - cache_control << 'max-age=60' # 1 minute + max_age = Blob::CACHE_TIME end + cache_control << "max-age=#{max_age}" headers['Cache-Control'] = cache_control headers['ETag'] = @blob.id - end - def check_etag! - stale?(etag: @blob.id) + !stale end end diff --git a/app/models/blob.rb b/app/models/blob.rb index 8ee9f3006b2..72e6c5fa3fd 100644 --- a/app/models/blob.rb +++ b/app/models/blob.rb @@ -1,5 +1,8 @@ # Blob is a Rails-specific wrapper around Gitlab::Git::Blob objects class Blob < SimpleDelegator + CACHE_TIME = 60 # Cache raw blobs referred to by a (mutable) ref for 1 minute + CACHE_TIME_IMMUTABLE = 3600 # Cache blobs referred to by an immutable reference for 1 hour + # Wrap a Gitlab::Git::Blob object, or return nil when given nil # # This method prevents the decorated object from evaluating to "truthy" when From a19b4e752918209a40cddfdf04cb2b7833017939 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Mon, 7 Mar 2016 15:39:12 +0100 Subject: [PATCH 064/112] make cleanup optional --- doc/update/8.4-to-8.5.md | 2 +- doc/update/patch_versions.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/update/8.4-to-8.5.md b/doc/update/8.4-to-8.5.md index c8083a45470..d323131ab7d 100644 --- a/doc/update/8.4-to-8.5.md +++ b/doc/update/8.4-to-8.5.md @@ -64,7 +64,7 @@ sudo -u git -H bundle install --without postgres development test --deployment # PostgreSQL installations (note: the line below states '--without mysql') sudo -u git -H bundle install --without mysql development test --deployment -# Clean up old gems +# Optional: clean up old gems sudo -u git -H bundle clean # Run database migrations diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index ce7ae0d285c..f446ed0a35b 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -62,8 +62,13 @@ sudo -u git -H bundle install --without development test mysql --deployment # MySQL sudo -u git -H bundle install --without development test postgres --deployment +# Optional: clean up old gems sudo -u git -H bundle clean + +# Run database migrations sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Clean up assets and cache sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` From a284d307838adf44080c43e4f553fe60b3495ffe Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 7 Mar 2016 16:49:46 +0100 Subject: [PATCH 065/112] Use Rails etag/cache_control helpers --- app/helpers/blob_helper.rb | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index e5c0ed4b7bd..0f77b3b299a 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -157,27 +157,20 @@ module BlobHelper stale = stale?(etag: @blob.id) # The #stale? method sets cache headers. # Because we are opionated we set the cache headers ourselves. - if @project.visibility_level == Project::PUBLIC - cache_control = 'public, ' - else - cache_control = 'private, ' - end + response.cache_control[:public] = @project.public? if @ref && @commit && @ref == @commit.id # This is a link to a commit by its commit SHA. That means that the blob # is immutable. The only reason to invalidate the cache is if the commit # was deleted or if the user lost access to the repository. - max_age = Blob::CACHE_TIME_IMMUTABLE + response.cache_control[:max_age] = Blob::CACHE_TIME_IMMUTABLE else # A branch or tag points at this blob. That means that the expected blob # value may change over time. - max_age = Blob::CACHE_TIME + response.cache_control[:max_age] = Blob::CACHE_TIME end - cache_control << "max-age=#{max_age}" - headers['Cache-Control'] = cache_control - headers['ETag'] = @blob.id - + response.etag = @blob.id !stale end end From 591bf92ce7ee4c8fab9f7d9491be8b9872cf8dc8 Mon Sep 17 00:00:00 2001 From: Iuri de Silvio Date: Mon, 22 Feb 2016 23:59:13 +0000 Subject: [PATCH 066/112] Fix bug where Bitbucket `closed` issues were imported as `opened` --- CHANGELOG | 1 + lib/gitlab/bitbucket_import/importer.rb | 2 +- .../gitlab/bitbucket_import/importer_spec.rb | 88 +++++++++++++++++++ 3 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 spec/lib/gitlab/bitbucket_import/importer_spec.rb diff --git a/CHANGELOG b/CHANGELOG index a8dc00ed814..a8a833e641c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.6.0 (unreleased) - Add support for cross-project label references - Update documentation to reflect Guest role not being enforced on internal projects - Allow search for logged out users + - Fix bug where Bitbucket `closed` issues were imported as `opened` (Iuri de Silvio) - Don't show Issues/MRs from archived projects in Groups view - Increase the notes polling timeout over time (Roberto Dip) - Show labels in dashboard and group milestone views diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index 3f483847efa..46e51a4bf6d 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -76,7 +76,7 @@ module Gitlab project.issues.create!( description: body, title: issue["title"], - state: %w(resolved invalid duplicate wontfix).include?(issue["status"]) ? 'closed' : 'opened', + state: %w(resolved invalid duplicate wontfix closed).include?(issue["status"]) ? 'closed' : 'opened', author_id: gl_user_id(project, reporter) ) end diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb new file mode 100644 index 00000000000..c413132abe5 --- /dev/null +++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb @@ -0,0 +1,88 @@ +require 'spec_helper' + +describe Gitlab::BitbucketImport::Importer, lib: true do + before do + Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "bitbucket") + end + + let(:statuses) do + [ + "open", + "resolved", + "on hold", + "invalid", + "duplicate", + "wontfix", + "closed" # undocumented status + ] + end + let(:sample_issues_statuses) do + issues = [] + + statuses.map.with_index do |status, index| + issues << { + local_id: index, + status: status, + title: "Issue #{index}", + content: "Some content to issue #{index}" + } + end + + issues + end + + let(:project_identifier) { 'namespace/repo' } + let(:data) do + { + bb_session: { + bitbucket_access_token: "123456", + bitbucket_access_token_secret: "secret" + } + } + end + let(:project) do + create( + :project, + import_source: project_identifier, + import_data: ProjectImportData.new(data: data) + ) + end + let(:importer) { Gitlab::BitbucketImport::Importer.new(project) } + let(:issues_statuses_sample_data) do + { + count: sample_issues_statuses.count, + issues: sample_issues_statuses + } + end + + context 'issues statuses' do + before do + stub_request( + :get, + "https://bitbucket.org/api/1.0/repositories/#{project_identifier}" + ).to_return(status: 200, body: { has_issues: true }.to_json) + + stub_request( + :get, + "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues?limit=50&sort=utc_created_on&start=0" + ).to_return(status: 200, body: issues_statuses_sample_data.to_json) + + sample_issues_statuses.each_with_index do |issue, index| + stub_request( + :get, + "https://bitbucket.org/api/1.0/repositories/#{project_identifier}/issues/#{issue[:local_id]}/comments" + ).to_return( + status: 200, + body: [{ author_info: { username: "username" }, utc_created_on: index }].to_json + ) + end + end + + it 'map statuses to open or closed' do + importer.execute + + expect(project.issues.where(state: "closed").size).to eq(5) + expect(project.issues.where(state: "opened").size).to eq(2) + end + end +end From f05c22295dbaa7eef769a232545ead6bb1d6c08e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Mar 2016 15:03:55 -0500 Subject: [PATCH 067/112] Bump poltergeist to `~> 1.9.0` --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index c66ef3cffad..62b2e55b537 100644 --- a/Gemfile +++ b/Gemfile @@ -273,7 +273,7 @@ group :development, :test do gem 'capybara', '~> 2.4.0' gem 'capybara-screenshot', '~> 1.0.0' - gem 'poltergeist', '~> 1.8.1' + gem 'poltergeist', '~> 1.9.0' gem 'teaspoon', '~> 1.0.0' gem 'teaspoon-jasmine', '~> 2.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 22c86e4ae8f..cc5e16c37d3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -552,7 +552,7 @@ GEM parser (2.2.3.0) ast (>= 1.1, < 3.0) pg (0.18.4) - poltergeist (1.8.1) + poltergeist (1.9.0) capybara (~> 2.1) cliver (~> 0.3.1) multi_json (~> 1.0) @@ -978,7 +978,7 @@ DEPENDENCIES org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) - poltergeist (~> 1.8.1) + poltergeist (~> 1.9.0) pry-rails quiet_assets (~> 1.0.2) rack-attack (~> 4.3.1) From 56c4f22c80b0555d5d7cfbc7f443122a8b70072c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Mar 2016 15:24:27 -0500 Subject: [PATCH 068/112] Remove redundant integration tests These three tests were essentially checking that adding a note to something updated its `noteable`'s `updated_at` attribute. This is well-tested Rails behavior and we shouldn't feel the need to write an integration test to verify it. At most we should be ensuring that the association definition adds the `touch: true` option, which we now do in Note's unit test. --- features/project/issues/issues.feature | 8 -------- features/project/merge_requests.feature | 19 ------------------- features/steps/project/issues/issues.rb | 4 ---- features/steps/project/merge_requests.rb | 8 -------- features/steps/shared/note.rb | 7 ------- spec/models/note_spec.rb | 2 +- 6 files changed, 1 insertion(+), 47 deletions(-) diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature index 89af58dcef3..ff21c7d1b83 100644 --- a/features/project/issues/issues.feature +++ b/features/project/issues/issues.feature @@ -58,14 +58,6 @@ Feature: Project Issues Then I should see comment "XML attached" And I should see an error alert section within the comment form - @javascript - Scenario: Visiting Issues after leaving a comment - Given I visit issue page "Release 0.4" - And I leave a comment like "XML attached" - And I visit project "Shop" issues page - And I sort the list by "Last updated" - Then I should see "Release 0.4" at the top - @javascript Scenario: Visiting Issues after being sorted the list Given I visit project "Shop" issues page diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index a69089f00c4..f8d9fe1854d 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -86,15 +86,6 @@ Feature: Project Merge Requests And I leave a comment like "XML attached" Then I should see comment "XML attached" - @javascript - Scenario: Visiting Merge Requests after leaving a comment - Given project "Shop" have "Bug NS-05" open merge request with diffs inside - And I visit merge request page "Bug NS-04" - And I leave a comment like "XML attached" - And I visit project "Shop" merge requests page - And I sort the list by "Last updated" - Then I should see "Bug NS-04" at the top - @javascript Scenario: Visiting Merge Requests after being sorted the list Given I visit project "Shop" merge requests page @@ -128,16 +119,6 @@ Feature: Project Merge Requests And I sort the list by "Least popular" Then The list should be sorted by "Least popular" - @javascript - Scenario: Visiting Merge Requests after commenting on diffs - Given project "Shop" have "Bug NS-05" open merge request with diffs inside - And I visit merge request page "Bug NS-05" - And I click on the Changes tab - And I leave a comment like "Line is wrong" on diff - And I visit project "Shop" merge requests page - And I sort the list by "Last updated" - Then I should see "Bug NS-05" at the top - @javascript Scenario: I comment on a merge request diff Given project "Shop" have "Bug NS-05" open merge request with diffs inside diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index 565bf088b41..d9842ccf95e 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -355,10 +355,6 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end end - step 'I should see "Release 0.4" at the top' do - expect(page.find('ul.content-list.issues-list li.issue:first-child')).to have_content("Release 0.4") - end - def filter_issue(text) fill_in 'issue_search', with: text end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 8bf423cc64b..c19b15bc9ed 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -517,14 +517,6 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end - step 'I should see "Bug NS-05" at the top' do - expect(page.find('ul.content-list.mr-list li.merge-request:first-child')).to have_content("Bug NS-05") - end - - step 'I should see "Bug NS-04" at the top' do - expect(page.find('ul.content-list.mr-list li.merge-request:first-child')).to have_content("Bug NS-04") - end - def merge_request @merge_request ||= MergeRequest.find_by!(title: "Bug NS-05") end diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb index eb6df61b8e6..444d6726f99 100644 --- a/features/steps/shared/note.rb +++ b/features/steps/shared/note.rb @@ -144,11 +144,4 @@ module SharedNote expect(page).to have_content("+1 Awesome!") end end - - step 'I sort the list by "Last updated"' do - find('button.dropdown-toggle.btn').click - page.within('ul.dropdown-menu.dropdown-menu-align-right li') do - click_link "Last updated" - end - end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 583937ca748..33085dac4ea 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -24,7 +24,7 @@ require 'spec_helper' describe Note, models: true do describe 'associations' do it { is_expected.to belong_to(:project) } - it { is_expected.to belong_to(:noteable) } + it { is_expected.to belong_to(:noteable).touch(true) } it { is_expected.to belong_to(:author).class_name('User') } it { is_expected.to have_many(:todos).dependent(:destroy) } From 73535b80b246248e932e6a88c462bc827d510e24 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Sun, 28 Feb 2016 17:27:22 +0000 Subject: [PATCH 069/112] adds language names to projects list github style --- app/models/project.rb | 9 +++++++++ app/views/shared/projects/_project.html.haml | 3 +++ 2 files changed, 12 insertions(+) diff --git a/app/models/project.rb b/app/models/project.rb index 3235a1cee50..2fdd4bfbbc1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -945,4 +945,13 @@ class Project < ActiveRecord::Base def wiki @wiki ||= ProjectWiki.new(self, self.owner) end + + def main_language + if !empty_repo? + languages = Linguist::Repository.new( + @repository.rugged, + @repository.rugged.head.target_id).languages + return languages.key(languages.values.max) + end + end end diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 99e48e86e38..97cfb76cdb0 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -28,6 +28,9 @@ = project.name .controls + - if project.main_language + %span + = project.main_language - if ci_commit %span = render_ci_status(ci_commit) From fe934a2da2be153d7346c90671103a5423b13833 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Sun, 28 Feb 2016 21:29:27 +0000 Subject: [PATCH 070/112] adds cache to languages list --- app/models/project.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 2fdd4bfbbc1..3a41812f3c9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -948,10 +948,10 @@ class Project < ActiveRecord::Base def main_language if !empty_repo? - languages = Linguist::Repository.new( - @repository.rugged, - @repository.rugged.head.target_id).languages - return languages.key(languages.values.max) + Rails.cache.fetch([self, "language"]) do + languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages + languages.key(languages.values.max) + end end end end From 85de3f69558af88ad0af8716ee6fb7197b2d28df Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Mon, 29 Feb 2016 10:15:53 +0000 Subject: [PATCH 071/112] changes if ! to unless and now asks for language instead of languages --- app/models/project.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 3a41812f3c9..92aef8a60f4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -947,10 +947,11 @@ class Project < ActiveRecord::Base end def main_language - if !empty_repo? + unless empty_repo? Rails.cache.fetch([self, "language"]) do - languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages - languages.key(languages.values.max) + Linguist::Repository.new( + @repository.rugged, + @repository.rugged.head.target_id).language end end end From d9e646a796795de43b23a27b74289f44255810e3 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Mon, 29 Feb 2016 16:50:59 +0000 Subject: [PATCH 072/112] moves method to repository model --- app/models/project.rb | 8 +------- app/models/repository.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 92aef8a60f4..3b565ef809a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -947,12 +947,6 @@ class Project < ActiveRecord::Base end def main_language - unless empty_repo? - Rails.cache.fetch([self, "language"]) do - Linguist::Repository.new( - @repository.rugged, - @repository.rugged.head.target_id).language - end - end + @main_language = repository.main_language end end diff --git a/app/models/repository.rb b/app/models/repository.rb index c135ab61f6a..f911611acab 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -812,6 +812,18 @@ class Repository raw_repository.ls_files(actual_ref) end + def main_language + return @main_language unless @main_language.nil? + + unless empty? + @main_language = cache.fetch(:main_language) do + Linguist::Repository.new( + rugged, + rugged.head.target_id).language + end + end + end + private def cache From a14b98f858cadf60c67db4cbd23aa10ae0d5213b Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Tue, 1 Mar 2016 01:55:36 +0000 Subject: [PATCH 073/112] implements project languages saving them onto the database --- app/models/.project.rb.swo | Bin 0 -> 49152 bytes app/models/project.rb | 12 ++++++++++ ...9193553_add_main_language_to_repository.rb | 21 ++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 app/models/.project.rb.swo create mode 100644 db/migrate/20160229193553_add_main_language_to_repository.rb diff --git a/app/models/.project.rb.swo b/app/models/.project.rb.swo new file mode 100644 index 0000000000000000000000000000000000000000..5d264ccfaaa42e0a6f93323aa8baf0a4e1fa7cf3 GIT binary patch literal 49152 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 Date: Tue, 1 Mar 2016 01:58:02 +0000 Subject: [PATCH 074/112] removes unused methods that I forgot --- app/models/project.rb | 4 ---- app/models/repository.rb | 12 ------------ 2 files changed, 16 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 4d2ff11cc11..3d3fd6f76a2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -957,8 +957,4 @@ class Project < ActiveRecord::Base def wiki @wiki ||= ProjectWiki.new(self, self.owner) end - - def main_language - @main_language = repository.main_language - end end diff --git a/app/models/repository.rb b/app/models/repository.rb index f911611acab..c135ab61f6a 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -812,18 +812,6 @@ class Repository raw_repository.ls_files(actual_ref) end - def main_language - return @main_language unless @main_language.nil? - - unless empty? - @main_language = cache.fetch(:main_language) do - Linguist::Repository.new( - rugged, - rugged.head.target_id).language - end - end - end - private def cache From c2c5572e22d8e03b111859f3cf143181edd8c20f Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Tue, 1 Mar 2016 11:42:47 +0000 Subject: [PATCH 075/112] adds swp and swo to gitignore and improves migration for project main_language --- app/models/.project.rb.swo | Bin 49152 -> 0 bytes app/models/project.rb | 18 ++++++++++------ app/models/repository.rb | 6 ++++++ ...9193553_add_main_language_to_repository.rb | 20 ++---------------- 4 files changed, 20 insertions(+), 24 deletions(-) delete mode 100644 app/models/.project.rb.swo diff --git a/app/models/.project.rb.swo b/app/models/.project.rb.swo deleted file mode 100644 index 5d264ccfaaa42e0a6f93323aa8baf0a4e1fa7cf3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49152 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 Date: Tue, 1 Mar 2016 19:45:55 +0000 Subject: [PATCH 076/112] adds tests and fixes some broken code to main language mr --- app/models/project.rb | 6 +++--- spec/models/project_spec.rb | 8 ++++++++ spec/models/repository_spec.rb | 6 ++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 9400d8a17af..470fa2194de 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -89,7 +89,7 @@ class Project < ActiveRecord::Base # checks if the language main language of the project changed before_save :check_main_language def check_main_language - if commit_count.changed? + if self.commit_count_changed? self.main_language = repository.main_language end end @@ -955,12 +955,12 @@ class Project < ActiveRecord::Base end def main_language - language = read_attributes(:main_language) + language = read_attribute(:main_language) return language if language update_attributes(main_language: repository.main_language) - read_attributes(:main_language) + read_attribute(:main_language) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f9842d23afa..60b0f55b31d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -560,6 +560,14 @@ describe Project, models: true do end end + describe "#main_language" do + let(:project) { create :project } + + it 'shows the main language of the project' do + expect(project.main_language).to eq("Ruby") + end + end + describe '#visibility_level_allowed?' do let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 1c7d66398cb..2094667565e 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -595,4 +595,10 @@ describe Repository, models: true do repository.after_remove_branch end end + + describe "#main_language" do + it 'shows the main language of the project' do + expect(repository.main_language).to eq("Ruby") + end + end end From 4b3f3167a4d7f1b1415052e350bc1f79ef5528dc Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 2 Mar 2016 12:55:01 +0000 Subject: [PATCH 077/112] implements test to simulate empty repo --- spec/models/repository_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 2094667565e..150422ac349 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -600,5 +600,11 @@ describe Repository, models: true do it 'shows the main language of the project' do expect(repository.main_language).to eq("Ruby") end + + it 'returns nil when the repository is empty' do + allow(repository).to receive(:empty?).and_return(true) + + expect(repository.main_language).to be_nil + end end end From 93fb4ce2c5f9c7db40a0ab698cd4812a5cb7373b Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 2 Mar 2016 13:01:00 +0000 Subject: [PATCH 078/112] removes redundant self --- app/models/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 470fa2194de..a8731514b9f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -89,7 +89,7 @@ class Project < ActiveRecord::Base # checks if the language main language of the project changed before_save :check_main_language def check_main_language - if self.commit_count_changed? + if commit_count_changed? self.main_language = repository.main_language end end From 68d6f5bce19bac95e63de165f3e9597273ef97b1 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 2 Mar 2016 13:06:21 +0000 Subject: [PATCH 079/112] fixes typo --- app/models/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index a8731514b9f..34787f5fa3c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -90,7 +90,7 @@ class Project < ActiveRecord::Base before_save :check_main_language def check_main_language if commit_count_changed? - self.main_language = repository.main_language + main_language = repository.main_language end end From 337cb45226cfc4955fdd79a15088522a91058b3c Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 2 Mar 2016 18:21:53 +0000 Subject: [PATCH 080/112] removes automatic setting of main_language to project for it to set a main language you have now to make a commit to the project --- app/models/project.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 34787f5fa3c..137fa42a9b9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -953,14 +953,4 @@ class Project < ActiveRecord::Base def wiki @wiki ||= ProjectWiki.new(self, self.owner) end - - def main_language - language = read_attribute(:main_language) - - return language if language - - update_attributes(main_language: repository.main_language) - - read_attribute(:main_language) - end end From 8039bbf7f8e686bdf1e8c00c44faa8a97a6c6fdb Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 2 Mar 2016 18:23:11 +0000 Subject: [PATCH 081/112] removes the test file of the project spec for invalid method --- spec/models/project_spec.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 60b0f55b31d..f9842d23afa 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -560,14 +560,6 @@ describe Project, models: true do end end - describe "#main_language" do - let(:project) { create :project } - - it 'shows the main language of the project' do - expect(project.main_language).to eq("Ruby") - end - end - describe '#visibility_level_allowed?' do let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL } From 96c02551070a5f2d90beea7068fc6fddbf1498e8 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Thu, 3 Mar 2016 10:51:48 +0000 Subject: [PATCH 082/112] moves the main_language update logic to git push service --- app/models/project.rb | 8 -------- app/services/git_push_service.rb | 12 ++++++++++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 137fa42a9b9..3235a1cee50 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -86,14 +86,6 @@ class Project < ActiveRecord::Base end end - # checks if the language main language of the project changed - before_save :check_main_language - def check_main_language - if commit_count_changed? - main_language = repository.main_language - end - end - ActsAsTaggableOn.strict_case_match = true acts_as_taggable_on :tags diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 9ba200f7bde..ab883b6ef2c 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -14,6 +14,7 @@ class GitPushService < BaseService # 3. Recognizes cross-references from commit messages # 4. Executes the project's web hooks # 5. Executes the project's services + # 6. Checks if the project's main language has changed # def execute @project.repository.after_push_commit(branch_name) @@ -42,6 +43,9 @@ class GitPushService < BaseService @push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev]) process_commit_messages end + # Checks if the main language has changed in the project and if so + # it updates it accordingly + update_main_language # Update merge requests that may be affected by this push. A new branch # could cause the last commit of a merge request to change. update_merge_requests @@ -49,6 +53,14 @@ class GitPushService < BaseService protected + def update_main_language + current_language = @project.repository.main_language + + unless current_language == @project.main_language + @project.update_attributes(main_language: current_language) + end + end + def update_merge_requests @project.update_merge_requests(params[:oldrev], params[:newrev], params[:ref], current_user) From 4e8a5343972e179022ff84954475a6aa0f8901f8 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Thu, 3 Mar 2016 11:06:37 +0000 Subject: [PATCH 083/112] add my mr to changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index a8dc00ed814..5b3b18ef2dc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.6.0 (unreleased) - Don't show Issues/MRs from archived projects in Groups view - Increase the notes polling timeout over time (Roberto Dip) - Show labels in dashboard and group milestone views + - Add main language of a project in the list of projects (Tiago Botelho) v 8.5.4 - Do not cache requests for badges (including builds badge) From 49295924586d89eaff899a4efc83a336148f3862 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Thu, 3 Mar 2016 15:40:05 +0000 Subject: [PATCH 084/112] adds test for git push service for updating the language of the project --- app/services/git_push_service.rb | 8 +++++--- spec/services/git_push_service_spec.rb | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index ab883b6ef2c..736b82e3571 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -51,16 +51,18 @@ class GitPushService < BaseService update_merge_requests end - protected - def update_main_language current_language = @project.repository.main_language unless current_language == @project.main_language - @project.update_attributes(main_language: current_language) + return @project.update_attributes(main_language: current_language) end + + true end + protected + def update_merge_requests @project.update_merge_requests(params[:oldrev], params[:newrev], params[:ref], current_user) diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 994585fb32c..f5c51e46e8b 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -155,6 +155,23 @@ describe GitPushService, services: true do end end + describe "Updates main language" do + + context "before push" do + it { expect(project.main_language).to eq(nil) } + end + + context "after push" do + before do + @service = execute_service(project, user, @oldrev, @newrev, @ref) + end + + it { expect(@service.update_main_language).to eq(true) } + it { expect(project.main_language).to eq("Ruby") } + end + end + + describe "Web Hooks" do context "execute web hooks" do it "when pushing a branch for the first time" do From 406f4a993a946db75d46756f936552144708a789 Mon Sep 17 00:00:00 2001 From: Patricio Cano Date: Mon, 7 Mar 2016 16:29:22 -0500 Subject: [PATCH 085/112] Fixed headers for anchors [ci skip] --- doc/integration/ldap.md | 11 +++-------- doc/integration/saml.md | 12 +++++------- doc/project_services/jira.md | 4 ++-- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md index ac15a2cdffc..cf1f98492ea 100644 --- a/doc/integration/ldap.md +++ b/doc/integration/ldap.md @@ -207,9 +207,7 @@ the LDAP server's SSL certificate is performed. ## Troubleshooting -### Common problems - -**Invalid credentials when logging in** +### Invalid credentials when logging in Make sure the user you are binding with has enough permissions to read the user's tree and traverse it. @@ -219,15 +217,12 @@ Also make sure that the `user_filter` is not blocking otherwise valid users. To make sure that the LDAP settings are correct and GitLab can see your users, execute the following command: -For Omnibus installations: ```bash +# For Omnibus installations sudo gitlab-rake gitlab:ldap:check -``` -For installations from source: - -```bash +# For installations from source sudo -u git -H bundle exec rake gitlab:ldap:check RAILS_ENV=production ``` diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 16e47bb99b0..148c4ac1886 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -133,9 +133,7 @@ will be returned to GitLab and will be signed in. ## Troubleshooting -### Common problems - -**500 error after login** +### 500 error after login If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page, this likely indicates that GitLab could not get the email address for the SAML user. @@ -143,7 +141,7 @@ this likely indicates that GitLab could not get the email address for the SAML u Make sure the IdP provides a claim containing the user's email address, using claim name `email` or `mail`. -**Redirect back to login screen with no evident error** +### Redirect back to login screen with no evident error If after signing in into your SAML server you are redirected back to the sign in page and no error is displayed, check your `production.log` file. It will most likely contain the @@ -155,13 +153,13 @@ To bypass this you can add `skip_before_action :verify_authenticity_token` to th where it can then be seen in the usual logs, or as a flash message in the login screen. -**Invalid audience** +### Invalid audience This error means that the IdP doesn't recognize GitLab as a valid sender and receiver of SAML requests. Make sure to add the GitLab callback URL to the approved audiences of the IdP server. -**Missing claims** +### Missing claims The IdP server needs to pass certain information in order for GitLab to either create an account, or match the login information to an existing account. `email` @@ -170,7 +168,7 @@ is not providing this information, all SAML requests will fail. Make sure this information is provided. -**Key validation error, Digest mismatch or Fingerprint mismatch** +### Key validation error, Digest mismatch or Fingerprint mismatch These errors all come from a similar place, the SAML certificate. SAML requests need to be validated using a fingerprint, a certificate or a validator. diff --git a/doc/project_services/jira.md b/doc/project_services/jira.md index 623ec857cbe..27170c1eb19 100644 --- a/doc/project_services/jira.md +++ b/doc/project_services/jira.md @@ -222,13 +222,13 @@ You can see from the above image that there are four references to GitLab: ## Troubleshooting -**GitLab is unable to comment on a ticket** +### GitLab is unable to comment on a ticket Make sure that the user you set up for GitLab to communicate with JIRA has the correct access permission to post comments on a ticket and to also transition the ticket, if you'd like GitLab to also take care of closing them. -**GitLab is unable to close a ticket** +### GitLab is unable to close a ticket Make sure the the `Transition ID` you set within the JIRA settings matches the one your project needs to close a ticket. From 9568b4b4f752bc564fead51756d1fd3044ddeffd Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Mar 2016 21:00:53 -0500 Subject: [PATCH 086/112] Document changes to the initial admin password [ci skip] --- CHANGELOG | 3 +++ doc/install/installation.md | 11 +++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a8dc00ed814..a9742d22015 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,9 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.6.0 (unreleased) - Contributions to forked projects are included in calendar - Improve the formatting for the user page bio (Connor Shea) + - Removed the default password from the initial admin account created during + setup. A password can be provided during setup (see installation docs), or + GitLab will ask the user to create a new one upon first visit. - Fix issue when pushing to projects ending in .wiki - Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - Don't load all of GitLab in mail_room diff --git a/doc/install/installation.md b/doc/install/installation.md index c1787a7c6a8..0fd54be58b0 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -467,12 +467,15 @@ NOTE: Supply `SANITIZE=true` environment variable to `gitlab:check` to omit proj ### Initial Login -Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has created a default admin account for you. You can use it to log in: +Visit YOUR_SERVER in your web browser for your first GitLab login. - root - 5iveL!fe +If you didn't [provide a root password during setup](#initialize-database-and-activate-advanced-features), +you'll be redirected to a password reset screen to provide the password for the +initial administrator account. Enter your desired password and you'll be +redirected back to the login screen. -**Important Note:** On login you'll be prompted to change the password. +The default account's username is **root**. Provide the password you created +earlier and login. After login you can change the username if you wish. **Enjoy!** From 5b621e1bb50e06c580f3c69ca832ab00a696217f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 8 Mar 2016 08:41:01 +0000 Subject: [PATCH 087/112] Snippet visibility badge spacing Closes #14074 --- app/assets/stylesheets/pages/snippets.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index 0161642d871..7d414ae003d 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -26,5 +26,5 @@ margin-right: 10px; font-size: $gl-font-size; border: 1px solid; - line-height: 40px; + line-height: 32px; } From 059df2256eb2ce5ddbbf43161eb8e9fca733bce4 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 4 Mar 2016 13:08:24 +0100 Subject: [PATCH 088/112] Added basic SQL guidelines [ci skip] --- doc/development/README.md | 1 + doc/development/sql.md | 219 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 doc/development/sql.md diff --git a/doc/development/README.md b/doc/development/README.md index b9a0d81e5ba..f5c3107ff44 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -9,4 +9,5 @@ - [Rake tasks](rake_tasks.md) for development - [Shell commands](shell_commands.md) in the GitLab codebase - [Sidekiq debugging](sidekiq_debugging.md) +- [SQL guidelines](sql.md) for SQL guidelines - [UI guide](ui_guide.md) for building GitLab with existing css styles and elements diff --git a/doc/development/sql.md b/doc/development/sql.md new file mode 100644 index 00000000000..23fd7604957 --- /dev/null +++ b/doc/development/sql.md @@ -0,0 +1,219 @@ +# SQL Query Guidelines + +This document describes various guidelines to follow when writing SQL queries, +either using ActiveRecord/Arel or raw SQL queries. + +## Using LIKE Statements + +The most common way to search for data is using the `LIKE` statement. For +example, to get all issues with a title starting with "WIP:" you'd write the +following query: + +```sql +SELECT * +FROM issues +WHERE title LIKE 'WIP:%'; +``` + +On PostgreSQL the `LIKE` statement is case-sensitive. On MySQL this depends on +the case-sensitivity of the collation, which is usually case-insensitive. To +perform a case-insensitive `LIKE` on PostgreSQL you have to use `ILIKE` instead. +This statement in turn isn't supported on MySQL. + +To work around this problem you should write `LIKE` queries using Arel instead +of raw SQL fragments as Arel automatically uses `ILIKE` on PostgreSQL and `LIKE` +on MySQL. This means that instead of this: + +```ruby +Issue.where('title LIKE ?', 'WIP:%') +``` + +You'd write this instead: + +```ruby +Issue.where(Issue.arel_table[:title].matches('WIP:%')) +``` + +Here `matches` generates the correct `LIKE` / `ILIKE` statement depending on the +database being used. + +If you need to chain multiple `OR` conditions you can also do this using Arel: + +```ruby +table = Issue.arel_table + +Issue.where(table[:title].matches('WIP:%').or(table[:foo].matches('WIP:%'))) +``` + +For PostgreSQL this produces: + +```sql +SELECT * +FROM issues +WHERE (title ILIKE 'WIP:%' OR foo ILIKE 'WIP:%') +``` + +In turn for MySQL this produces: + +```sql +SELECT * +FROM issues +WHERE (title LIKE 'WIP:%' OR foo LIKE 'WIP:%') +``` + +## LIKE & Indexes + +Neither PostgreSQL nor MySQL use any indexes when using `LIKE` / `ILIKE` with a +wildcard at the start. For example, this will not use any indexes: + +```sql +SELECT * +FROM issues +WHERE title ILIKE '%WIP:%'; +``` + +Because the value for `ILIKE` starts with a wildcard the database is not able to +use an index as it doesn't know where to start scanning the indexes. + +MySQL provides no known solution to this problem. Luckily PostgreSQL _does_ +provide a solution: trigram GIN indexes. These indexes can be created as +follows: + +```sql +CREATE INDEX [CONCURRENTLY] index_name_here +ON table_name +USING GIN(column_name gin_trgm_ops); +``` + +The key here is the `GIN(column_name gin_trgm_ops)` part. This creates a [GIN +index][gin-index] with the operator class set to `gin_trgm_ops`. These indexes +_can_ be used by `ILIKE` / `LIKE` and can lead to greatly improved performance. +One downside of these indexes is that they can easily get quite large (depending +on the amount of data indexed). + +To keep naming of these indexes consistent please use the following naming +pattern: + + index_TABLE_on_COLUMN_trigram + +For example, a GIN/trigram index for `issues.title` would be called +`index_issues_on_title_trigram`. + +Due to these indexes taking quite some time to be built they should be built +concurrently. This can be done by using `CREATE INDEX CONCURRENTLY` instead of +just `CREATE INDEX`. Concurrent indexes can _not_ be created inside a +transaction. Transactions for migrations can be disabled using the following +pattern: + +```ruby +class MigrationName < ActiveRecord::Migration + disable_ddl_transaction! +end +``` + +For example: + +```ruby +class AddUsersLowerUsernameEmailIndexes < ActiveRecord::Migration + disable_ddl_transaction! + + def up + return unless Gitlab::Database.postgresql? + + execute 'CREATE INDEX CONCURRENTLY index_on_users_lower_username ON users (LOWER(username));' + execute 'CREATE INDEX CONCURRENTLY index_on_users_lower_email ON users (LOWER(email));' + end + + def down + return unless Gitlab::Database.postgresql? + + remove_index :users, :index_on_users_lower_username + remove_index :users, :index_on_users_lower_email + end +end +``` + +## Plucking IDs + +This can't be stressed enough: **never** use ActiveRecord's `pluck` to pluck a +set of values into memory only to use them as an argument for another query. For +example, this will make the database **very** sad: + +```ruby +projects = Project.all.pluck(:id) + +MergeRequest.where(source_project_id: projects) +``` + +Instead you can just use sub-queries which perform far better: + +```ruby +MergeRequest.where(source_project_id: Project.all.select(:id)) +``` + +The _only_ time you should use `pluck` is when you actually need to operate on +the values in Ruby itself (e.g. write them to a file). In almost all other cases +you should ask yourself "Can I not just use a sub-query?". + +## Use UNIONs + +UNIONs aren't very commonly used in most Rails applications but they're very +powerful and useful. In most applications queries tend to use a lot of JOINs to +get related data or data based on certain criteria, but JOIN performance can +quickly deteriorate as the data involved grows. + +For example, if you want to get a list of projects where the name contains a +value _or_ the name of the namespace contains a value most people would write +the following query: + +```sql +SELECT * +FROM projects +JOIN namespaces ON namespaces.id = projects.namespace_id +WHERE projects.name ILIKE '%gitlab%' +OR namespaces.name ILIKE '%gitlab%'; +``` + +Using a large database this query can easily take around 800 milliseconds to +run. Using a UNION we'd write the following instead: + +```sql +SELECT projects.* +FROM projects +WHERE projects.name ILIKE '%gitlab%' + +UNION + +SELECT projects.* +FROM projects +JOIN namespaces ON namespaces.id = projects.namespace_id +WHERE namespaces.name ILIKE '%gitlab%'; +``` + +This query in turn only takes around 15 milliseconds to complete while returning +the exact same records. + +This doesn't mean you should start using UNIONs everywhere, but it's something +to keep in mind when using lots of JOINs in a query and filtering out records +based on the joined data. + +GitLab comes with a `Gitlab::SQL::Union` class that can be used to build a UNION +of multiple `ActiveRecord::Relation` objects. You can use this class as +follows: + +```ruby +union = Gitlab::SQL::Union.new([projects, more_projects, ...]) + +Project.from("(#{union.to_sql}) projects") +``` + +## Ordering by Creation Date + +When ordering records based on the time they were created you can simply order +by the `id` column instead of ordering by `created_at`. Because IDs are always +unique and incremented in the order that rows are created this will produce the +exact same results. This also means there's no need to add an index on +`created_at` to ensure consistent performance as `id` is already indexed by +default. + +[gin-index]: http://www.postgresql.org/docs/current/static/gin.html From 6ac68d8cb0477367fbf9642526c459b87b116d40 Mon Sep 17 00:00:00 2001 From: Yatish Mehta Date: Fri, 4 Mar 2016 14:34:32 -0800 Subject: [PATCH 089/112] Remove instance variable @group multiple assignment --- CHANGELOG | 1 + app/controllers/admin/groups_controller.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index a98bdd26f74..1c09111443a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ v 8.6.0 (unreleased) - Fix issue when pushing to projects ending in .wiki - Fix avatar stretching by providing a cropping feature (Johann Pardanaud) - Don't load all of GitLab in mail_room + - Memoize @group in Admin::GroupsController (Yatish Mehta) - Indicate how much an MR diverged from the target branch (Pierre de La Morinerie) - Strip leading and trailing spaces in URL validator (evuez) - Return empty array instead of 404 when commit has no statuses in commit status API diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 4d3e48f7f81..668396a0f20 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -55,7 +55,7 @@ class Admin::GroupsController < Admin::ApplicationController private def group - @group = Group.find_by(path: params[:id]) + @group ||= Group.find_by(path: params[:id]) end def group_params From cb5a5ba09588af866aed595d960755bf4ced4483 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 8 Mar 2016 17:38:23 +0100 Subject: [PATCH 090/112] Cache & flush tag/branch counts The methods used for this are Repository#tag_count and Repository#branch_count which cache their output in Redis as well as memoizing it in an instance variable. Both methods have a corresponding methods/hooks to flush the caches at the right time. --- app/models/repository.rb | 33 +++++++++++++-- app/services/git_tag_push_service.rb | 2 +- app/views/projects/branches/destroy.js.haml | 2 +- app/views/projects/commits/_head.html.haml | 4 +- spec/models/repository_spec.rb | 47 +++++++++++++++++++-- spec/services/delete_tag_service_spec.rb | 26 ++++++++++++ 6 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 spec/services/delete_tag_service_spec.rb diff --git a/app/models/repository.rb b/app/models/repository.rb index ff48f993d42..d3ca268a659 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -160,7 +160,7 @@ class Repository end def rm_tag(tag_name) - expire_tags_cache + before_remove_tag gitlab_shell.rm_tag(path_with_namespace, tag_name) end @@ -183,6 +183,14 @@ class Repository end end + def branch_count + @branch_count ||= cache.fetch(:branch_count) { raw_repository.branch_count } + end + + def tag_count + @tag_count ||= cache.fetch(:tag_count) { raw_repository.rugged.tags.count } + end + # Return repo size in megabytes # Cached in redis def size @@ -278,6 +286,16 @@ class Repository @has_visible_content = nil end + def expire_branch_count_cache + cache.expire(:branch_count) + @branch_count = nil + end + + def expire_tag_count_cache + cache.expire(:tag_count) + @tag_count = nil + end + def rebuild_cache cache_keys.each do |key| cache.expire(key) @@ -313,9 +331,16 @@ class Repository expire_root_ref_cache end - # Runs code before creating a new tag. - def before_create_tag + # Runs code before pushing (= creating or removing) a tag. + def before_push_tag expire_cache + expire_tag_count_cache + end + + # Runs code before removing a tag. + def before_remove_tag + expire_tags_cache + expire_tag_count_cache end # Runs code after a repository has been forked/imported. @@ -331,11 +356,13 @@ class Repository # Runs code after a new branch has been created. def after_create_branch expire_has_visible_content_cache + expire_branch_count_cache end # Runs code after an existing branch has been removed. def after_remove_branch expire_has_visible_content_cache + expire_branch_count_cache end def method_missing(m, *args, &block) diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index a62c5fc4fc4..c88c7672805 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -2,7 +2,7 @@ class GitTagPushService attr_accessor :project, :user, :push_data def execute(project, user, oldrev, newrev, ref) - project.repository.before_create_tag + project.repository.before_push_tag @project, @user = project, user @push_data = build_push_data(oldrev, newrev, ref) diff --git a/app/views/projects/branches/destroy.js.haml b/app/views/projects/branches/destroy.js.haml index 882a4d0c5e2..a21ddaf4930 100644 --- a/app/views/projects/branches/destroy.js.haml +++ b/app/views/projects/branches/destroy.js.haml @@ -1 +1 @@ -$('.js-totalbranch-count').html("#{@repository.branches.size}") +$('.js-totalbranch-count').html("#{@repository.branch_count}") diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 498c5e05b32..7a5b0d993db 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -15,9 +15,9 @@ = nav_link(html_options: {class: branches_tab_class}) do = link_to namespace_project_branches_path(@project.namespace, @project) do Branches - %span.badge.js-totalbranch-count= @repository.branches.size + %span.badge.js-totalbranch-count= @repository.branch_count = nav_link(controller: [:tags, :releases]) do = link_to namespace_project_tags_path(@project.namespace, @project) do Tags - %span.badge.js-totaltags-count= @repository.tags.length + %span.badge.js-totaltags-count= @repository.tag_count diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 150422ac349..7af80704690 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -405,7 +405,7 @@ describe Repository, models: true do end end - describe '#expire_branch_ache' do + describe '#expire_branch_cache' do # This method is private but we need it for testing purposes. Sadly there's # no other proper way of testing caching operations. let(:cache) { repository.send(:cache) } @@ -556,11 +556,12 @@ describe Repository, models: true do end end - describe '#before_create_tag' do + describe '#before_push_tag' do it 'flushes the cache' do expect(repository).to receive(:expire_cache) + expect(repository).to receive(:expire_tag_count_cache) - repository.before_create_tag + repository.before_push_tag end end @@ -607,4 +608,44 @@ describe Repository, models: true do expect(repository.main_language).to be_nil end end + + describe '#before_remove_tag' do + it 'flushes the tag cache' do + expect(repository).to receive(:expire_tag_count_cache) + + repository.before_remove_tag + end + end + + describe '#branch_count' do + it 'returns the number of branches' do + expect(repository.branch_count).to be_an_instance_of(Fixnum) + end + end + + describe '#tag_count' do + it 'returns the number of tags' do + expect(repository.tag_count).to be_an_instance_of(Fixnum) + end + end + + describe '#expire_branch_count_cache' do + let(:cache) { repository.send(:cache) } + + it 'expires the cache' do + expect(cache).to receive(:expire).with(:branch_count) + + repository.expire_branch_count_cache + end + end + + describe '#expire_tag_count_cache' do + let(:cache) { repository.send(:cache) } + + it 'expires the cache' do + expect(cache).to receive(:expire).with(:tag_count) + + repository.expire_tag_count_cache + end + end end diff --git a/spec/services/delete_tag_service_spec.rb b/spec/services/delete_tag_service_spec.rb new file mode 100644 index 00000000000..5b7ba521812 --- /dev/null +++ b/spec/services/delete_tag_service_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe DeleteTagService, services: true do + let(:project) { create(:project) } + let(:repository) { project.repository } + let(:user) { create(:user) } + let(:service) { described_class.new(project, user) } + + let(:tag) { double(:tag, name: '8.5', target: 'abc123') } + + describe '#execute' do + before do + allow(repository).to receive(:find_tag).and_return(tag) + end + + it 'removes the tag' do + expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag). + and_return(true) + + expect(repository).to receive(:before_remove_tag) + expect(service).to receive(:success) + + service.execute('8.5') + end + end +end From 177025b5dde5ca34b05744f000a1eee64501863a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 8 Mar 2016 17:53:00 +0100 Subject: [PATCH 091/112] Call the right hooks when removing branches This ensures that Repository#rm_branch calls before_remove_branch/after_remove_branch instead of just 1 random cache expiration method. --- app/models/repository.rb | 10 ++++++++-- spec/models/repository_spec.rb | 11 +++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index d3ca268a659..c3ae461a016 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -144,7 +144,7 @@ class Repository end def rm_branch(user, branch_name) - expire_branches_cache + before_remove_branch branch = find_branch(branch_name) oldrev = branch.try(:target) @@ -155,7 +155,7 @@ class Repository rugged.branches.delete(branch_name) end - expire_branches_cache + after_remove_branch true end @@ -359,10 +359,16 @@ class Repository expire_branch_count_cache end + # Runs code before removing an existing branch. + def before_remove_branch + expire_branches_cache + end + # Runs code after an existing branch has been removed. def after_remove_branch expire_has_visible_content_cache expire_branch_count_cache + expire_branches_cache end def method_missing(m, *args, &block) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 7af80704690..b2b4d38756f 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -648,4 +648,15 @@ describe Repository, models: true do repository.expire_tag_count_cache end end + + describe '#rm_branch' do + let(:user) { create(:user) } + + it 'removes a branch' do + expect(repository).to receive(:before_remove_branch) + expect(repository).to receive(:after_remove_branch) + + repository.rm_branch(user, 'feature') + end + end end From 6857b92fabe511c4ed32310ad0f6bf43c534ffab Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 8 Mar 2016 17:53:23 +0100 Subject: [PATCH 092/112] Added specs for Repository#rm_tag --- spec/models/repository_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index b2b4d38756f..40cbb3d635e 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -659,4 +659,15 @@ describe Repository, models: true do repository.rm_branch(user, 'feature') end end + + describe '#rm_tag' do + it 'removes a tag' do + expect(repository).to receive(:before_remove_tag) + + expect_any_instance_of(Gitlab::Shell).to receive(:rm_tag). + with(repository.path_with_namespace, '8.5') + + repository.rm_tag('8.5') + end + end end From 4ec035b48869d8364f6ae8cb14c8486075786757 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 8 Mar 2016 18:01:16 +0100 Subject: [PATCH 093/112] Call the right hooks in Repository#add_tag This ensures Repository#add_tag calls Repository#before_push_tag instead of just 1 random cache expiration method. --- app/models/repository.rb | 3 ++- spec/models/repository_spec.rb | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index c3ae461a016..c0730b34316 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -138,7 +138,7 @@ class Repository end def add_tag(tag_name, ref, message = nil) - expire_tags_cache + before_push_tag gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) end @@ -334,6 +334,7 @@ class Repository # Runs code before pushing (= creating or removing) a tag. def before_push_tag expire_cache + expire_tags_cache expire_tag_count_cache end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 40cbb3d635e..97778c354a4 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -649,6 +649,17 @@ describe Repository, models: true do end end + describe '#add_tag' do + it 'adds a tag' do + expect(repository).to receive(:before_push_tag) + + expect_any_instance_of(Gitlab::Shell).to receive(:add_tag). + with(repository.path_with_namespace, '8.5', 'master', 'foo') + + repository.add_tag('8.5', 'master', 'foo') + end + end + describe '#rm_branch' do let(:user) { create(:user) } From 590e1b4b21f2a39bf573800392b04859b563f3e5 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 8 Mar 2016 18:04:00 +0100 Subject: [PATCH 094/112] Call after_create_branch in Repository#add_branch This ensures the right caches are flushed when adding a branch via the UI, instead of only flushing this one random cache. --- app/models/repository.rb | 3 ++- spec/models/repository_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index c0730b34316..6441cd87e87 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -133,7 +133,7 @@ class Repository rugged.branches.create(branch_name, target) end - expire_branches_cache + after_create_branch find_branch(branch_name) end @@ -356,6 +356,7 @@ class Repository # Runs code after a new branch has been created. def after_create_branch + expire_branches_cache expire_has_visible_content_cache expire_branch_count_cache end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 97778c354a4..34866be3395 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -148,6 +148,12 @@ describe Repository, models: true do expect(branch.name).to eq('new_feature') end + + it 'calls the after_create_branch hook' do + expect(repository).to receive(:after_create_branch) + + repository.add_branch(user, 'new_feature', 'master') + end end context 'when pre hooks failed' do From 7000e76a01f20fcb739587cb446610c4076bf910 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Mar 2016 16:39:53 -0500 Subject: [PATCH 095/112] Simplify the various Snippet factories --- spec/factories/personal_snippets.rb | 11 ----------- spec/factories/project_snippets.rb | 6 +----- spec/factories/snippets.rb | 12 ++++++++++++ 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/spec/factories/personal_snippets.rb b/spec/factories/personal_snippets.rb index b493a6968ff..0f13b2c1020 100644 --- a/spec/factories/personal_snippets.rb +++ b/spec/factories/personal_snippets.rb @@ -1,15 +1,4 @@ FactoryGirl.define do factory :personal_snippet, parent: :snippet, class: :PersonalSnippet do - trait :public do - visibility_level PersonalSnippet::PUBLIC - end - - trait :internal do - visibility_level PersonalSnippet::INTERNAL - end - - trait :private do - visibility_level PersonalSnippet::PRIVATE - end end end diff --git a/spec/factories/project_snippets.rb b/spec/factories/project_snippets.rb index 154442bd3db..d681a2c8483 100644 --- a/spec/factories/project_snippets.rb +++ b/spec/factories/project_snippets.rb @@ -1,9 +1,5 @@ FactoryGirl.define do - factory :project_snippet do + factory :project_snippet, parent: :snippet, class: :ProjectSnippet do project - author - title - content - file_name end end diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb index b9127b3d75e..365f12a0c95 100644 --- a/spec/factories/snippets.rb +++ b/spec/factories/snippets.rb @@ -12,5 +12,17 @@ FactoryGirl.define do title content file_name + + trait :public do + visibility_level Snippet::PUBLIC + end + + trait :internal do + visibility_level Snippet::INTERNAL + end + + trait :private do + visibility_level Snippet::PRIVATE + end end end From b900304c3867454ac11e12d303a318ae7494f93e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Mar 2016 16:40:45 -0500 Subject: [PATCH 096/112] Make better use of the `visibility_level` factory traits --- .../projects/forks_controller_spec.rb | 2 +- spec/finders/snippets_finder_spec.rb | 25 +++++++++---------- spec/helpers/visibility_level_helper_spec.rb | 6 ++--- spec/models/project_spec.rb | 2 +- spec/services/projects/update_service_spec.rb | 4 +-- 5 files changed, 19 insertions(+), 20 deletions(-) diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb index 883bbaedd4e..70ed8f3a62e 100644 --- a/spec/controllers/projects/forks_controller_spec.rb +++ b/spec/controllers/projects/forks_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' describe Projects::ForksController do let(:user) { create(:user) } - let(:project) { create(:project, visibility_level: Project::PUBLIC) } + let(:project) { create(:project, :public) } let(:forked_project) { Projects::ForkService.new(project, user).execute } let(:group) { create(:group, owner: forked_project.creator) } diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb index 1b4ffc2d717..7fdc5e5d7aa 100644 --- a/spec/finders/snippets_finder_spec.rb +++ b/spec/finders/snippets_finder_spec.rb @@ -5,15 +5,14 @@ describe SnippetsFinder do let(:user1) { create :user } let(:group) { create :group } - let(:project1) { create(:empty_project, :public, group: group) } - let(:project2) { create(:empty_project, :private, group: group) } - + let(:project1) { create(:empty_project, :public, group: group) } + let(:project2) { create(:empty_project, :private, group: group) } context ':all filter' do before do - @snippet1 = create(:personal_snippet, visibility_level: Snippet::PRIVATE) - @snippet2 = create(:personal_snippet, visibility_level: Snippet::INTERNAL) - @snippet3 = create(:personal_snippet, visibility_level: Snippet::PUBLIC) + @snippet1 = create(:personal_snippet, :private) + @snippet2 = create(:personal_snippet, :internal) + @snippet3 = create(:personal_snippet, :public) end it "returns all private and internal snippets" do @@ -31,9 +30,9 @@ describe SnippetsFinder do context ':by_user filter' do before do - @snippet1 = create(:personal_snippet, visibility_level: Snippet::PRIVATE, author: user) - @snippet2 = create(:personal_snippet, visibility_level: Snippet::INTERNAL, author: user) - @snippet3 = create(:personal_snippet, visibility_level: Snippet::PUBLIC, author: user) + @snippet1 = create(:personal_snippet, :private, author: user) + @snippet2 = create(:personal_snippet, :internal, author: user) + @snippet3 = create(:personal_snippet, :public, author: user) end it "returns all public and internal snippets" do @@ -75,9 +74,9 @@ describe SnippetsFinder do context 'by_project filter' do before do - @snippet1 = create(:project_snippet, visibility_level: Snippet::PRIVATE, project: project1) - @snippet2 = create(:project_snippet, visibility_level: Snippet::INTERNAL, project: project1) - @snippet3 = create(:project_snippet, visibility_level: Snippet::PUBLIC, project: project1) + @snippet1 = create(:project_snippet, :private, project: project1) + @snippet2 = create(:project_snippet, :internal, project: project1) + @snippet3 = create(:project_snippet, :public, project: project1) end it "returns public snippets for unauthorized user" do @@ -93,7 +92,7 @@ describe SnippetsFinder do end it "returns all snippets for project members" do - project1.team << [user, :developer] + project1.team << [user, :developer] snippets = SnippetsFinder.new.execute(user, filter: :by_project, project: project1) expect(snippets).to include(@snippet1, @snippet2, @snippet3) end diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index aafc24397a9..cd7596a763d 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -58,7 +58,7 @@ describe VisibilityLevelHelper do describe "skip_level?" do describe "forks" do - let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } + let(:project) { create(:project, :internal) } let(:fork_project) { create(:forked_project_with_submodules) } before do @@ -74,7 +74,7 @@ describe VisibilityLevelHelper do end describe "non-forked project" do - let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } + let(:project) { create(:project, :internal) } it "skips levels" do expect(skip_level?(project, Gitlab::VisibilityLevel::PUBLIC)).to be_falsey @@ -84,7 +84,7 @@ describe VisibilityLevelHelper do end describe "Snippet" do - let(:snippet) { create(:snippet, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } + let(:snippet) { create(:snippet, :internal) } it "skips levels" do expect(skip_level?(snippet, Gitlab::VisibilityLevel::PUBLIC)).to be_falsey diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f9842d23afa..c458d9c9b1b 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -561,7 +561,7 @@ describe Project, models: true do end describe '#visibility_level_allowed?' do - let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL } + let(:project) { create(:project, :internal) } context 'when checking on non-forked project' do it { expect(project.visibility_level_allowed?(Gitlab::VisibilityLevel::PRIVATE)).to be_truthy } diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index 3c06a890163..e8b9e6b9238 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -102,8 +102,8 @@ describe Projects::UpdateService, services: true do describe :visibility_level do let(:user) { create :user, admin: true } - let(:project) { create :project, visibility_level: Gitlab::VisibilityLevel::INTERNAL } - let(:forked_project) { create :forked_project_with_submodules, visibility_level: Gitlab::VisibilityLevel::INTERNAL } + let(:project) { create(:project, :internal) } + let(:forked_project) { create(:forked_project_with_submodules, :internal) } let(:opts) { {} } before do From 0853628cbf982bc3191b3ee7efa42044fb8db9c2 Mon Sep 17 00:00:00 2001 From: Jonas Friedmann Date: Wed, 9 Mar 2016 08:26:39 +0100 Subject: [PATCH 097/112] Fix URL to upgrader guide --- doc/update/upgrader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md index fd0327686b1..5fa39ef1b0a 100644 --- a/doc/update/upgrader.md +++ b/doc/update/upgrader.md @@ -4,7 +4,7 @@ Although deprecated, if someone wants to make this script into a gem or otherwise improve it merge requests are welcome. -*Make sure you view this [upgrade guide from the 'master' branch](../../../master/doc/update/upgrader.md) for the most up to date instructions.* +*Make sure you view this [upgrade guide from the 'master' branch](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md) for the most up to date instructions.* GitLab Upgrader - a ruby script that allows you easily upgrade GitLab to latest minor version. From 9d9d2fbbc23e030416a6ebab556e87cf7014a057 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 9 Mar 2016 12:00:17 +0100 Subject: [PATCH 098/112] Prevent performance issues --- app/views/projects/commits/_commit_list.html.haml | 2 +- app/views/projects/commits/_commits.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/commits/_commit_list.html.haml b/app/views/projects/commits/_commit_list.html.haml index c0988eca6de..bac9e244d36 100644 --- a/app/views/projects/commits/_commit_list.html.haml +++ b/app/views/projects/commits/_commit_list.html.haml @@ -9,6 +9,6 @@ - commits.each do |commit| = render "projects/commits/inline_commit", commit: commit, project: @project %li.warning-row.unstyled - #{number_with_delimiter(hidden)} additional commits have been omitted. + #{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues. - else %ul.well-list= render commits, project: @project diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index 787fe443ac8..a7e3c2478c2 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -18,4 +18,4 @@ - if hidden > 0 .alert.alert-warning - #{number_with_delimiter(hidden)} additional commits have been omitted. + #{number_with_delimiter(hidden)} additional commits have been omitted to prevent performance issues. From c666441d405c1275ef7bcc791153f09d819cc89d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 9 Mar 2016 10:41:41 +0100 Subject: [PATCH 099/112] Added main_language to the schema This wasn't included in gitlab-org/gitlab-ce!3000. --- db/schema.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/schema.rb b/db/schema.rb index 2d6b9b5a4cc..4f56f3970db 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -697,6 +697,7 @@ ActiveRecord::Schema.define(version: 20160305220806) do t.integer "build_timeout", default: 3600, null: false t.boolean "pending_delete", default: false t.boolean "public_builds", default: true, null: false + t.string "main_language" end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree From 640bf8ab7212988fda0189e1534d46dcbf0c126f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 12 Nov 2015 10:52:20 +0100 Subject: [PATCH 100/112] Retry failed tests --- .gitlab-ci.yml | 2 ++ Gemfile | 1 + Gemfile.lock | 3 +++ spec/spec_helper.rb | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c477721f9da..f77b5bdc955 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,6 +12,8 @@ cache: variables: MYSQL_ALLOW_EMPTY_PASSWORD: "1" + # retry tests only in CI environment + RSPEC_RETRY_RETRY_COUNT: "3" before_script: - source ./scripts/prepare_build.sh diff --git a/Gemfile b/Gemfile index c66ef3cffad..a5c244fb7b6 100644 --- a/Gemfile +++ b/Gemfile @@ -263,6 +263,7 @@ group :development, :test do gem 'database_cleaner', '~> 1.4.0' gem 'factory_girl_rails', '~> 4.6.0' gem 'rspec-rails', '~> 3.3.0' + gem 'rspec-retry' gem 'spinach-rails', '~> 0.2.1' # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826) diff --git a/Gemfile.lock b/Gemfile.lock index 22c86e4ae8f..7f3c5911e5f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -679,6 +679,8 @@ GEM rspec-expectations (~> 3.3.0) rspec-mocks (~> 3.3.0) rspec-support (~> 3.3.0) + rspec-retry (0.4.5) + rspec-core rspec-support (3.3.0) rubocop (0.35.1) astrolabe (~> 1.3) @@ -999,6 +1001,7 @@ DEPENDENCIES rouge (~> 1.10.1) rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) + rspec-retry rubocop (~> 0.35.0) ruby-fogbugz (~> 0.2.1) sanitize (~> 2.0) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8f381f46e57..159fb964171 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -15,6 +15,7 @@ require 'rspec/rails' require 'shoulda/matchers' require 'sidekiq/testing/inline' require 'benchmark/ips' +require 'rspec/retry' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -25,6 +26,9 @@ RSpec.configure do |config| config.use_instantiated_fixtures = false config.mock_with :rspec + config.verbose_retry = true + config.display_try_failure_messages = true + config.include Devise::TestHelpers, type: :controller config.include LoginHelpers, type: :feature config.include LoginHelpers, type: :request From af43401602d637c5b91def323ba3a0bac824b785 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 9 Mar 2016 14:12:08 +0100 Subject: [PATCH 101/112] Retry spinach tests --- Gemfile | 1 + Gemfile.lock | 3 ++ features/support/env.rb | 1 + features/support/rerun.rb | 14 +++++++++ lib/tasks/spinach.rake | 64 +++++++++++++++++++++------------------ 5 files changed, 54 insertions(+), 29 deletions(-) create mode 100644 features/support/rerun.rb diff --git a/Gemfile b/Gemfile index a5c244fb7b6..9aa76f92aa3 100644 --- a/Gemfile +++ b/Gemfile @@ -265,6 +265,7 @@ group :development, :test do gem 'rspec-rails', '~> 3.3.0' gem 'rspec-retry' gem 'spinach-rails', '~> 0.2.1' + gem 'spinach-rerun-reporter', '~> 0.0.2' # Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826) gem 'minitest', '~> 5.7.0' diff --git a/Gemfile.lock b/Gemfile.lock index 7f3c5911e5f..dcfef4cd6b9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -766,6 +766,8 @@ GEM capybara (>= 2.0.0) railties (>= 3) spinach (>= 0.4) + spinach-rerun-reporter (0.0.2) + spinach (~> 0.8) spring (1.6.4) spring-commands-rspec (1.0.4) spring (>= 0.9.1) @@ -1020,6 +1022,7 @@ DEPENDENCIES six (~> 0.2.0) slack-notifier (~> 1.2.0) spinach-rails (~> 0.2.1) + spinach-rerun-reporter (~> 0.0.2) spring (~> 1.6.4) spring-commands-rspec (~> 1.0.4) spring-commands-spinach (~> 1.0.0) diff --git a/features/support/env.rb b/features/support/env.rb index 62c80b9c948..357d164d87f 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -14,6 +14,7 @@ require 'sidekiq/testing/inline' require_relative 'capybara' require_relative 'db_cleaner' +require_relative 'rerun' %w(select2_helper test_env repo_helpers).each do |f| require Rails.root.join('spec', 'support', f) diff --git a/features/support/rerun.rb b/features/support/rerun.rb new file mode 100644 index 00000000000..8b176c5be89 --- /dev/null +++ b/features/support/rerun.rb @@ -0,0 +1,14 @@ +# The spinach-rerun-reporter doesn't define the on_undefined_step +# See it here: https://github.com/javierav/spinach-rerun-reporter/blob/master/lib/spinach/reporter/rerun.rb +module Spinach + class Reporter + class Rerun + def on_undefined_step(step_data, failure, step_definitions = nil) + super step_data, failure, step_definitions + + # save feature file and scenario line + @rerun << "#{current_feature.filename}:#{current_scenario.line}" + end + end + end +end diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake index 3acfc6e2075..01d23b89bb7 100644 --- a/lib/tasks/spinach.rake +++ b/lib/tasks/spinach.rake @@ -4,53 +4,59 @@ namespace :spinach do namespace :project do desc "GitLab | Spinach | Run project commits, issues and merge requests spinach features" task :half do - cmds = [ - %W(rake gitlab:setup), - %W(spinach --tags @project_commits,@project_issues,@project_merge_requests), - ] - run_commands(cmds) + run_spinach_tests('@project_commits,@project_issues,@project_merge_requests') end desc "GitLab | Spinach | Run remaining project spinach features" task :rest do - cmds = [ - %W(rake gitlab:setup), - %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests), - ] - run_commands(cmds) + run_spinach_tests('~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests') end end desc "GitLab | Spinach | Run project spinach features" task :project do - cmds = [ - %W(rake gitlab:setup), - %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets), - ] - run_commands(cmds) + run_spinach_tests('~@admin,~@dashboard,~@profile,~@public,~@snippets') end desc "GitLab | Spinach | Run other spinach features" task :other do - cmds = [ - %W(rake gitlab:setup), - %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets), - ] - run_commands(cmds) + run_spinach_tests('@admin,@dashboard,@profile,@public,@snippets') + end + + desc "GitLab | Spinach | Run other spinach features" + task :builds do + run_spinach_tests('@builds') end end desc "GitLab | Run spinach" task :spinach do - cmds = [ - %W(rake gitlab:setup), - %W(spinach), - ] - run_commands(cmds) + run_spinach_tests(nil) end -def run_commands(cmds) - cmds.each do |cmd| - system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!") - end +def run_command(cmd) + system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) +end + +def run_spinach_command(args) + run_command(%w(spinach -r rerun) + args) +end + +def run_spinach_tests(tags) + #run_command(%w(rake gitlab:setup)) or raise('gitlab:setup failed!') + + success = run_spinach_command(%W(--tags #{tags})) + 3.times do |_| + break if success + break unless File.exists?('tmp/spinach-rerun.txt') + + tests = File.foreach('tmp/spinach-rerun.txt').map(&:chomp) + puts '' + puts "Spinach tests for #{tags}: Retrying tests... #{tests}".red + puts '' + sleep(3) + success = run_spinach_command(tests) + end + + raise("spinach tests for #{tags} failed!") unless success end From 978703abe287519784b8258296ca71d783f67794 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 9 Mar 2016 14:12:29 +0100 Subject: [PATCH 102/112] Retry apt-get installation and bundle install --- .gitlab-ci.yml | 2 +- scripts/prepare_build.sh | 30 +++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f77b5bdc955..ffefeb6dfd8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,7 +23,7 @@ before_script: - cp config/gitlab.yml.example config/gitlab.yml - touch log/application.log - touch log/test.log - - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" + - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" - RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate stages: diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index b6f076a90c3..bce9bce99c3 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -2,15 +2,27 @@ if [ -f /.dockerinit ]; then mkdir -p vendor - if [ ! -e vendor/phantomjs_1.9.8-0jessie_amd64.deb ]; then - wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb - mv phantomjs_1.9.8-0jessie_amd64.deb vendor/ - fi - dpkg -i vendor/phantomjs_1.9.8-0jessie_amd64.deb - apt-get update -qq - apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \ - libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip + # Install phantomjs package + pushd vendor + if [ ! -e phantomjs_1.9.8-0jessie_amd64.deb ]; then + wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb + fi + dpkg -i phantomjs_1.9.8-0jessie_amd64.deb + popd + + # Try to install packages + for i in $(seq 1 3); do + apt-get update -yqqq || true + + if apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \ + libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip; then + break + fi + + sleep 3s + echo "Retrying package installation..." + fi cp config/database.yml.mysql config/database.yml sed -i 's/username:.*/username: root/g' config/database.yml @@ -20,7 +32,7 @@ if [ -f /.dockerinit ]; then cp config/resque.yml.example config/resque.yml sed -i 's/localhost/redis/g' config/resque.yml - export FLAGS=(--path vendor) + export FLAGS=(--path vendor --retry 3) else export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin cp config/database.yml.mysql config/database.yml From b651cd227efae152ef56c112576c5464af4ee725 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 9 Mar 2016 14:26:49 +0100 Subject: [PATCH 103/112] Fix script error --- scripts/prepare_build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index bce9bce99c3..82de51a9a2e 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -22,7 +22,7 @@ if [ -f /.dockerinit ]; then sleep 3s echo "Retrying package installation..." - fi + done cp config/database.yml.mysql config/database.yml sed -i 's/username:.*/username: root/g' config/database.yml From 9e342fb00a50cbb483013acd4644bd31c440f99a Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Mar 2016 11:06:58 -0300 Subject: [PATCH 104/112] Destroy all related todos when removing a project --- app/models/project.rb | 1 + spec/models/project_spec.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/app/models/project.rb b/app/models/project.rb index 3235a1cee50..426464dee81 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -151,6 +151,7 @@ class Project < ActiveRecord::Base has_many :releases, dependent: :destroy has_many :lfs_objects_projects, dependent: :destroy has_many :lfs_objects, through: :lfs_objects_projects + has_many :todos, dependent: :destroy has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index c458d9c9b1b..2fa38a5d3d3 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -68,6 +68,7 @@ describe Project, models: true do it { is_expected.to have_many(:runners) } it { is_expected.to have_many(:variables) } it { is_expected.to have_many(:triggers) } + it { is_expected.to have_many(:todos).dependent(:destroy) } end describe 'modules' do From 22140ced0b881617ef376e21ba28c9bb9b6c5cc4 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Mar 2016 11:18:13 -0300 Subject: [PATCH 105/112] Remove invalid todos from database --- db/migrate/20160309140734_fix_todos.rb | 16 ++++++++++++++++ db/schema.rb | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20160309140734_fix_todos.rb diff --git a/db/migrate/20160309140734_fix_todos.rb b/db/migrate/20160309140734_fix_todos.rb new file mode 100644 index 00000000000..ebe0fc82305 --- /dev/null +++ b/db/migrate/20160309140734_fix_todos.rb @@ -0,0 +1,16 @@ +class FixTodos < ActiveRecord::Migration + def up + execute <<-SQL + DELETE FROM todos + WHERE todos.target_type IN ('Commit', 'ProjectSnippet') + OR NOT EXISTS ( + SELECT * + FROM projects + WHERE projects.id = todos.project_id + ) + SQL + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index 4f56f3970db..a74b86d8e2f 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: 20160305220806) do +ActiveRecord::Schema.define(version: 20160309140734) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From cac83cead523bc5a314950c2168fcebf8da8d405 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Mar 2016 11:18:37 -0300 Subject: [PATCH 106/112] Update CHANGELOG --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 10984ebd190..3c19e8477fe 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,9 @@ v 8.6.0 (unreleased) - Add main language of a project in the list of projects (Tiago Botelho) - Add ability to show archived projects on dashboard, explore and group pages +v 8.5.5 + - Fix error 500 in Todos + v 8.5.4 - Do not cache requests for badges (including builds badge) From 96d35c5975fe8c5ff0a6306b8da5a623cfcb47b6 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 9 Mar 2016 13:49:05 +0100 Subject: [PATCH 107/112] Fixed part of the GitPushService specs These were broken by commit 21a05328ffd5cb9130ae516faa7dd672cacba90c. Two JIRA tests remain broken but I can't quite figure out how to fix them. --- spec/services/git_push_service_spec.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index f5c51e46e8b..7fee16a0586 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -271,22 +271,24 @@ describe GitPushService, services: true do allow(project.repository).to receive(:commits_between). and_return([closing_commit]) + + project.team << [commit_author, :master] end context "to default branches" do it "closes issues" do - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, @oldrev, @newrev, @ref ) expect(Issue.find(issue.id)).to be_closed end it "adds a note indicating that the issue is now closed" do expect(SystemNoteService).to receive(:change_status).with(issue, project, commit_author, "closed", closing_commit) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, @oldrev, @newrev, @ref ) end it "doesn't create additional cross-reference notes" do expect(SystemNoteService).not_to receive(:cross_reference) - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, @oldrev, @newrev, @ref ) end it "doesn't close issues when external issue tracker is in use" do @@ -294,7 +296,7 @@ describe GitPushService, services: true do # The push still shouldn't create cross-reference notes. expect do - execute_service(project, user, @oldrev, @newrev, 'refs/heads/hurf' ) + execute_service(project, commit_author, @oldrev, @newrev, 'refs/heads/hurf' ) end.not_to change { Note.where(project_id: project.id, system: true).count } end end @@ -316,7 +318,6 @@ describe GitPushService, services: true do end end - # EE-only tests context "for jira issue tracker" do include JiraServiceHelper From 53719ecb80b757a2096e55cd3da995ac9db8d3b8 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 9 Mar 2016 15:38:07 +0100 Subject: [PATCH 108/112] Handle permissions for ExternalIssue instances This fixes the remainder of the GitPushService specs. --- app/models/ability.rb | 5 +++++ spec/services/git_push_service_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index f34554d557c..fe9e0aab717 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -9,6 +9,7 @@ class Ability when CommitStatus then commit_status_abilities(user, subject) when Project then project_abilities(user, subject) when Issue then issue_abilities(user, subject) + when ExternalIssue then external_issue_abilities(user, subject) when Note then note_abilities(user, subject) when ProjectSnippet then project_snippet_abilities(user, subject) when PersonalSnippet then personal_snippet_abilities(user, subject) @@ -424,6 +425,10 @@ class Ability end end + def external_issue_abilities(user, subject) + project_abilities(user, subject.project) + end + private def named_abilities(name) diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 7fee16a0586..82813cee227 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -367,7 +367,7 @@ describe GitPushService, services: true do } }.to_json - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, @oldrev, @newrev, @ref ) expect(WebMock).to have_requested(:post, jira_api_transition_url).with( body: transition_body ).once @@ -378,7 +378,7 @@ describe GitPushService, services: true do body: "Issue solved with [#{closing_commit.id}|http://localhost/#{project.path_with_namespace}/commit/#{closing_commit.id}]." }.to_json - execute_service(project, user, @oldrev, @newrev, @ref ) + execute_service(project, commit_author, @oldrev, @newrev, @ref ) expect(WebMock).to have_requested(:post, jira_api_comment_url).with( body: comment_body ).once From e443c7c6d861391f906e6124188aa305b59a9e63 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Mar 2016 16:13:51 -0500 Subject: [PATCH 109/112] Add a `gemojione:sprite` Rake task This task will generate a standard and Retina sprite of all of the current Gemojione Emojis, with the accompanying SCSS map. It will not appear in `rake -T` output, and the dependent gems are not included in the Gemfile by default, because this task will only be needed occasionally. [ci skip] --- lib/tasks/gemojione.rake | 121 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 lib/tasks/gemojione.rake diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake new file mode 100644 index 00000000000..ebe301c1fc7 --- /dev/null +++ b/lib/tasks/gemojione.rake @@ -0,0 +1,121 @@ +# This task will generate a standard and Retina sprite of all of the current +# Gemojione Emojis, with the accompanying SCSS map. +# +# It will not appear in `rake -T` output, and the dependent gems are not +# included in the Gemfile by default, because this task will only be needed +# occasionally, such as when new Emojis are added to Gemojione. + +begin + require 'sprite_factory' + require 'rmagick' +rescue LoadError + # noop +end + +namespace :gemojione do + task sprite: :environment do + check_requirements! + + SIZE = 20 + RETINA = SIZE * 2 + + Dir.mktmpdir do |tmpdir| + # Copy the Gemojione assets to the temporary folder for resizing + FileUtils.cp_r(Gemojione.index.images_path, tmpdir) + + Dir.chdir(tmpdir) do + Dir["**/*.png"].each do |png| + resize!(File.join(tmpdir, png), SIZE) + end + end + + style_path = Rails.root.join(*%w(app assets stylesheets pages emojis.scss)) + + # Combine the resized assets into a packed sprite and re-generate the SCSS + SpriteFactory.cssurl = "image-url('$IMAGE')" + SpriteFactory.run!(File.join(tmpdir, 'images'), { + output_style: style_path, + output_image: "app/assets/images/emoji.png", + selector: '.emoji-', + style: :scss, + nocomments: true, + pngcrush: true, + layout: :packed + }) + + # SpriteFactory's SCSS is a bit too verbose for our purposes here, so + # let's simplify it + system(%Q(sed -i '' "s/width: #{SIZE}px; height: #{SIZE}px; background: image-url('emoji.png')/background-position:/" #{style_path})) + system(%Q(sed -i '' "s/ no-repeat//" #{style_path})) + + # Append a generic rule that applies to all Emojis + File.open(style_path, 'a') do |f| + f.puts + f.puts <<-CSS.strip_heredoc + .emoji-icon { + background-image: image-url('emoji.png'); + background-repeat: no-repeat; + height: #{SIZE}px; + width: #{SIZE}px; + + @media only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and (min--moz-device-pixel-ratio: 2), + only screen and (-o-min-device-pixel-ratio: 2/1), + only screen and (min-device-pixel-ratio: 2), + only screen and (min-resolution: 192dpi), + only screen and (min-resolution: 2dppx) { + background-image: image-url('emoji@2x.png'); + background-size: 840px 820px; + } + } + CSS + end + end + + # Now do it again but for Retina + Dir.mktmpdir do |tmpdir| + # Copy the Gemojione assets to the temporary folder for resizing + FileUtils.cp_r(Gemojione.index.images_path, tmpdir) + + Dir.chdir(tmpdir) do + Dir["**/*.png"].each do |png| + resize!(File.join(tmpdir, png), RETINA) + end + end + + # Combine the resized assets into a packed sprite and re-generate the SCSS + SpriteFactory.run!(File.join(tmpdir, 'images'), { + output_image: "app/assets/images/emoji@2x.png", + style: false, + nocomments: true, + pngcrush: true, + layout: :packed + }) + end + end + + def check_requirements! + return if defined?(SpriteFactory) && defined?(Magick) + + puts <<-MSG.strip_heredoc + This task is disabled by default and should only be run when the Gemojione + gem is updated with new Emojis. + + To enable this task, *temporarily* add the following lines to Gemfile and + re-bundle: + + gem 'sprite-factory' + gem 'rmagick' + MSG + + exit 1 + end + + def resize!(image_path, size) + # Resize the image in-place, save it, and free the object + image = Magick::Image.read(image_path).first + image.resize!(size, size) + image.write(image_path) { self.quality = 100 } + image.destroy! + end +end From 7500a12aae91231aba1021df2034d724126ba55f Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Mar 2016 14:47:04 -0300 Subject: [PATCH 110/112] Avoid error 500 when todo author was removed --- app/helpers/application_helper.rb | 2 +- app/views/dashboard/todos/_todo.html.haml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f0aa2b57121..368969c6472 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -72,7 +72,7 @@ module ApplicationHelper if user_or_email.is_a?(User) user = user_or_email else - user = User.find_by(email: user_or_email.downcase) + user = User.find_by(email: user_or_email.try(:downcase)) end if user diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index f878d36e739..45cfe3da188 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -4,7 +4,10 @@ .todo-title %span.author-name - = link_to_author todo + - if todo.author + = link_to_author(todo) + - else + (removed) %span.todo-label = todo_action_name(todo) = todo_target_link(todo) From b100a9f206ce16391038bf7c2b0bd247b1bfe822 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Mar 2016 16:21:53 -0300 Subject: [PATCH 111/112] Update CHANGELOG --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3c19e8477fe..75ea12320b3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,7 +25,8 @@ v 8.6.0 (unreleased) - Add ability to show archived projects on dashboard, explore and group pages v 8.5.5 - - Fix error 500 in Todos + - Ensure removing a project removes associated Todo entries. + - Prevent a 500 error in Todos when author was removed. v 8.5.4 - Do not cache requests for badges (including builds badge) From 3da55e37d0d3025893fedb4b87bebd0f2b4635f1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 9 Mar 2016 21:22:25 +0100 Subject: [PATCH 112/112] Retry bundler and apt-get --- .gitlab-ci.yml | 4 ++-- scripts/prepare_build.sh | 24 +++++++++++++----------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ffefeb6dfd8..d21785f7af2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,11 +19,11 @@ before_script: - source ./scripts/prepare_build.sh - ruby -v - which ruby - - gem install bundler --no-ri --no-rdoc + - retry gem install bundler --no-ri --no-rdoc - cp config/gitlab.yml.example config/gitlab.yml - touch log/application.log - touch log/test.log - - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" + - retry bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" - RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate stages: diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 82de51a9a2e..4a7ee7dbb64 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -1,5 +1,16 @@ #!/bin/bash +retry() { + for i in $(seq 1 3); do + if eval "$@"; then + return 0 + fi + sleep 3s + echo "Retrying..." + done + return 1 +} + if [ -f /.dockerinit ]; then mkdir -p vendor @@ -12,17 +23,8 @@ if [ -f /.dockerinit ]; then popd # Try to install packages - for i in $(seq 1 3); do - apt-get update -yqqq || true - - if apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \ - libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip; then - break - fi - - sleep 3s - echo "Retrying package installation..." - done + retry 'apt-get update -yqqq; apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \ + libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip' cp config/database.yml.mysql config/database.yml sed -i 's/username:.*/username: root/g' config/database.yml