summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/u2f/authenticate.js8
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss6
-rw-r--r--app/assets/stylesheets/framework/header.scss2
-rw-r--r--app/assets/stylesheets/pages/boards.scss4
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml5
-rw-r--r--app/views/u2f/_authenticate.html.haml3
-rw-r--r--changelogs/unreleased/40005-u2f-unspported-browsers.yml5
-rw-r--r--changelogs/unreleased/47274-help-users-find-our-contributing-page.yml5
-rw-r--r--changelogs/unreleased/bjk-48176_ruby_gc.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml5
-rw-r--r--changelogs/unreleased/fix-boards-issue-highlight.yml5
-rw-r--r--doc/administration/job_traces.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md14
-rw-r--r--doc/development/utilities.md41
-rw-r--r--doc/install/kubernetes/gitlab_chart.md15
-rw-r--r--doc/install/kubernetes/index.md4
-rw-r--r--doc/topics/autodevops/index.md12
-rw-r--r--doc/user/admin_area/settings/sign_up_restrictions.md1
-rw-r--r--doc/user/project/img/group_issue_board.pngbin0 -> 163417 bytes
-rw-r--r--doc/user/project/img/issue_board.pngbin82592 -> 100684 bytes
-rw-r--r--doc/user/project/img/issue_board_add_list.pngbin17312 -> 6404 bytes
-rw-r--r--doc/user/project/img/issue_board_assignee_lists.pngbin0 -> 134674 bytes
-rw-r--r--doc/user/project/img/issue_board_creation.pngbin0 -> 108674 bytes
-rw-r--r--doc/user/project/img/issue_board_edit_button.pngbin0 -> 108168 bytes
-rw-r--r--doc/user/project/img/issue_board_focus_mode.gifbin0 -> 1043366 bytes
-rw-r--r--doc/user/project/img/issue_board_move_issue_card_list.pngbin36747 -> 13670 bytes
-rw-r--r--doc/user/project/img/issue_board_system_notes.pngbin4899 -> 4893 bytes
-rw-r--r--doc/user/project/img/issue_board_view_scope.pngbin0 -> 63542 bytes
-rw-r--r--doc/user/project/img/issue_board_welcome_message.pngbin26533 -> 13519 bytes
-rw-r--r--doc/user/project/img/issue_boards_add_issues_modal.pngbin29176 -> 12421 bytes
-rw-r--r--doc/user/project/img/issue_boards_multiple.pngbin0 -> 6092 bytes
-rw-r--r--doc/user/project/img/issue_boards_remove_issue.pngbin135168 -> 39357 bytes
-rw-r--r--doc/user/project/import/bitbucket.md4
-rw-r--r--doc/user/project/issue_board.md212
-rw-r--r--doc/user/project/issues/deleting_issues.md4
-rw-r--r--doc/workflow/lfs/lfs_administration.md95
-rw-r--r--lib/gitlab/cache/request_cache.rb37
-rw-r--r--lib/gitlab/git/commit.rb10
-rw-r--r--lib/gitlab/git/gitlab_projects.rb25
-rw-r--r--lib/gitlab/git/repository.rb28
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb26
-rw-r--r--qa/qa/specs/features/repository/protected_branches_spec.rb24
-rw-r--r--rubocop/cop/migration/update_large_table.rb15
-rw-r--r--spec/features/admin/admin_groups_spec.rb1
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb36
-rw-r--r--spec/features/users/signup_spec.rb9
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js84
-rw-r--r--spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb8
-rw-r--r--spec/rubocop/cop/migration/update_large_table_spec.rb20
-rw-r--r--spec/support/matchers/match_ids.rb7
52 files changed, 550 insertions, 241 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index 3c619b2e534..fdc8f54e9c9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -867,7 +867,7 @@ GEM
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
- sprockets (3.7.1)
+ sprockets (3.7.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
diff --git a/app/assets/javascripts/u2f/authenticate.js b/app/assets/javascripts/u2f/authenticate.js
index 96af6d2fcca..78fd7ad441f 100644
--- a/app/assets/javascripts/u2f/authenticate.js
+++ b/app/assets/javascripts/u2f/authenticate.js
@@ -11,7 +11,6 @@ export default class U2FAuthenticate {
constructor(container, form, u2fParams, fallbackButton, fallbackUI) {
this.u2fUtils = null;
this.container = container;
- this.renderNotSupported = this.renderNotSupported.bind(this);
this.renderAuthenticated = this.renderAuthenticated.bind(this);
this.renderError = this.renderError.bind(this);
this.renderInProgress = this.renderInProgress.bind(this);
@@ -41,7 +40,6 @@ export default class U2FAuthenticate {
this.signRequests = u2fParams.sign_requests.map(request => _(request).omit('challenge'));
this.templates = {
- notSupported: '#js-authenticate-u2f-not-supported',
setup: '#js-authenticate-u2f-setup',
inProgress: '#js-authenticate-u2f-in-progress',
error: '#js-authenticate-u2f-error',
@@ -55,7 +53,7 @@ export default class U2FAuthenticate {
this.u2fUtils = utils;
this.renderInProgress();
})
- .catch(() => this.renderNotSupported());
+ .catch(() => this.switchToFallbackUI());
}
authenticate() {
@@ -96,10 +94,6 @@ export default class U2FAuthenticate {
this.fallbackButton.classList.add('hidden');
}
- renderNotSupported() {
- return this.renderTemplate('notSupported');
- }
-
switchToFallbackUI() {
this.fallbackButton.classList.add('hidden');
this.container[0].classList.add('hidden');
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 0d8e867f41d..ba1f0a975a9 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -173,6 +173,12 @@ table {
display: none;
}
+h3.popover-header {
+ // Default bootstrap popovers use <h3>
+ // which we default to having a top margin
+ margin-top: 0;
+}
+
// Add to .label so that old system notes that are saved to the db
// will still receive the correct styling
.badge,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 2fa71b23314..91a9b956d9d 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -527,7 +527,7 @@
.header-user {
.dropdown-menu {
width: auto;
- min-width: 160px;
+ min-width: unset;
margin-top: 4px;
color: $gl-text-color;
left: auto;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 7c1d1626f1c..750d2c8b990 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -290,10 +290,6 @@
&.is-active,
&.is-active .board-card-assignee:hover a {
background-color: $row-hover;
-
- &:first-child:not(:only-child) {
- box-shadow: -10px 0 10px 1px $row-hover;
- }
}
.badge {
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 24b6c490a5a..cdfd45fceb1 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -17,6 +17,11 @@
= link_to _("Help"), help_path
- if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile)
%li.divider
+ %li
+ = link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap' do
+ = _("Contribute to GitLab")
+ = icon('external-link')
+ %li.divider
- if current_user_menu?(:sign_out)
%li
= link_to _("Sign out"), destroy_user_session_path, class: "sign-out-link"
diff --git a/app/views/u2f/_authenticate.html.haml b/app/views/u2f/_authenticate.html.haml
index 7eb221620ad..1c788b9a737 100644
--- a/app/views/u2f/_authenticate.html.haml
+++ b/app/views/u2f/_authenticate.html.haml
@@ -2,9 +2,6 @@
%a.btn.btn-block.btn-info#js-login-2fa-device{ href: '#' } Sign in via 2FA code
-# haml-lint:disable InlineJavaScript
-%script#js-authenticate-u2f-not-supported{ type: "text/template" }
- %p Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer).
-
%script#js-authenticate-u2f-in-progress{ type: "text/template" }
%p Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.
diff --git a/changelogs/unreleased/40005-u2f-unspported-browsers.yml b/changelogs/unreleased/40005-u2f-unspported-browsers.yml
new file mode 100644
index 00000000000..eb5ff99246e
--- /dev/null
+++ b/changelogs/unreleased/40005-u2f-unspported-browsers.yml
@@ -0,0 +1,5 @@
+---
+title: Improve U2F workflow when using unsupported browsers
+merge_request: 19938
+author: Jan Beckmann
+type: changed
diff --git a/changelogs/unreleased/47274-help-users-find-our-contributing-page.yml b/changelogs/unreleased/47274-help-users-find-our-contributing-page.yml
new file mode 100644
index 00000000000..ed13c917a2e
--- /dev/null
+++ b/changelogs/unreleased/47274-help-users-find-our-contributing-page.yml
@@ -0,0 +1,5 @@
+---
+title: Add a link to the contributing page in the user dropdown
+merge_request: 19708
+author:
+type: added
diff --git a/changelogs/unreleased/bjk-48176_ruby_gc.yml b/changelogs/unreleased/bjk-48176_ruby_gc.yml
new file mode 100644
index 00000000000..45c6338df81
--- /dev/null
+++ b/changelogs/unreleased/bjk-48176_ruby_gc.yml
@@ -0,0 +1,5 @@
+---
+title: Cleanup Prometheus ruby metrics
+merge_request: 20039
+author: Ben Kochie
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml b/changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml
new file mode 100644
index 00000000000..403c3764321
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-fix-data-store-spec.yml
@@ -0,0 +1,5 @@
+---
+title: '[Rails5] Fix "-1 is not a valid data_store"'
+merge_request: 19917
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/fix-boards-issue-highlight.yml b/changelogs/unreleased/fix-boards-issue-highlight.yml
new file mode 100644
index 00000000000..0cc3faa81ca
--- /dev/null
+++ b/changelogs/unreleased/fix-boards-issue-highlight.yml
@@ -0,0 +1,5 @@
+---
+title: Fix boards issue highlight
+merge_request: 20063
+author: George Tsiolis
+type: changed
diff --git a/doc/administration/job_traces.md b/doc/administration/job_traces.md
index f0b2054a7f3..a5cd2b642dc 100644
--- a/doc/administration/job_traces.md
+++ b/doc/administration/job_traces.md
@@ -134,4 +134,4 @@ We're currently evaluating this feature on dev.gitalb.org or staging.gitlab.com
- TBD
-[ce-44935]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169
+[ce-18169]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/18169 \ No newline at end of file
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 411a0fae93f..cea6764df41 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -49,6 +49,20 @@ The following metrics are available:
| filesystem_circuitbreaker | Gauge | 9.5 | Whether or not the circuit for a certain shard is broken or not |
| circuitbreaker_storage_check_duration_seconds | Histogram | 10.3 | Time a single storage probe took |
+### Ruby metrics
+
+Some basic Ruby runtime metrics are available:
+
+| Metric | Type | Since | Description |
+|:-------------------------------------- |:--------- |:----- |:----------- |
+| ruby_gc_duration_seconds_total | Counter | 11.1 | Time spent by Ruby in GC |
+| ruby_gc_stat_... | Gauge | 11.1 | Various metrics from [GC.stat] |
+| ruby_file_descriptors | Gauge | 11.1 | File descriptors per process |
+| ruby_memory_bytes | Gauge | 11.1 | Memory usage by process |
+| ruby_sampler_duration_seconds_total | Counter | 11.1 | Time spent collecting stats |
+
+[GC.stat]: https://ruby-doc.org/core-2.3.0/GC.html#method-c-stat
+
## Metrics shared directory
GitLab's Prometheus client requires a directory to store metrics data shared between multi-process services.
diff --git a/doc/development/utilities.md b/doc/development/utilities.md
index 8f9aff1a35f..0d074a3ef05 100644
--- a/doc/development/utilities.md
+++ b/doc/development/utilities.md
@@ -135,3 +135,44 @@ We developed a number of utilities to ease development.
Find.new.clear_memoization(:result)
```
+
+## [`RequestCache`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/cache/request_cache.rb)
+
+This module provides a simple way to cache values in RequestStore,
+and the cache key would be based on the class name, method name,
+optionally customized instance level values, optionally customized
+method level values, and optional method arguments.
+
+A simple example that only uses the instance level customised values:
+
+``` ruby
+class UserAccess
+ extend Gitlab::Cache::RequestCache
+
+ request_cache_key do
+ [user&.id, project&.id]
+ end
+
+ request_cache def can_push_to_branch?(ref)
+ # ...
+ end
+end
+```
+
+This way, the result of `can_push_to_branch?` would be cached in
+`RequestStore.store` based on the cache key. If `RequestStore` is not
+currently active, then it would be stored in a hash saved in an
+instance variable, so the cache logic would be the same.
+
+We can also set different strategies for different methods:
+
+``` ruby
+class Commit
+ extend Gitlab::Cache::RequestCache
+
+ def author
+ User.find_by_any_email(author_email.downcase)
+ end
+ request_cache(:author) { author_email.downcase }
+end
+```
diff --git a/doc/install/kubernetes/gitlab_chart.md b/doc/install/kubernetes/gitlab_chart.md
index 3d16b3cadd8..48f3df1925a 100644
--- a/doc/install/kubernetes/gitlab_chart.md
+++ b/doc/install/kubernetes/gitlab_chart.md
@@ -32,7 +32,7 @@ Currently out of scope:
In order to deploy GitLab on Kubernetes, a few prerequisites are required.
1. `helm` and `kubectl` [installed on your computer](preparation/tools_installation.md).
-1. A Kubernetes cluster, version 1.8 or higher. 4vCPU and 16GB of RAM is recommended.
+1. A Kubernetes cluster, version 1.8 or higher. 6vCPU and 16GB of RAM is recommended.
* [Google GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/creating-a-container-cluster)
* [Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html)
* [Microsoft AKS](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal)
@@ -64,7 +64,7 @@ run helm. In this example, we've named our helm release "gitlab".
```
helm repo add gitlab https://charts.gitlab.io/
-helm dependencies update
+helm update
helm upgrade --install gitlab gitlab/gitlab \
--timeout 600 \
--set global.hosts.domain=example.local \
@@ -81,15 +81,16 @@ the deployment is taking place if you run the command in another terminal.
### Initial login
-You can access the GitLab instance by visiting the domain specified during
-installation. If you manually created the secret for initial root password, you
-can use that to sign in as `root` user. If not, Gitlab would've automatically
+You can access the GitLab instance by visiting the domain name beginning with `gitlab.` followed by the domain specified during installation. From the example above, the URL would be `https://gitlab.example.local`.
+
+If you manually created the secret for initial root password, you
+can use that to sign in as `root` user. If not, Gitlab automatically
created a random password for `root` user. This can be extracted by the
following command (replace `<name>` by name of the release - which is `gitlab`
-if you used the command above)
+if you used the command above).
```
-kubectl get secret <name>-gitlab-initial-root-password -ojsonpath={.data.password} | base64 -d
+kubectl get secret <name>-gitlab-initial-root-password -ojsonpath={.data.password} | base64 --decode
```
## Outgoing email
diff --git a/doc/install/kubernetes/index.md b/doc/install/kubernetes/index.md
index b1cecf46697..6419a9dcb69 100644
--- a/doc/install/kubernetes/index.md
+++ b/doc/install/kubernetes/index.md
@@ -14,7 +14,7 @@ should be deployed, upgraded, and configured.
## Chart Overview
-* **[GitLab Chart](https://gitlab.com/charts/gitlab/blob/master/README.md)**: The recommended GitLab chart, currently in beta. Support large deployments with horizontal scaling of individual GitLab components, and does not require NFS.
+* **[GitLab Chart](gitlab_chart.html)**: The recommended GitLab chart, currently in beta. Supports large deployments with horizontal scaling of individual GitLab components, and does not require NFS.
* **[GitLab Runner Chart](gitlab_runner_chart.md)**: For deploying just the GitLab Runner.
* Other Charts
* [GitLab-Omnibus](gitlab_omnibus.md): Chart based on the Omnibus GitLab linux package, only suitable for small deployments. The chart will be deprecated by the [GitLab chart](#gitlab-chart) when it is GA.
@@ -26,7 +26,7 @@ should be deployed, upgraded, and configured.
The best way to operate GitLab on Kubernetes. This chart contains all the required components to get started, and can scale to large deployments.
-This chart includes a number of benefits:
+This chart offers a number of benefits:
* Horizontal scaling of individual components
* No requirement for shared storage to scale
* Containers do not need `root` permissions
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 9b1acbac17f..1d26a743500 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -44,15 +44,15 @@ project in a simple and automatic way:
1. [Auto Build](#auto-build)
1. [Auto Test](#auto-test)
-1. [Auto Code Quality](#auto-code-quality)
-1. [Auto SAST (Static Application Security Testing)](#auto-sast)
-1. [Auto Dependency Scanning](#auto-dependency-scanning)
-1. [Auto License Management](#auto-license-management)
+1. [Auto Code Quality](#auto-code-quality) **[STARTER]**
+1. [Auto SAST (Static Application Security Testing)](#auto-sast) **[ULTIMATE]**
+1. [Auto Dependency Scanning](#auto-dependency-scanning) **[ULTIMATE]**
+1. [Auto License Management](#auto-license-management) **[ULTIMATE]**
1. [Auto Container Scanning](#auto-container-scanning)
1. [Auto Review Apps](#auto-review-apps)
-1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast)
+1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast) **[ULTIMATE]**
1. [Auto Deploy](#auto-deploy)
-1. [Auto Browser Performance Testing](#auto-browser-performance-testing)
+1. [Auto Browser Performance Testing](#auto-browser-performance-testing) **[PREMIUM]**
1. [Auto Monitoring](#auto-monitoring)
As Auto DevOps relies on many different components, it's good to have a basic
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index 26329f20339..9801a0a14ed 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -38,3 +38,4 @@ semicolon, comma, or a new line.
![Domain Blacklist](img/domain_blacklist.png)
[ce-5259]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5259
+[ce-598]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/598
diff --git a/doc/user/project/img/group_issue_board.png b/doc/user/project/img/group_issue_board.png
new file mode 100644
index 00000000000..be360d18540
--- /dev/null
+++ b/doc/user/project/img/group_issue_board.png
Binary files differ
diff --git a/doc/user/project/img/issue_board.png b/doc/user/project/img/issue_board.png
index 5f6dc9e4e8b..50e051e25a0 100644
--- a/doc/user/project/img/issue_board.png
+++ b/doc/user/project/img/issue_board.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_add_list.png b/doc/user/project/img/issue_board_add_list.png
index 973d9f7cde4..91098daa1d1 100644
--- a/doc/user/project/img/issue_board_add_list.png
+++ b/doc/user/project/img/issue_board_add_list.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_assignee_lists.png b/doc/user/project/img/issue_board_assignee_lists.png
new file mode 100644
index 00000000000..1ec94d22e33
--- /dev/null
+++ b/doc/user/project/img/issue_board_assignee_lists.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_creation.png b/doc/user/project/img/issue_board_creation.png
new file mode 100644
index 00000000000..9dc4925b0a5
--- /dev/null
+++ b/doc/user/project/img/issue_board_creation.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_edit_button.png b/doc/user/project/img/issue_board_edit_button.png
new file mode 100644
index 00000000000..23883175344
--- /dev/null
+++ b/doc/user/project/img/issue_board_edit_button.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_focus_mode.gif b/doc/user/project/img/issue_board_focus_mode.gif
new file mode 100644
index 00000000000..9565bdb0865
--- /dev/null
+++ b/doc/user/project/img/issue_board_focus_mode.gif
Binary files differ
diff --git a/doc/user/project/img/issue_board_move_issue_card_list.png b/doc/user/project/img/issue_board_move_issue_card_list.png
index 3666dbb87ab..cce252234c1 100644
--- a/doc/user/project/img/issue_board_move_issue_card_list.png
+++ b/doc/user/project/img/issue_board_move_issue_card_list.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_system_notes.png b/doc/user/project/img/issue_board_system_notes.png
index bd0f5f54095..c6ecb498198 100644
--- a/doc/user/project/img/issue_board_system_notes.png
+++ b/doc/user/project/img/issue_board_system_notes.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_view_scope.png b/doc/user/project/img/issue_board_view_scope.png
new file mode 100644
index 00000000000..4e03cecbc2d
--- /dev/null
+++ b/doc/user/project/img/issue_board_view_scope.png
Binary files differ
diff --git a/doc/user/project/img/issue_board_welcome_message.png b/doc/user/project/img/issue_board_welcome_message.png
index 127b9b08cc7..357dff42488 100644
--- a/doc/user/project/img/issue_board_welcome_message.png
+++ b/doc/user/project/img/issue_board_welcome_message.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_add_issues_modal.png b/doc/user/project/img/issue_boards_add_issues_modal.png
index bedaf724a15..625a4304eaf 100644
--- a/doc/user/project/img/issue_boards_add_issues_modal.png
+++ b/doc/user/project/img/issue_boards_add_issues_modal.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_multiple.png b/doc/user/project/img/issue_boards_multiple.png
new file mode 100644
index 00000000000..4b2b8d457f1
--- /dev/null
+++ b/doc/user/project/img/issue_boards_multiple.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_remove_issue.png b/doc/user/project/img/issue_boards_remove_issue.png
index 8b3beca97cf..9a2fad2cc7f 100644
--- a/doc/user/project/img/issue_boards_remove_issue.png
+++ b/doc/user/project/img/issue_boards_remove_issue.png
Binary files differ
diff --git a/doc/user/project/import/bitbucket.md b/doc/user/project/import/bitbucket.md
index b22c7db0047..e3d625cc621 100644
--- a/doc/user/project/import/bitbucket.md
+++ b/doc/user/project/import/bitbucket.md
@@ -9,6 +9,10 @@ The [Bitbucket integration][bb-import] must be first enabled in order to be
able to import your projects from Bitbucket. Ask your GitLab administrator
to enable this if not already.
+>**Note:**
+The BitBucket importer currently only works with BitBucket's cloud offering
+(bitbucket.org) and does not work with BitBucket Server (aka Stash).
+
- At its current state, the Bitbucket importer can import:
- the repository description (GitLab 7.7+)
- the Git repository data (GitLab 7.7+)
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 9ca1e6226c5..10647e33f4c 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -1,16 +1,10 @@
-# Issue Board
+# Issue Boards
->**Note:**
-[Introduced][ce-5554] in [GitLab 8.11](https://about.gitlab.com/2016/08/22/gitlab-8-11-released/#issue-board).
+> [Introduced][ce-5554] in [GitLab 8.11](https://about.gitlab.com/2016/08/22/gitlab-8-11-released/#issue-board).
The GitLab Issue Board is a software project management tool used to plan,
organize, and visualize a workflow for a feature or product release.
-It can be seen like a light version of a [Kanban] or a [Scrum] board.
-
-Other interesting links:
-
-- [GitLab Issue Board landing page on about.gitlab.com][landing]
-- [YouTube video introduction to Issue Boards][youtube]
+It can be used as a [Kanban] or a [Scrum] board.
![GitLab Issue Board](img/issue_board.png)
@@ -18,7 +12,7 @@ Other interesting links:
The Issue Board builds on GitLab's existing
[issue tracking functionality](issues/index.md#issue-tracker) and
-leverages the power of [labels] by utilizing them as lists of the scrum board.
+leverages the power of [labels](labels.md) by utilizing them as lists of the scrum board.
With the Issue Board you can have a different view of your issues while
maintaining the same filtering and sorting abilities you see across the
@@ -33,15 +27,23 @@ You create issues, host code, perform reviews, build, test,
and deploy from one single platform. Issue Boards help you to visualize
and manage the entire process _in_ GitLab.
-With [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards), available
-only in [GitLab Ultimate](https://about.gitlab.com/products/),
+With [Multiple Issue Boards](#multiple-issue-boards), available
+only in [GitLab Enterprise Edition](#features-per-tier),
you go even further, as you can not only keep yourself and your project
organized from a broader perspective with one Issue Board per project,
but also allow your team members to organize their own workflow by creating
multiple Issue Boards within the same project.
+For a visual overview, see our [Issue Board feature page](https://about.gitlab.com/features/issueboard/)
+on about.gitlab.com or our [video introduction to Issue Boards](https://www.youtube.com/watch?v=UWsJ8tkHAa8).
+
## Use cases
+There are many ways to use GitLab Issue Boards tailored to your own preferred workflow.
+Here are some common use cases for Issue Boards.
+
+### Use cases for a single Issue Board
+
GitLab Workflow allows you to discuss proposals in issues, categorize them
with labels, and from there organize and prioritize them with Issue Boards.
@@ -65,33 +67,66 @@ beginning of the development lifecycle until deployed to production
![issue card moving](img/issue_board_move_issue_card_list.png)
-> **Notes:**
->
->- For a broader use case, please check the blog post
+### Use cases for Multiple Issue Boards
+
+With [Multiple Issue Boards](#multiple-issue-boards), available only in
+[GitLab Enterprise Edition](https://about.gitlab.com/products/),
+each team can have their own board to organize their workflow individually.
+
+#### Scrum team
+
+With multiple Issue Boards, each team has one board. Now you can move issues through each
+part of the process. For instance: **To Do**, **Doing**, and **Done**.
+
+#### Organization of topics
+
+Create lists to order things by topic and quickly change them between topics or groups,
+such as between **UX**, **Frontend**, and **Backend**. The changes will be reflected across boards,
+as changing lists will update the label accordingly.
+
+#### Advanced team handover
+
+For example, suppose we have a UX team with an Issue Board that contains:
+
+- **To Do**
+- **Doing**
+- **Frontend**
+
+When done with something, they move the card to **Frontend**. The Frontend team's board looks like:
+
+- **Frontend**
+- **Doing**
+- **Done**
+
+Cards finished by the UX team will automatically appear in the **Frontend** column when they're ready for them.
+
+NOTE: **Note:**
+For a broader use case, please see the blog post
[GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/#gitlab-workflow-use-case-scenario).
->
->- For a real use case, please check why
+For a real use case example, you can read why
[Codepen decided to adopt Issue Boards](https://about.gitlab.com/2017/01/27/codepen-welcome-to-gitlab/#project-management-everything-in-one-place)
-to improve their workflow with [multiple boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
+to improve their workflow with multiple boards.
-## Issue Board terminology
+#### Quick assignments
-Below is a table of the definitions used for GitLab's Issue Board.
+Create lists for each of your team members and quickly drag-and-drop issues onto each team member.
-| What we call it | What it means |
-| -------------- | ------------- |
-| **Issue Board** | It represents a different view for your issues. It can have multiple lists with each list consisting of issues represented by cards. |
-| **List** | Each label that exists in the issue tracker can have its own dedicated list. Every list is named after the label it is based on and is represented by a column which contains all the issues associated with that label. You can think of a list like the results you get when you filter the issues by a label in your issue tracker. |
-| **Card** | Every card represents an issue and it is shown under the list for which it has a label. The information you can see on a card consists of the issue number, the issue title, the assignee and the labels associated with it. You can drag cards around from one list to another. You can re-order cards within a list. |
+## Permissions
-There are two types of lists, the ones you create based on your labels, and
-two defaults:
+[Developers and up](../permissions.md) can use all the functionality of the
+Issue Board, that is, create or delete lists and drag issues from one list to another.
-- Label list: a list based on a label. It shows all opened issues with that label.
-- **Backlog** (default): shows all open issues that does not belong to one of lists. Always appears on the very left.
-- **Closed** (default): shows all closed issues. Always appears on the very right.
+## Issue Board terminology
+
+- **Issue Board** - Each board represents a unique view for your issues. It can have multiple lists with each list consisting of issues represented by cards.
+- **List** - A column on the issue board that displays issues matching certain attributes. In addition to the default lists of 'Backlog' and 'Closed' issue, each additional list will show issues matching your chosen label or assignee.
+ - **Label list**: a list based on a label. It shows all opened issues with that label.
+ - **Assignee list**: a list which includes all issues assigned to a user.
+ - **Backlog** (default): shows all open issues that do not belong to one of the other lists. Always appears as the leftmost list.
+ - **Closed** (default): shows all closed issues. Always appears as the rightmost list.
+- **Card** - A box in the list that represents an individual issue. The information you can see on a card consists of the issue number, the issue title, the assignee, and the labels associated with the issue. You can drag cards from one list to another to change their label or assignee from that of the source list to that of the destination list.
-In short, here's a list of actions you can take in an Issue Board:
+## Actions you can take on an Issue Board
- [Create a new list](#creating-a-new-list).
- [Delete an existing list](#deleting-a-list).
@@ -129,7 +164,7 @@ right corner of the Issue Board.
![Issue Board welcome message](img/issue_board_add_list.png)
-Simply choose the label to create the list from. The new list will be inserted
+Simply choose the label or user to create the list from. The new list will be inserted
at the end of the lists, before **Done**. Moving and reordering lists is as
easy as dragging them around.
@@ -174,17 +209,19 @@ to the system so that anybody who visits the same board later will see the reord
with some exceptions.
The first time a given issue appears in any board (i.e. the first time a user
-loads a board containing that issue), it will be ordered with
-respect to other issues in that list according to [Priority order][label-priority].
+loads a board containing that issue), it will be ordered with
+respect to other issues in that list according to [Priority order](labels.md#label-priority).
+
At that point, that issue will be assigned a relative order value by the system
representing its relative order with respect to the other issues in the list. Any time
you drag-and-drop reorder that issue, its relative order value will change accordingly.
+
Also, any time that issue appears in any board when it is loaded by a user,
the updated relative order value will be used for the ordering. (It's only the first
time an issue appears that it takes from the Priority order mentioned above.) This means that
if issue `A` is drag-and-drop reordered to be above issue `B` by any user in
a given board inside your GitLab instance, any time those two issues are subsequently
-loaded in any board in the same instance (could be a different project board or a different group board, for example),
+loaded in any board in the same instance (could be a different project board or a different group board, for example),
that ordering will be maintained.
## Filtering issues
@@ -205,8 +242,8 @@ something between lists by changing a label.
A typical workflow of using the Issue Board would be:
-1. You have [created][create-labels] and [prioritized][label-priority] labels
- so that you can easily categorize your issues.
+1. You have [created](labels.md#creating-labels) and [prioritized](labels.md#label-priority)
+ labels so that you can easily categorize your issues.
1. You have a bunch of issues (ideally labeled).
1. You visit the Issue Board and start [creating lists](#creating-a-new-list) to
create a workflow.
@@ -230,21 +267,98 @@ to another list the label changes and a system not is recorded.
![Issue Board system notes](img/issue_board_system_notes.png)
-## Permissions
+## Multiple Issue Boards **[STARTER]**
-[Developers and up](../permissions.md) can use all the functionality of the
-Issue Board, that is create/delete lists and drag issues around.
+> Introduced in [GitLab Enterprise Edition 8.13](https://about.gitlab.com/2016/10/22/gitlab-8-13-released/#multiple-issue-boards-ee).
-## Group Issue Board
+Multiple Issue Boards, as the name suggests, allow for more than one Issue Board
+for a given project or group. This is great for large projects with more than one team
+or in situations where a repository is used to host the code of multiple
+products.
-> Introduced in [GitLab 10.6](https://about.gitlab.com/2018/03/22/gitlab-10-6-released/#single-group-issue-board-in-core-and-free)
+Clicking on the current board name in the upper left corner will reveal a
+menu from where you can create another Issue Board and rename or delete the
+existing one.
-Group issue board is analogous to project-level issue board and it is accessible at the group
-navigation level. A group-level issue board allows you to view all issues from all projects in that group or descendant subgroups. Similarly, you can only filter by group labels for these
+NOTE: **Note:**
+The Multiple Issue Boards feature is available for
+**projects in GitLab Starter Edition** and for **groups in GitLab Premium Edition**.
+
+![Multiple Issue Boards](img/issue_boards_multiple.png)
+
+## Configurable Issue Boards **[STARTER]**
+
+> Introduced in [GitLab Starter Edition 10.2](https://about.gitlab.com/2017/11/22/gitlab-10-2-released/#issue-boards-configuration).
+
+An Issue Board can be associated with GitLab [Milestone](milestones/index.md#milestones),
+[Labels](labels.md), Assignee and Weight
+which will automatically filter the Board issues according to these fields.
+This allows you to create unique boards according to your team's need.
+
+![Create scoped board](img/issue_board_creation.png)
+
+You can define the scope of your board when creating it or by clicking on the "Edit board" button. Once a milestone, assignee or weight is assigned to an Issue Board, you will no longer be able to filter
+through these in the search bar. In order to do that, you need to remove the desired scope (e.g. milestone, assignee or weight) from the Issue Board.
+
+![Edit board configuration](img/issue_board_edit_button.png)
+
+If you don't have editing permission in a board, you're still able to see the configuration by clicking on "View scope".
+
+![Viewing board configuration](img/issue_board_view_scope.png)
+
+## Focus mode **[STARTER]**
+
+> Introduced in [GitLab Starter 9.1](https://about.gitlab.com/2017/04/22/gitlab-9-1-released/#issue-boards-focus-mode-ees-eep).
+
+Click the button at the top right to toggle focus mode on and off. In focus mode, the navigation UI is hidden, allowing you to focus on issues in the board.
+
+![Board focus mode](img/issue_board_focus_mode.gif)
+
+## Group Issue Boards **[PREMIUM]**
+
+> Introduced in [GitLab Premium 10.0](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards).
+
+Accessible at the group navigation level, a group issue board offers the same features as a project-level board,
+but it can display issues from all projects in that
+group and its descendant subgroups. Similarly, you can only filter by group labels for these
boards. When updating milestones and labels for an issue through the sidebar update mechanism, again only
group-level objects are available.
-One group issue board per group was made available in GitLab 10.6 Core after multiple group issue boards were originally introduced in [GitLab 10.0 Premium](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards).
+NOTE: **Note:**
+Multiple group issue boards were originally introduced in [GitLab 10.0 Premium](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards) and
+one group issue board per group was made available in GitLab 10.6 Core.
+
+![Group issue board](img/group_issue_board.png)
+
+## Assignee lists **[PREMIUM]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5784) in GitLab 11.0 Premium.
+
+Like a regular list that shows all issues that have the list label, you can add
+an assignee list that shows all issues assigned to the given user.
+You can have a board with both label lists and assignee lists. To add an
+assignee list:
+
+1. Click **Add list**.
+1. Select the **Assignee list** tab.
+1. Search and click on the user you want to add as an assignee.
+
+Now that the assignee list is added, you can assign or unassign issues to that user
+by [dragging issues](#dragging-issues-between-lists) to and/or from an assignee list.
+To remove an assignee list, just as with a label list, click the trash icon.
+
+![Assignee lists](img/issue_board_assignee_lists.png)
+
+## Dragging issues between lists
+
+When dragging issues between lists, different behavior occurs depending on the source list and the target list.
+
+| | To Backlog | To Closed | To label `B` list | To assignee `Bob` list |
+| --- | --- | --- | --- | --- |
+| From Backlog | - | Issue closed | `B` added | `Bob` assigned |
+| From Closed | Issue reopened | - | Issue reopened<br/>`B` added | Issue reopened<br/>`Bob` assigned |
+| From label `A` list | `A` removed | Issue closed | `A` removed<br/>`B` added | `Bob` assigned |
+| From assignee `Alice` list | `Alice` unassigned | Issue closed | `B` added | `Alice` unassigned<br/>`Bob` assigned |
## Features per tier
@@ -261,11 +375,8 @@ Different issue board features are available in different [GitLab tiers](https:/
A few things to remember:
-- The label that corresponds to a list is hidden for issues under that list.
- Moving an issue between lists removes the label from the list it came from
and adds the label from the list it goes to.
-- When moving a card to **Done**, the label of the list it came from is removed
- and the issue gets closed.
- An issue can exist in multiple lists if it has more than one label.
- Lists are populated with issues automatically if the issues are labeled.
- Clicking on the issue title inside a card will take you to that issue.
@@ -276,10 +387,5 @@ A few things to remember:
20 will appear.
[ce-5554]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5554
-[labels]: ./labels.md
[scrum]: https://en.wikipedia.org/wiki/Scrum_(software_development)
[kanban]: https://en.wikipedia.org/wiki/Kanban_(development)
-[create-labels]: ./labels.md#create-new-labels
-[label-priority]: ./labels.md#prioritize-labels
-[landing]: https://about.gitlab.com/solutions/issueboard
-[youtube]: https://www.youtube.com/watch?v=UWsJ8tkHAa8
diff --git a/doc/user/project/issues/deleting_issues.md b/doc/user/project/issues/deleting_issues.md
index d7442104c53..536a0de8974 100644
--- a/doc/user/project/issues/deleting_issues.md
+++ b/doc/user/project/issues/deleting_issues.md
@@ -8,4 +8,6 @@ You can delete an issue by editing it and clicking on the delete button.
![delete issue - button](img/delete_issue.png)
->**Note:** Only [project owners](../../permissions.md) can delete issues. \ No newline at end of file
+>**Note:** Only [project owners](../../permissions.md) can delete issues.
+
+[ce-2982]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2982 \ No newline at end of file
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
index f824756c10c..8a2f230f505 100644
--- a/doc/workflow/lfs/lfs_administration.md
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -17,7 +17,7 @@ There are various configuration options to help GitLab server administrators:
* Enabling/disabling Git LFS support
* Changing the location of LFS object storage
-* Setting up AWS S3 compatible object storage
+* Setting up object storage supported by [Fog](http://fog.io/about/provider_documentation.html)
### Configuration for Omnibus installations
@@ -44,19 +44,31 @@ In `config/gitlab.yml`:
storage_path: /mnt/storage/lfs-objects
```
-## Storing the LFS objects in an S3-compatible object storage
+## Storing LFS objects in remote object storage
> [Introduced][ee-2760] in [GitLab Premium][eep] 10.0. Brought to GitLab Core
in 10.7.
-It is possible to store LFS objects on a remote object storage which allows you
-to offload storage to an external AWS S3 compatible service, freeing up disk
-space locally. You can also host your own S3 compatible storage decoupled from
-GitLab, with with a service such as [Minio](https://www.minio.io/).
+It is possible to store LFS objects in remote object storage which allows you
+to offload local hard disk R/W operations, and free up disk space significantly.
+GitLab is tightly integrated with `Fog`, so you can refer to its [documentation](http://fog.io/about/provider_documentation.html)
+to check which storage services can be integrated with GitLab.
+You can also use external object storage in a private local network. For example,
+[Minio](https://www.minio.io/) is a standalone object storage service, is easy to setup, and works well with GitLab instances.
-Object storage currently transfers files first to GitLab, and then on the
-object storage in a second stage. This can be done either by using a rake task
-to transfer existing objects, or in a background job after each file is received.
+GitLab provides two different options for the uploading mechanism: "Direct upload" and "Background upload".
+
+**Option 1. Direct upload**
+
+1. User pushes an lfs file to the GitLab instance
+1. GitLab-workhorse uploads the file directly to the external object storage
+1. GitLab-workhorse notifies GitLab-rails that the upload process is complete
+
+**Option 2. Background upload**
+
+1. User pushes an lfs file to the GitLab instance
+1. GitLab-rails stores the file in the local file storage
+1. GitLab-rails then uploads the file to the external object storage asynchronously
The following general settings are supported.
@@ -71,16 +83,50 @@ The following general settings are supported.
The `connection` settings match those provided by [Fog](https://github.com/fog).
-| Setting | Description | Default |
+Here is a configuration example with S3.
+
+| Setting | Description | example |
|---------|-------------|---------|
-| `provider` | Always `AWS` for compatible hosts | AWS |
-| `aws_access_key_id` | AWS credentials, or compatible | |
-| `aws_secret_access_key` | AWS credentials, or compatible | |
+| `provider` | The provider name | AWS |
+| `aws_access_key_id` | AWS credentials, or compatible | `ABC123DEF456` |
+| `aws_secret_access_key` | AWS credentials, or compatible | `ABC123DEF456ABC123DEF456ABC123DEF456` |
| `region` | AWS region | us-east-1 |
| `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com |
| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) |
| `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false |
+Here is a configuration example with GCS.
+
+| Setting | Description | example |
+|---------|-------------|---------|
+| `provider` | The provider name | `Google` |
+| `google_project` | GCP project name | `gcp-project-12345` |
+| `google_client_email` | The email address of the service account | `foo@gcp-project-12345.iam.gserviceaccount.com` |
+| `google_json_key_location` | The json key path | `/path/to/gcp-project-12345-abcde.json` |
+
+_NOTE: The service account must have permission to access the bucket. [See more](https://cloud.google.com/storage/docs/authentication)_
+
+### Manual uploading to an object storage
+
+There are two ways to manually do the same thing as automatic uploading (described above).
+
+**Option 1: rake task**
+
+```
+$ rake gitlab:lfs:migrate
+```
+
+**Option 2: rails console**
+
+```
+$ sudo gitlab-rails console # Login to rails console
+
+> # Upload LFS files manually
+> LfsObject.where(file_store: [nil, 1]).find_each do |lfs_object|
+> lfs_object.file.migrate!(ObjectStorage::Store::REMOTE) if lfs_object.file.file.exists?
+> end
+```
+
### S3 for Omnibus installations
On Omnibus installations, the settings are prefixed by `lfs_object_store_`:
@@ -156,6 +202,29 @@ You can see the total storage used for LFS objects on groups and projects
in the administration area, as well as through the [groups](../../api/groups.md)
and [projects APIs](../../api/projects.md).
+## Troubleshooting: `Google::Apis::TransmissionError: execution expired`
+
+If LFS integration is configred with Google Cloud Storage and background uploads (`background_upload: true` and `direct_upload: false`),
+sidekiq workers may encouter this error. This is because the uploading timed out with very large files.
+LFS files up to 6Gb can be uploaded without any extra steps, otherwise you need to use the following workaround.
+
+```shell
+$ sudo gitlab-rails console # Login to rails console
+
+> # Set up timeouts. 20 minutes is enough to upload 30GB LFS files.
+> # These settings are only in effect for the same session, i.e. they are not effective for sidekiq workers.
+> ::Google::Apis::ClientOptions.default.open_timeout_sec = 1200
+> ::Google::Apis::ClientOptions.default.read_timeout_sec = 1200
+> ::Google::Apis::ClientOptions.default.send_timeout_sec = 1200
+
+> # Upload LFS files manually. This process does not use sidekiq at all.
+> LfsObject.where(file_store: [nil, 1]).find_each do |lfs_object|
+> lfs_object.file.migrate!(ObjectStorage::Store::REMOTE) if lfs_object.file.file.exists?
+> end
+```
+
+See more information in [!19581](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19581)
+
## Known limitations
* Support for removing unreferenced LFS objects was added in 8.14 onwards.
diff --git a/lib/gitlab/cache/request_cache.rb b/lib/gitlab/cache/request_cache.rb
index ecc85f847d4..671b8e7e1b1 100644
--- a/lib/gitlab/cache/request_cache.rb
+++ b/lib/gitlab/cache/request_cache.rb
@@ -1,41 +1,6 @@
module Gitlab
module Cache
- # This module provides a simple way to cache values in RequestStore,
- # and the cache key would be based on the class name, method name,
- # optionally customized instance level values, optionally customized
- # method level values, and optional method arguments.
- #
- # A simple example:
- #
- # class UserAccess
- # extend Gitlab::Cache::RequestCache
- #
- # request_cache_key do
- # [user&.id, project&.id]
- # end
- #
- # request_cache def can_push_to_branch?(ref)
- # # ...
- # end
- # end
- #
- # This way, the result of `can_push_to_branch?` would be cached in
- # `RequestStore.store` based on the cache key. If RequestStore is not
- # currently active, then it would be stored in a hash saved in an
- # instance variable, so the cache logic would be the same.
- # Here's another example using customized method level values:
- #
- # class Commit
- # extend Gitlab::Cache::RequestCache
- #
- # def author
- # User.find_by_any_email(author_email.downcase)
- # end
- # request_cache(:author) { author_email.downcase }
- # end
- #
- # So that we could have different strategies for different methods
- #
+ # See https://docs.gitlab.com/ee/development/utilities.html#requestcache
module RequestCache
def self.extended(klass)
return if klass < self
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index c9806cdb85f..341768752dc 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -381,15 +381,11 @@ module Gitlab
# empty repo. See Repository#diff for keys allowed in the +options+
# hash.
def diff_from_parent(options = {})
- Gitlab::GitalyClient.migrate(:commit_raw_diffs) do |is_enabled|
- if is_enabled
- @repository.gitaly_commit_client.diff_from_parent(self, options)
- else
- rugged_diff_from_parent(options)
- end
- end
+ @repository.gitaly_commit_client.diff_from_parent(self, options)
end
+ # Not to be called directly, but right now its used for tests and in old
+ # migrations
def rugged_diff_from_parent(options = {})
options ||= {}
break_rewrites = options[:break_rewrites]
diff --git a/lib/gitlab/git/gitlab_projects.rb b/lib/gitlab/git/gitlab_projects.rb
index 8475645971e..5ff15a787f0 100644
--- a/lib/gitlab/git/gitlab_projects.rb
+++ b/lib/gitlab/git/gitlab_projects.rb
@@ -61,22 +61,15 @@ module Gitlab
end
def fetch_remote(name, timeout, force:, tags:, ssh_key: nil, known_hosts: nil, prune: true)
- tags_option = tags ? '--tags' : '--no-tags'
-
logger.info "Fetching remote #{name} for repository #{repository_absolute_path}."
- cmd = %W(#{Gitlab.config.git.bin_path} fetch #{name} --quiet)
- cmd << '--prune' if prune
- cmd << '--force' if force
- cmd << tags_option
+ cmd = fetch_remote_command(name, tags, prune, force)
setup_ssh_auth(ssh_key, known_hosts) do |env|
- success = run_with_timeout(cmd, timeout, repository_absolute_path, env)
-
- unless success
- logger.error "Fetching remote #{name} for repository #{repository_absolute_path} failed."
+ run_with_timeout(cmd, timeout, repository_absolute_path, env).tap do |success|
+ unless success
+ logger.error "Fetching remote #{name} for repository #{repository_absolute_path} failed."
+ end
end
-
- success
end
end
@@ -202,6 +195,14 @@ module Gitlab
private
+ def fetch_remote_command(name, tags, prune, force)
+ %W(#{Gitlab.config.git.bin_path} fetch #{name} --quiet).tap do |cmd|
+ cmd << '--prune' if prune
+ cmd << '--force' if force
+ cmd << (tags ? '--tags' : '--no-tags')
+ end
+ end
+
def git_import_repository(source, timeout)
# Skip import if repo already exists
return false if File.exist?(repository_absolute_path)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 2b599ee9f82..0904e1c2973 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -403,13 +403,7 @@ module Gitlab
# Return repo size in megabytes
def size
- size = gitaly_migrate(:repository_size) do |is_enabled|
- if is_enabled
- size_by_gitaly
- else
- size_by_shelling_out
- end
- end
+ size = gitaly_repository_client.repository_size
(size.to_f / 1024).round(2)
end
@@ -613,17 +607,7 @@ module Gitlab
def ref_name_for_sha(ref_path, sha)
raise ArgumentError, "sha can't be empty" unless sha.present?
- gitaly_migrate(:find_ref_name) do |is_enabled|
- if is_enabled
- gitaly_ref_client.find_ref_name(sha, ref_path)
- else
- args = %W(for-each-ref --count=1 #{ref_path} --contains #{sha})
-
- # Not found -> ["", 0]
- # Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
- run_git(args).first.split.last
- end
- end
+ gitaly_ref_client.find_ref_name(sha, ref_path)
end
# Get refs hash which key is is the commit id
@@ -1822,14 +1806,6 @@ module Gitlab
commit(sha)
end
- def size_by_shelling_out
- popen(%w(du -sk), path).first.strip.to_i
- end
-
- def size_by_gitaly
- gitaly_repository_client.repository_size
- end
-
# Returns true if the given ref name exists
#
# Ref names must start with `refs/`.
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index a39b3bc158c..7b2b3bedf04 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -22,27 +22,27 @@ module Gitlab
def init_metrics
metrics = {}
- metrics[:sampler_duration] = Metrics.histogram(with_prefix(:sampler_duration, :seconds), 'Sampler time', { worker: nil })
- metrics[:total_time] = Metrics.gauge(with_prefix(:gc, :time_total), 'Total GC time', labels, :livesum)
+ metrics[:sampler_duration] = Metrics.counter(with_prefix(:sampler, :duration_seconds_total), 'Sampler time', labels)
+ metrics[:total_time] = Metrics.counter(with_prefix(:gc, :duration_seconds_total), 'Total GC time', labels)
GC.stat.keys.each do |key|
- metrics[key] = Metrics.gauge(with_prefix(:gc, key), to_doc_string(key), labels, :livesum)
+ metrics[key] = Metrics.gauge(with_prefix(:gc_stat, key), to_doc_string(key), labels, :livesum)
end
- metrics[:objects_total] = Metrics.gauge(with_prefix(:objects, :total), 'Objects total', labels.merge(class: nil), :livesum)
- metrics[:memory_usage] = Metrics.gauge(with_prefix(:memory, :usage_total), 'Memory used total', labels, :livesum)
- metrics[:file_descriptors] = Metrics.gauge(with_prefix(:file, :descriptors_total), 'File descriptors total', labels, :livesum)
+ metrics[:memory_usage] = Metrics.gauge(with_prefix(:memory, :bytes), 'Memory used', labels, :livesum)
+ metrics[:file_descriptors] = Metrics.gauge(with_prefix(:file, :descriptors), 'File descriptors used', labels, :livesum)
metrics
end
def sample
start_time = System.monotonic_time
- sample_gc
- metrics[:memory_usage].set(labels, System.memory_usage)
- metrics[:file_descriptors].set(labels, System.file_descriptor_count)
+ metrics[:memory_usage].set(labels.merge(worker_label), System.memory_usage)
+ metrics[:file_descriptors].set(labels.merge(worker_label), System.file_descriptor_count)
+
+ sample_gc
- metrics[:sampler_duration].observe(labels.merge(worker_label), System.monotonic_time - start_time)
+ metrics[:sampler_duration].increment(labels, System.monotonic_time - start_time)
ensure
GC::Profiler.clear
end
@@ -50,11 +50,13 @@ module Gitlab
private
def sample_gc
- metrics[:total_time].set(labels, GC::Profiler.total_time * 1000)
-
+ # Collect generic GC stats.
GC.stat.each do |key, value|
metrics[key].set(labels, value)
end
+
+ # Collect the GC time since last sample in float seconds.
+ metrics[:total_time].increment(labels, GC::Profiler.total_time)
end
def worker_label
diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb
index ec3802e7a76..c5b8c271d7d 100644
--- a/qa/qa/specs/features/repository/protected_branches_spec.rb
+++ b/qa/qa/specs/features/repository/protected_branches_spec.rb
@@ -13,11 +13,15 @@ module QA
Page::Main::Login.act { sign_in_using_credentials }
end
- after do
+ after do |example|
# We need to clear localStorage because we're using it for the dropdown,
# and capybara doesn't do this for us.
# https://github.com/teamcapybara/capybara/issues/1702
Capybara.execute_script 'localStorage.clear()'
+
+ # In order to help diagnose a false failure
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/48241
+ log_push_output if example.exception
end
context 'when developers and maintainers are allowed to push to a protected branch' do
@@ -27,9 +31,9 @@ module QA
expect(protected_branch.name).to have_content(branch_name)
expect(protected_branch.push_allowance).to have_content('Developers + Maintainers')
- push = push_new_file(branch_name)
+ @push = push_new_file(branch_name)
- expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
+ expect(@push.output).to match(/remote: To create a merge request for protected-branch, visit/)
end
end
@@ -37,11 +41,11 @@ module QA
scenario 'user without push rights fails to push to the protected branch' do
create_protected_branch(allow_to_push: false)
- push = push_new_file(branch_name)
+ @push = push_new_file(branch_name)
- expect(push.output)
+ expect(@push.output)
.to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
- expect(push.output)
+ expect(@push.output)
.to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
@@ -65,5 +69,13 @@ module QA
resource.new_branch = false
end
end
+
+ def log_push_output
+ if defined?(@push)
+ filename = File.join('tmp', "push-output-#{project.name}")
+ puts "Exception detected. Push output will be saved to #{filename}"
+ IO.binwrite(filename, @push.output)
+ end
+ end
end
end
diff --git a/rubocop/cop/migration/update_large_table.rb b/rubocop/cop/migration/update_large_table.rb
index bb14d0f4f56..c15eec22d04 100644
--- a/rubocop/cop/migration/update_large_table.rb
+++ b/rubocop/cop/migration/update_large_table.rb
@@ -20,10 +20,14 @@ module RuboCop
'necessary'.freeze
LARGE_TABLES = %i[
- ci_pipelines
+ ci_build_trace_sections
ci_builds
+ ci_job_artifacts
+ ci_pipelines
+ ci_stages
events
issues
+ merge_request_diff_commits
merge_request_diff_files
merge_request_diffs
merge_requests
@@ -34,8 +38,15 @@ module RuboCop
users
].freeze
+ BATCH_UPDATE_METHODS = %w[
+ :add_column_with_default
+ :change_column_type_concurrently
+ :rename_column_concurrently
+ :update_column_in_batches
+ ].join(' ').freeze
+
def_node_matcher :batch_update?, <<~PATTERN
- (send nil? ${:add_column_with_default :update_column_in_batches} $(sym ...) ...)
+ (send nil? ${#{BATCH_UPDATE_METHODS}} $(sym ...) ...)
PATTERN
def on_send(node)
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index d5e603baeae..a4226d7a682 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -31,6 +31,7 @@ feature 'Admin Groups' do
path_component = 'gitlab'
group_name = 'GitLab group name'
group_description = 'Description of group for GitLab'
+
fill_in 'group_path', with: path_component
fill_in 'group_name', with: group_name
fill_in 'group_description', with: group_description
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index bc42618306f..8dca81a8627 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -10,6 +10,7 @@ describe 'Filter issues', :js do
# When the name is longer, the filtered search input can end up scrolling
# horizontally, and PhantomJS can't handle it.
let(:user) { create(:user, name: 'Ann') }
+ let(:user2) { create(:user, name: 'jane') }
let!(:bug_label) { create(:label, project: project, title: 'bug') }
let!(:caps_sensitive_label) { create(:label, project: project, title: 'CaPs') }
@@ -25,8 +26,6 @@ describe 'Filter issues', :js do
before do
project.add_master(user)
- user2 = create(:user)
-
create(:issue, project: project, author: user2, title: "Bug report 1")
create(:issue, project: project, author: user2, title: "Bug report 2")
@@ -113,6 +112,24 @@ describe 'Filter issues', :js do
expect_issues_list_count(3)
expect_filtered_search_input_empty
end
+
+ it 'filters issues by invalid assignee' do
+ skip('to be tested, issue #26546')
+ end
+
+ it 'filters issues by multiple assignees' do
+ create(:issue, project: project, author: user, assignees: [user2, user])
+
+ input_filtered_search("assignee:@#{user.username} assignee:@#{user2.username}")
+
+ expect_tokens([
+ assignee_token(user.name),
+ assignee_token(user2.name)
+ ])
+
+ expect_issues_list_count(1)
+ expect_filtered_search_input_empty
+ end
end
end
@@ -491,6 +508,21 @@ describe 'Filter issues', :js do
it_behaves_like 'updates atom feed link', :group do
let(:path) { issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id) }
end
+
+ it 'updates atom feed link for group issues' do
+ visit issues_group_path(group, milestone_title: milestone.title, assignee_id: user.id)
+ link = find('.nav-controls a[title="Subscribe to RSS feed"]', visible: false)
+ params = CGI.parse(URI.parse(link[:href]).query)
+ auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
+ auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
+
+ expect(params).to include('feed_token' => [user.feed_token])
+ expect(params).to include('milestone_title' => [milestone.title])
+ expect(params).to include('assignee_id' => [user.id.to_s])
+ expect(auto_discovery_params).to include('feed_token' => [user.feed_token])
+ expect(auto_discovery_params).to include('milestone_title' => [milestone.title])
+ expect(auto_discovery_params).to include('assignee_id' => [user.id.to_s])
+ end
end
context 'URL has a trailing slash' do
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index b51ca5d130b..bfe11ddf673 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -40,6 +40,15 @@ describe 'Signup' do
expect(find('.username')).to have_css '.gl-field-error-outline'
end
+
+ it 'shows an error message on submit if the username contains special characters' do
+ fill_in 'new_user_username', with: 'new$user!username'
+ wait_for_requests
+
+ click_button "Register"
+
+ expect(page).to have_content("Please create a username with only alphanumeric characters.")
+ end
end
context 'with no errors' do
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index d84b13b07c4..57e0caa692c 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -6,7 +6,7 @@ import MockU2FDevice from './mock_u2f_device';
describe('U2FAuthenticate', function () {
preloadFixtures('u2f/authenticate.html.raw');
- beforeEach((done) => {
+ beforeEach(() => {
loadFixtures('u2f/authenticate.html.raw');
this.u2fDevice = new MockU2FDevice();
this.container = $('#js-authenticate-u2f');
@@ -19,46 +19,70 @@ describe('U2FAuthenticate', function () {
document.querySelector('#js-login-2fa-device'),
document.querySelector('.js-2fa-form'),
);
+ });
- // bypass automatic form submission within renderAuthenticated
- spyOn(this.component, 'renderAuthenticated').and.returnValue(true);
+ describe('with u2f unavailable', () => {
+ beforeEach(() => {
+ spyOn(this.component, 'switchToFallbackUI');
+ this.oldu2f = window.u2f;
+ window.u2f = null;
+ });
- this.component.start().then(done).catch(done.fail);
- });
+ afterEach(() => {
+ window.u2f = this.oldu2f;
+ });
- it('allows authenticating via a U2F device', () => {
- const inProgressMessage = this.container.find('p');
- expect(inProgressMessage.text()).toContain('Trying to communicate with your device');
- this.u2fDevice.respondToAuthenticateRequest({
- deviceData: 'this is data from the device',
+ it('falls back to normal 2fa', (done) => {
+ this.component.start().then(() => {
+ expect(this.component.switchToFallbackUI).toHaveBeenCalled();
+ done();
+ }).catch(done.fail);
});
- expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
});
- describe('errors', () => {
- it('displays an error message', () => {
- const setupButton = this.container.find('#js-login-u2f-device');
- setupButton.trigger('click');
- this.u2fDevice.respondToAuthenticateRequest({
- errorCode: 'error!',
- });
- const errorMessage = this.container.find('p');
- return expect(errorMessage.text()).toContain('There was a problem communicating with your device');
+ describe('with u2f available', () => {
+ beforeEach((done) => {
+ // bypass automatic form submission within renderAuthenticated
+ spyOn(this.component, 'renderAuthenticated').and.returnValue(true);
+ this.u2fDevice = new MockU2FDevice();
+
+ this.component.start().then(done).catch(done.fail);
});
- return it('allows retrying authentication after an error', () => {
- let setupButton = this.container.find('#js-login-u2f-device');
- setupButton.trigger('click');
- this.u2fDevice.respondToAuthenticateRequest({
- errorCode: 'error!',
- });
- const retryButton = this.container.find('#js-u2f-try-again');
- retryButton.trigger('click');
- setupButton = this.container.find('#js-login-u2f-device');
- setupButton.trigger('click');
+
+ it('allows authenticating via a U2F device', () => {
+ const inProgressMessage = this.container.find('p');
+ expect(inProgressMessage.text()).toContain('Trying to communicate with your device');
this.u2fDevice.respondToAuthenticateRequest({
deviceData: 'this is data from the device',
});
expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
});
+
+ describe('errors', () => {
+ it('displays an error message', () => {
+ const setupButton = this.container.find('#js-login-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToAuthenticateRequest({
+ errorCode: 'error!',
+ });
+ const errorMessage = this.container.find('p');
+ return expect(errorMessage.text()).toContain('There was a problem communicating with your device');
+ });
+ return it('allows retrying authentication after an error', () => {
+ let setupButton = this.container.find('#js-login-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToAuthenticateRequest({
+ errorCode: 'error!',
+ });
+ const retryButton = this.container.find('#js-u2f-try-again');
+ retryButton.trigger('click');
+ setupButton = this.container.find('#js-login-u2f-device');
+ setupButton.trigger('click');
+ this.u2fDevice.respondToAuthenticateRequest({
+ deviceData: 'this is data from the device',
+ });
+ expect(this.component.renderAuthenticated).toHaveBeenCalledWith('{"deviceData":"this is data from the device"}');
+ });
+ });
});
});
diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
index 091645ee86f..7972ff253fe 100644
--- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
@@ -45,7 +45,7 @@ describe Gitlab::Metrics::Samplers::RubySampler do
it 'adds a metric containing garbage collection time statistics' do
expect(GC::Profiler).to receive(:total_time).and_return(0.24)
- expect(sampler.metrics[:total_time]).to receive(:set).with({}, 240)
+ expect(sampler.metrics[:total_time]).to receive(:increment).with({}, 0.24)
sampler.sample
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index b5a6d959ccb..c5d550cba1b 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -125,14 +125,6 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
end
end
-
- context 'when data_store is others' do
- before do
- build_trace_chunk.send(:write_attribute, :data_store, -1)
- end
-
- it { expect { subject }.to raise_error('Unsupported data store') }
- end
end
describe '#truncate' do
diff --git a/spec/rubocop/cop/migration/update_large_table_spec.rb b/spec/rubocop/cop/migration/update_large_table_spec.rb
index ef724fc8bad..5e08eb4f772 100644
--- a/spec/rubocop/cop/migration/update_large_table_spec.rb
+++ b/spec/rubocop/cop/migration/update_large_table_spec.rb
@@ -32,6 +32,14 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do
include_examples 'large tables', 'add_column_with_default'
end
+ context 'for the change_column_type_concurrently method' do
+ include_examples 'large tables', 'change_column_type_concurrently'
+ end
+
+ context 'for the rename_column_concurrently method' do
+ include_examples 'large tables', 'rename_column_concurrently'
+ end
+
context 'for the update_column_in_batches method' do
include_examples 'large tables', 'update_column_in_batches'
end
@@ -60,6 +68,18 @@ describe RuboCop::Cop::Migration::UpdateLargeTable do
expect(cop.offenses).to be_empty
end
+ it 'registers no offense for change_column_type_concurrently' do
+ inspect_source("change_column_type_concurrently :#{table}, :column, default: true")
+
+ expect(cop.offenses).to be_empty
+ end
+
+ it 'registers no offense for update_column_in_batches' do
+ inspect_source("rename_column_concurrently :#{table}, :column, default: true")
+
+ expect(cop.offenses).to be_empty
+ end
+
it 'registers no offense for update_column_in_batches' do
inspect_source("add_column_with_default :#{table}, :column, default: true")
diff --git a/spec/support/matchers/match_ids.rb b/spec/support/matchers/match_ids.rb
index d8424405b96..1cb6b74acac 100644
--- a/spec/support/matchers/match_ids.rb
+++ b/spec/support/matchers/match_ids.rb
@@ -10,6 +10,13 @@ RSpec::Matchers.define :match_ids do |*expected|
'matches elements by ids'
end
+ failure_message do
+ actual_ids = map_ids(actual)
+ expected_ids = map_ids(expected)
+
+ "expected IDs #{actual_ids} in:\n\n #{actual.inspect}\n\nto match IDs #{expected_ids} in:\n\n #{expected.inspect}"
+ end
+
def map_ids(elements)
elements = elements.flatten if elements.respond_to?(:flatten)