summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Newdigate <andrew@gitlab.com>2017-07-17 19:08:35 +0100
committerAndrew Newdigate <andrew@gitlab.com>2017-07-17 19:08:35 +0100
commita7e7686213227e8cd5b7f5016b04f5fdfffe9aab (patch)
treee73c9998b4d8b5a037cd221f8250ed77bb6ce723
parent9b1b65329d7fdef2361d645b59808947e6fbe159 (diff)
parent9c70d98eefa9e306171158a5e1aaca17b6e47917 (diff)
downloadgitlab-ce-an-rename-gitaly-services.tar.gz
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into an-rename-gitaly-servicesan-rename-gitaly-services
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock16
-rw-r--r--README.md2
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_bundle.js29
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue11
-rw-r--r--app/assets/javascripts/main.js1
-rw-r--r--app/assets/javascripts/users/activity_calendar.js227
-rw-r--r--app/assets/javascripts/users/calendar.js231
-rw-r--r--app/assets/javascripts/users/index.js7
-rw-r--r--app/assets/javascripts/users/user.js (renamed from app/assets/javascripts/user.js)13
-rw-r--r--app/assets/javascripts/users/user_tabs.js (renamed from app/assets/javascripts/user_tabs.js)0
-rw-r--r--app/assets/javascripts/users/users_bundle.js1
-rw-r--r--app/assets/stylesheets/framework/variables.scss12
-rw-r--r--app/assets/stylesheets/new_nav.scss9
-rw-r--r--app/assets/stylesheets/new_sidebar.scss1
-rw-r--r--app/controllers/autocomplete_controller.rb4
-rw-r--r--app/controllers/projects/commit_controller.rb11
-rw-r--r--app/controllers/projects/merge_requests_controller.rb11
-rw-r--r--app/controllers/projects/triggers_controller.rb3
-rw-r--r--app/controllers/projects_controller.rb3
-rw-r--r--app/controllers/sessions_controller.rb2
-rw-r--r--app/models/group.rb5
-rw-r--r--app/models/namespace.rb5
-rw-r--r--app/models/project.rb8
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/projects/update_service.rb47
-rw-r--r--app/views/layouts/nav/_new_group_sidebar.html.haml17
-rw-r--r--app/views/layouts/nav/_new_project_sidebar.html.haml6
-rw-r--r--app/views/projects/commit/_ci_menu.html.haml2
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml4
-rw-r--r--app/views/projects/merge_requests/show.html.haml2
-rw-r--r--app/views/shared/_personal_access_tokens_table.html.haml19
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml2
-rw-r--r--app/workers/project_service_worker.rb2
-rw-r--r--app/workers/web_hook_worker.rb2
-rw-r--r--changelogs/unreleased/19629-remove-inactive-tokens-list.yml4
-rw-r--r--changelogs/unreleased/31571-don-t-let-webhooks-jobs-go-to-the-dead-jobs-queue.yml5
-rw-r--r--changelogs/unreleased/34075-pipelines-count-mt.yml5
-rw-r--r--changelogs/unreleased/35155-upgrade-fog-core-to-1-44-3-and-its-providers-to-the-latest.yml4
-rw-r--r--changelogs/unreleased/35164-cycle-analytics-firefox.yml4
-rw-r--r--changelogs/unreleased/35181-cannot-create-label-from-board-page.yml4
-rw-r--r--changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml4
-rw-r--r--changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml4
-rw-r--r--changelogs/unreleased/pass-before-script-as-is.yml4
-rw-r--r--config/initializers/8_metrics.rb2
-rw-r--r--config/prometheus/additional_metrics.yml2
-rw-r--r--config/webpack.config.js2
-rw-r--r--doc/README.md3
-rw-r--r--doc/administration/auth/ldap.md7
-rw-r--r--doc/administration/monitoring/ip_whitelist.md39
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md51
-rw-r--r--doc/development/doc_styleguide.md4
-rw-r--r--doc/gitlab-basics/README.md2
-rw-r--r--doc/gitlab-basics/create-group.md50
-rw-r--r--doc/gitlab-basics/img/create_new_group_sidebar.pngbin2682 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/index.md8
-rw-r--r--doc/user/admin_area/monitoring/health_check.md136
-rw-r--r--doc/user/group/img/access_requests_management.png (renamed from doc/workflow/groups/access_requests_management.png)bin11186 -> 11186 bytes
-rw-r--r--doc/user/group/img/add_new_members.pngbin0 -> 67235 bytes
-rw-r--r--doc/user/group/img/create_new_group_info.png (renamed from doc/gitlab-basics/img/create_new_group_info.png)bin105173 -> 105173 bytes
-rw-r--r--doc/user/group/img/create_new_project_from_group.png (renamed from doc/gitlab-basics/img/create_new_project_from_group.png)bin3194 -> 3194 bytes
-rw-r--r--doc/user/group/img/group_settings.pngbin0 -> 28821 bytes
-rw-r--r--doc/user/group/img/groups.pngbin0 -> 202498 bytes
-rw-r--r--doc/user/group/img/membership_lock.pngbin0 -> 17333 bytes
-rw-r--r--doc/user/group/img/new_group_form.png (renamed from doc/workflow/groups/new_group_form.png)bin114515 -> 114515 bytes
-rw-r--r--doc/user/group/img/new_group_from_groups.pngbin0 -> 97271 bytes
-rw-r--r--doc/user/group/img/new_group_from_other_pages.pngbin0 -> 70899 bytes
-rw-r--r--doc/user/group/img/request_access_button.png (renamed from doc/workflow/groups/request_access_button.png)bin35917 -> 35917 bytes
-rw-r--r--doc/user/group/img/select_group_dropdown.png (renamed from doc/gitlab-basics/img/select_group_dropdown.png)bin3489 -> 3489 bytes
-rw-r--r--doc/user/group/img/share_with_group_lock.pngbin0 -> 18257 bytes
-rw-r--r--doc/user/group/img/transfer_project_to_other_group.pngbin0 -> 66460 bytes
-rw-r--r--doc/user/group/img/withdraw_access_request_button.png (renamed from doc/workflow/groups/withdraw_access_request_button.png)bin36413 -> 36413 bytes
-rw-r--r--doc/user/group/index.md208
-rw-r--r--doc/user/project/issues/index.md31
-rw-r--r--doc/user/project/issues/issues_functionalities.md6
-rw-r--r--doc/user/project/merge_requests/index.md64
-rw-r--r--doc/user/project/pages/getting_started_part_one.md19
-rw-r--r--doc/user/project/pages/introduction.md3
-rw-r--r--doc/workflow/README.md2
-rw-r--r--doc/workflow/groups.md97
-rw-r--r--doc/workflow/groups/add_member_to_group.pngbin35724 -> 0 bytes
-rw-r--r--doc/workflow/groups/group_dashboard.pngbin28155 -> 0 bytes
-rw-r--r--doc/workflow/groups/group_with_two_projects.pngbin34462 -> 0 bytes
-rw-r--r--doc/workflow/groups/new_group_button.pngbin49708 -> 0 bytes
-rw-r--r--doc/workflow/groups/override_access_level.pngbin40993 -> 0 bytes
-rw-r--r--doc/workflow/groups/project_members_via_group.pngbin39532 -> 0 bytes
-rw-r--r--doc/workflow/groups/transfer_project.pngbin43502 -> 0 bytes
-rw-r--r--doc/workflow/share_projects_with_other_groups.md2
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb2
-rw-r--r--lib/gitlab/background_migration.rb35
-rw-r--r--lib/gitlab/ci/build/step.rb3
-rw-r--r--lib/gitlab/git/branch.rb35
-rw-r--r--lib/gitlab/git/ref.rb7
-rw-r--r--lib/gitlab/git/repository.rb139
-rw-r--r--lib/gitlab/git/tag.rb4
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb30
-rw-r--r--lib/gitlab/health_checks/fs_shards_check.rb6
-rw-r--r--lib/gitlab/health_checks/simple_abstract_check.rb2
-rw-r--r--lib/gitlab/metrics/connection_rack_middleware.rb45
-rw-r--r--lib/gitlab/metrics/requests_rack_middleware.rb40
-rw-r--r--lib/gitlab/performance_bar.rb5
-rw-r--r--lib/gitlab/performance_bar/peek_query_tracker.rb9
-rw-r--r--locale/bg/gitlab.po8
-rw-r--r--locale/en/gitlab.po4
-rw-r--r--locale/eo/gitlab.po8
-rw-r--r--locale/it/gitlab.po8
-rw-r--r--locale/zh_CN/gitlab.po6
-rw-r--r--locale/zh_HK/gitlab.po6
-rw-r--r--locale/zh_TW/gitlab.po6
-rw-r--r--rubocop/cop/migration/hash_index.rb51
-rw-r--r--rubocop/rubocop.rb1
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb15
-rw-r--r--spec/controllers/metrics_controller_spec.rb16
-rw-r--r--spec/controllers/projects/commit_controller_spec.rb3
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb3
-rw-r--r--spec/controllers/projects_controller_spec.rb41
-rw-r--r--spec/factories/ci/triggers.rb7
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb12
-rw-r--r--spec/features/boards/boards_spec.rb3
-rw-r--r--spec/features/profiles/personal_access_tokens_spec.rb12
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js35
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb47
-rw-r--r--spec/lib/gitlab/background_migration_spec.rb120
-rw-r--r--spec/lib/gitlab/ci/build/step_spec.rb49
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb45
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb71
-rw-r--r--spec/lib/gitlab/health_checks/fs_shards_check_spec.rb18
-rw-r--r--spec/lib/gitlab/health_checks/simple_check_shared.rb6
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb (renamed from spec/lib/gitlab/metrics/connection_rack_middleware_spec.rb)35
-rw-r--r--spec/models/namespace_spec.rb8
-rw-r--r--spec/models/project_spec.rb2
-rw-r--r--spec/models/user_spec.rb6
-rw-r--r--spec/requests/api/version_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/hash_index_spec.rb53
-rw-r--r--spec/services/projects/update_service_spec.rb50
-rw-r--r--spec/support/matchers/have_gitlab_http_status.rb14
-rw-r--r--spec/support/sidekiq.rb4
137 files changed, 1546 insertions, 1088 deletions
diff --git a/Gemfile b/Gemfile
index 8d71ac74308..1a5837cbcec 100644
--- a/Gemfile
+++ b/Gemfile
@@ -91,7 +91,7 @@ gem 'carrierwave', '~> 1.1'
gem 'dropzonejs-rails', '~> 0.7.1'
# for backups
-gem 'fog-aws', '~> 0.9'
+gem 'fog-aws', '~> 1.4'
gem 'fog-core', '~> 1.44'
gem 'fog-google', '~> 0.5'
gem 'fog-local', '~> 0.3'
diff --git a/Gemfile.lock b/Gemfile.lock
index 41ef1e9d456..e25a7d16461 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -186,7 +186,7 @@ GEM
et-orbi (1.0.3)
tzinfo
eventmachine (1.0.8)
- excon (0.55.0)
+ excon (0.57.1)
execjs (2.6.0)
expression_parser (0.9.0)
extlib (0.9.16)
@@ -222,26 +222,26 @@ GEM
fog-json (~> 1.0)
ipaddress (~> 0.8)
xml-simple (~> 1.1)
- fog-aws (0.13.0)
+ fog-aws (1.4.0)
fog-core (~> 1.38)
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
- fog-core (1.44.1)
+ fog-core (1.44.3)
builder
excon (~> 0.49)
formatador (~> 0.2)
- fog-google (0.5.0)
+ fog-google (0.5.3)
fog-core
fog-json
fog-xml
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
- fog-local (0.3.0)
+ fog-local (0.3.1)
fog-core (~> 1.27)
- fog-openstack (0.1.6)
- fog-core (>= 1.39)
+ fog-openstack (0.1.21)
+ fog-core (>= 1.40)
fog-json (>= 1.0)
ipaddress (>= 0.8)
fog-rackspace (0.1.1)
@@ -961,7 +961,7 @@ DEPENDENCIES
flipper (~> 0.10.2)
flipper-active_record (~> 0.10.2)
fog-aliyun (~> 0.1.0)
- fog-aws (~> 0.9)
+ fog-aws (~> 1.4)
fog-core (~> 1.44)
fog-google (~> 0.5)
fog-local (~> 0.3)
diff --git a/README.md b/README.md
index 59de828e1ac..9309922ae39 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
## Test coverage
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
-- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=rake+karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
+- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
## Canonical source
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
index 2c38440a2af..687f09882a7 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
@@ -18,13 +18,26 @@ window.gl.CommitPipelinesTable = CommitPipelinesTable;
document.addEventListener('DOMContentLoaded', () => {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
- if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
- const table = new CommitPipelinesTable({
- propsData: {
- endpoint: pipelineTableViewEl.dataset.endpoint,
- helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
- },
- }).$mount();
- pipelineTableViewEl.appendChild(table.$el);
+ if (pipelineTableViewEl) {
+ // Update MR and Commits tabs
+ pipelineTableViewEl.addEventListener('update-pipelines-count', (event) => {
+ if (event.detail.pipelines &&
+ event.detail.pipelines.count &&
+ event.detail.pipelines.count.all) {
+ const badge = document.querySelector('.js-pipelines-mr-count');
+
+ badge.textContent = event.detail.pipelines.count.all;
+ }
+ });
+
+ if (pipelineTableViewEl.dataset.disableInitialization === undefined) {
+ const table = new CommitPipelinesTable({
+ propsData: {
+ endpoint: pipelineTableViewEl.dataset.endpoint,
+ helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
+ },
+ }).$mount();
+ pipelineTableViewEl.appendChild(table.$el);
+ }
}
});
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 6d31b78b36d..dd751ec97a8 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -55,6 +55,17 @@
// depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = response.pipelines || response;
this.setCommonData(pipelines);
+
+ const updatePipelinesEvent = new CustomEvent('update-pipelines-count', {
+ detail: {
+ pipelines: response,
+ },
+ });
+
+ // notifiy to update the count in tabs
+ if (this.$el.parentElement) {
+ this.$el.parentElement.dispatchEvent(updatePipelinesEvent);
+ }
});
},
},
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 892b3fab1c6..26c67fb721c 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -149,7 +149,6 @@ import './star';
import './subscription';
import './subscription_select';
import './syntax_highlight';
-import './user';
// eslint-disable-next-line global-require, import/no-commonjs
if (process.env.NODE_ENV !== 'production') require('./test_utils/');
diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js
new file mode 100644
index 00000000000..b7f50cfd083
--- /dev/null
+++ b/app/assets/javascripts/users/activity_calendar.js
@@ -0,0 +1,227 @@
+/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */
+
+import d3 from 'd3';
+
+export default class ActivityCalendar {
+ constructor(timestamps, calendar_activities_path) {
+ this.calendar_activities_path = calendar_activities_path;
+ this.clickDay = this.clickDay.bind(this);
+ this.currentSelectedDate = '';
+ this.daySpace = 1;
+ this.daySize = 15;
+ this.daySizeWithSpace = this.daySize + (this.daySpace * 2);
+ this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
+ this.months = [];
+ // Loop through the timestamps to create a group of objects
+ // The group of objects will be grouped based on the day of the week they are
+ this.timestampsTmp = [];
+ var group = 0;
+
+ var today = new Date();
+ today.setHours(0, 0, 0, 0, 0);
+
+ var oneYearAgo = new Date(today);
+ oneYearAgo.setFullYear(today.getFullYear() - 1);
+
+ var days = gl.utils.getDayDifference(oneYearAgo, today);
+
+ for (var i = 0; i <= days; i += 1) {
+ var date = new Date(oneYearAgo);
+ date.setDate(date.getDate() + i);
+
+ var day = date.getDay();
+ var count = timestamps[date.format('yyyy-mm-dd')];
+
+ // Create a new group array if this is the first day of the week
+ // or if is first object
+ if ((day === 0 && i !== 0) || i === 0) {
+ this.timestampsTmp.push([]);
+ group += 1;
+ }
+
+ var innerArray = this.timestampsTmp[group - 1];
+ // Push to the inner array the values that will be used to render map
+ innerArray.push({
+ count: count || 0,
+ date: date,
+ day: day
+ });
+ }
+
+ // Init color functions
+ this.colorKey = this.initColorKey();
+ this.color = this.initColor();
+ // Init the svg element
+ this.renderSvg(group);
+ this.renderDays();
+ this.renderMonths();
+ this.renderDayTitles();
+ this.renderKey();
+ this.initTooltips();
+ }
+
+ // Add extra padding for the last month label if it is also the last column
+ getExtraWidthPadding(group) {
+ var extraWidthPadding = 0;
+ var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth();
+ var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth();
+
+ if (lastColMonth != secondLastColMonth) {
+ extraWidthPadding = 3;
+ }
+
+ return extraWidthPadding;
+ }
+
+ renderSvg(group) {
+ var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
+ return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar');
+ }
+
+ renderDays() {
+ return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) {
+ return function(group, i) {
+ _.each(group, function(stamp, a) {
+ var lastMonth, lastMonthX, month, x;
+ if (a === 0 && stamp.day === 0) {
+ month = stamp.date.getMonth();
+ x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace;
+ lastMonth = _.last(_this.months);
+ if (lastMonth != null) {
+ lastMonthX = lastMonth.x;
+ }
+ if (lastMonth == null) {
+ return _this.months.push({
+ month: month,
+ x: x
+ });
+ } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) {
+ return _this.months.push({
+ month: month,
+ x: x
+ });
+ }
+ }
+ });
+ return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)";
+ };
+ })(this)).selectAll('rect').data(function(stamp) {
+ return stamp;
+ }).enter().append('rect').attr('x', '0').attr('y', (function(_this) {
+ return function(stamp, i) {
+ return _this.daySizeWithSpace * stamp.day;
+ };
+ })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) {
+ return function(stamp) {
+ var contribText, date, dateText;
+ date = new Date(stamp.date);
+ contribText = 'No contributions';
+ if (stamp.count > 0) {
+ contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
+ }
+ dateText = date.format('mmm d, yyyy');
+ return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
+ };
+ })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {
+ return function(stamp) {
+ if (stamp.count !== 0) {
+ return _this.color(Math.min(stamp.count, 40));
+ } else {
+ return '#ededed';
+ }
+ };
+ })(this)).attr('data-container', 'body').on('click', this.clickDay);
+ }
+
+ renderDayTitles() {
+ var days;
+ days = [
+ {
+ text: 'M',
+ y: 29 + (this.daySizeWithSpace * 1)
+ }, {
+ text: 'W',
+ y: 29 + (this.daySizeWithSpace * 3)
+ }, {
+ text: 'F',
+ y: 29 + (this.daySizeWithSpace * 5)
+ }
+ ];
+ return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) {
+ return day.y;
+ }).text(function(day) {
+ return day.text;
+ }).attr('class', 'user-contrib-text');
+ }
+
+ renderMonths() {
+ return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
+ return date.x;
+ }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) {
+ return function(date) {
+ return _this.monthNames[date.month];
+ };
+ })(this));
+ }
+
+ renderKey() {
+ const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions'];
+ const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
+
+ this.svg.append('g')
+ .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`)
+ .selectAll('rect')
+ .data(keyColors)
+ .enter()
+ .append('rect')
+ .attr('width', this.daySize)
+ .attr('height', this.daySize)
+ .attr('x', (color, i) => this.daySizeWithSpace * i)
+ .attr('y', 0)
+ .attr('fill', color => color)
+ .attr('class', 'js-tooltip')
+ .attr('title', (color, i) => keyValues[i])
+ .attr('data-container', 'body');
+ }
+
+ initColor() {
+ var colorRange;
+ colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
+ return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange);
+ }
+
+ initColorKey() {
+ return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
+ }
+
+ clickDay(stamp) {
+ var formatted_date;
+ if (this.currentSelectedDate !== stamp.date) {
+ this.currentSelectedDate = stamp.date;
+ formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate();
+ return $.ajax({
+ url: this.calendar_activities_path,
+ data: {
+ date: formatted_date
+ },
+ cache: false,
+ dataType: 'html',
+ beforeSend: function() {
+ return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>');
+ },
+ success: function(data) {
+ return $('.user-calendar-activities').html(data);
+ }
+ });
+ } else {
+ this.currentSelectedDate = '';
+ return $('.user-calendar-activities').html('');
+ }
+ }
+
+ initTooltips() {
+ return $('.js-contrib-calendar .js-tooltip').tooltip({
+ html: true
+ });
+ }
+}
diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js
deleted file mode 100644
index b11f691e424..00000000000
--- a/app/assets/javascripts/users/calendar.js
+++ /dev/null
@@ -1,231 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */
-
-import d3 from 'd3';
-
-(function() {
- this.Calendar = (function() {
- function Calendar(timestamps, calendar_activities_path) {
- this.calendar_activities_path = calendar_activities_path;
- this.clickDay = this.clickDay.bind(this);
- this.currentSelectedDate = '';
- this.daySpace = 1;
- this.daySize = 15;
- this.daySizeWithSpace = this.daySize + (this.daySpace * 2);
- this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
- this.months = [];
- // Loop through the timestamps to create a group of objects
- // The group of objects will be grouped based on the day of the week they are
- this.timestampsTmp = [];
- var group = 0;
-
- var today = new Date();
- today.setHours(0, 0, 0, 0, 0);
-
- var oneYearAgo = new Date(today);
- oneYearAgo.setFullYear(today.getFullYear() - 1);
-
- var days = gl.utils.getDayDifference(oneYearAgo, today);
-
- for (var i = 0; i <= days; i += 1) {
- var date = new Date(oneYearAgo);
- date.setDate(date.getDate() + i);
-
- var day = date.getDay();
- var count = timestamps[date.format('yyyy-mm-dd')];
-
- // Create a new group array if this is the first day of the week
- // or if is first object
- if ((day === 0 && i !== 0) || i === 0) {
- this.timestampsTmp.push([]);
- group += 1;
- }
-
- var innerArray = this.timestampsTmp[group - 1];
- // Push to the inner array the values that will be used to render map
- innerArray.push({
- count: count || 0,
- date: date,
- day: day
- });
- }
-
- // Init color functions
- this.colorKey = this.initColorKey();
- this.color = this.initColor();
- // Init the svg element
- this.renderSvg(group);
- this.renderDays();
- this.renderMonths();
- this.renderDayTitles();
- this.renderKey();
- this.initTooltips();
- }
-
- // Add extra padding for the last month label if it is also the last column
- Calendar.prototype.getExtraWidthPadding = function(group) {
- var extraWidthPadding = 0;
- var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth();
- var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth();
-
- if (lastColMonth != secondLastColMonth) {
- extraWidthPadding = 3;
- }
-
- return extraWidthPadding;
- };
-
- Calendar.prototype.renderSvg = function(group) {
- var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
- return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar');
- };
-
- Calendar.prototype.renderDays = function() {
- return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) {
- return function(group, i) {
- _.each(group, function(stamp, a) {
- var lastMonth, lastMonthX, month, x;
- if (a === 0 && stamp.day === 0) {
- month = stamp.date.getMonth();
- x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace;
- lastMonth = _.last(_this.months);
- if (lastMonth != null) {
- lastMonthX = lastMonth.x;
- }
- if (lastMonth == null) {
- return _this.months.push({
- month: month,
- x: x
- });
- } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) {
- return _this.months.push({
- month: month,
- x: x
- });
- }
- }
- });
- return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)";
- };
- })(this)).selectAll('rect').data(function(stamp) {
- return stamp;
- }).enter().append('rect').attr('x', '0').attr('y', (function(_this) {
- return function(stamp, i) {
- return _this.daySizeWithSpace * stamp.day;
- };
- })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) {
- return function(stamp) {
- var contribText, date, dateText;
- date = new Date(stamp.date);
- contribText = 'No contributions';
- if (stamp.count > 0) {
- contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
- }
- dateText = date.format('mmm d, yyyy');
- return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
- };
- })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {
- return function(stamp) {
- if (stamp.count !== 0) {
- return _this.color(Math.min(stamp.count, 40));
- } else {
- return '#ededed';
- }
- };
- })(this)).attr('data-container', 'body').on('click', this.clickDay);
- };
-
- Calendar.prototype.renderDayTitles = function() {
- var days;
- days = [
- {
- text: 'M',
- y: 29 + (this.daySizeWithSpace * 1)
- }, {
- text: 'W',
- y: 29 + (this.daySizeWithSpace * 3)
- }, {
- text: 'F',
- y: 29 + (this.daySizeWithSpace * 5)
- }
- ];
- return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) {
- return day.y;
- }).text(function(day) {
- return day.text;
- }).attr('class', 'user-contrib-text');
- };
-
- Calendar.prototype.renderMonths = function() {
- return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
- return date.x;
- }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) {
- return function(date) {
- return _this.monthNames[date.month];
- };
- })(this));
- };
-
- Calendar.prototype.renderKey = function() {
- const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions'];
- const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
-
- this.svg.append('g')
- .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`)
- .selectAll('rect')
- .data(keyColors)
- .enter()
- .append('rect')
- .attr('width', this.daySize)
- .attr('height', this.daySize)
- .attr('x', (color, i) => this.daySizeWithSpace * i)
- .attr('y', 0)
- .attr('fill', color => color)
- .attr('class', 'js-tooltip')
- .attr('title', (color, i) => keyValues[i])
- .attr('data-container', 'body');
- };
-
- Calendar.prototype.initColor = function() {
- var colorRange;
- colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
- return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange);
- };
-
- Calendar.prototype.initColorKey = function() {
- return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
- };
-
- Calendar.prototype.clickDay = function(stamp) {
- var formatted_date;
- if (this.currentSelectedDate !== stamp.date) {
- this.currentSelectedDate = stamp.date;
- formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate();
- return $.ajax({
- url: this.calendar_activities_path,
- data: {
- date: formatted_date
- },
- cache: false,
- dataType: 'html',
- beforeSend: function() {
- return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>');
- },
- success: function(data) {
- return $('.user-calendar-activities').html(data);
- }
- });
- } else {
- this.currentSelectedDate = '';
- return $('.user-calendar-activities').html('');
- }
- };
-
- Calendar.prototype.initTooltips = function() {
- return $('.js-contrib-calendar .js-tooltip').tooltip({
- html: true
- });
- };
-
- return Calendar;
- })();
-}).call(window);
diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/users/index.js
new file mode 100644
index 00000000000..ecd8e09161e
--- /dev/null
+++ b/app/assets/javascripts/users/index.js
@@ -0,0 +1,7 @@
+import ActivityCalendar from './activity_calendar';
+import User from './user';
+
+// use legacy exports until embedded javascript is refactored
+window.Calendar = ActivityCalendar;
+window.gl = window.gl || {};
+window.gl.User = User;
diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/users/user.js
index 9ef94ac7616..0b0a3e1afb4 100644
--- a/app/assets/javascripts/user.js
+++ b/app/assets/javascripts/users/user.js
@@ -1,9 +1,9 @@
-/* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */
+/* eslint-disable class-methods-use-this */
import Cookies from 'js-cookie';
import UserTabs from './user_tabs';
-class User {
+export default class User {
constructor({ action }) {
this.action = action;
this.placeProfileAvatarsToTop();
@@ -13,25 +13,22 @@ class User {
placeProfileAvatarsToTop() {
$('.profile-groups-avatars').tooltip({
- placement: 'top'
+ placement: 'top',
});
}
initTabs() {
return new UserTabs({
parentEl: '.user-profile',
- action: this.action
+ action: this.action,
});
}
hideProjectLimitMessage() {
- $('.hide-project-limit-message').on('click', e => {
+ $('.hide-project-limit-message').on('click', (e) => {
e.preventDefault();
Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove();
});
}
}
-
-window.gl = window.gl || {};
-window.gl.User = User;
diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/users/user_tabs.js
index f8e23c8624d..f8e23c8624d 100644
--- a/app/assets/javascripts/user_tabs.js
+++ b/app/assets/javascripts/users/user_tabs.js
diff --git a/app/assets/javascripts/users/users_bundle.js b/app/assets/javascripts/users/users_bundle.js
deleted file mode 100644
index a38ce4eb25e..00000000000
--- a/app/assets/javascripts/users/users_bundle.js
+++ /dev/null
@@ -1 +0,0 @@
-import './calendar';
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 3405f142428..8bd69faf84c 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -110,10 +110,10 @@ $well-light-text-color: #5b6169;
* Text
*/
$gl-font-size: 14px;
-$gl-text-color: rgba(0, 0, 0, .85);
-$gl-text-color-light: rgba(0, 0, 0, .7);
-$gl-text-color-secondary: rgba(0, 0, 0, .55);
-$gl-text-color-disabled: rgba(0, 0, 0, .35);
+$gl-text-color: #2e2e2e;
+$gl-text-color-secondary: #707070;
+$gl-text-color-tertiary: #949494;
+$gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1.0);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, .85);
$gl-text-green: $green-600;
@@ -127,7 +127,7 @@ $gl-gray-dark: #313236;
$gl-gray-light: #5c5c5c;
$gl-header-color: #4c4e54;
$gl-header-nav-hover-color: #434343;
-$placeholder-text-color: rgba(0, 0, 0, .42);
+$placeholder-text-color: $gl-text-color-tertiary;
/*
* Lists
@@ -135,7 +135,7 @@ $placeholder-text-color: rgba(0, 0, 0, .42);
$list-font-size: $gl-font-size;
$list-title-color: $gl-text-color;
$list-text-color: $gl-text-color;
-$list-text-disabled-color: $gl-text-color-disabled;
+$list-text-disabled-color: $gl-text-color-tertiary;
$list-border-light: #eee;
$list-border: rgba(0, 0, 0, 0.05);
$list-text-height: 42px;
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 73cb3a7cf4c..393d5006e24 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -284,7 +284,7 @@ header.navbar-gitlab-new {
position: relative;
top: -1px;
padding: 0 5px;
- color: rgba($black, .65);
+ color: $gl-text-color-secondary;
font-size: 10px;
line-height: 1;
background: none;
@@ -310,10 +310,10 @@ header.navbar-gitlab-new {
.breadcrumbs-links {
flex: 1;
align-self: center;
- color: $black-transparent;
+ color: $gl-text-color-quaternary;
a {
- color: rgba($black, .65);
+ color: $gl-text-color-secondary;
&:not(:first-child),
&.group-path {
@@ -368,9 +368,10 @@ header.navbar-gitlab-new {
}
.breadcrumbs-sub-title {
- margin: 2px 0 0;
+ margin: 2px 0;
font-size: 16px;
font-weight: normal;
+ line-height: 1;
ul {
margin: 0;
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index 07b487cd090..82cabefa129 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -35,6 +35,7 @@ $new-sidebar-width: 220px;
.avatar-container {
flex: 0 0 40px;
+ background-color: $white-light;
}
&:hover {
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index fe331a883c1..3120916c5bb 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -5,10 +5,10 @@ class AutocompleteController < ApplicationController
def users
@users ||= User.none
- @users = @users.search(params[:search]) if params[:search].present?
- @users = @users.where.not(id: params[:skip_users]) if params[:skip_users].present?
@users = @users.active
@users = @users.reorder(:name)
+ @users = @users.search(params[:search]) if params[:search].present?
+ @users = @users.where.not(id: params[:skip_users]) if params[:skip_users].present?
@users = @users.page(params[:page]).per(params[:per_page])
if params[:todo_filter].present? && current_user
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 14a1e11a6ea..6de125e7e80 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -38,9 +38,14 @@ class Projects::CommitController < Projects::ApplicationController
format.json do
Gitlab::PollingInterval.set_header(response, interval: 10_000)
- render json: PipelineSerializer
- .new(project: @project, current_user: @current_user)
- .represent(@pipelines)
+ render json: {
+ pipelines: PipelineSerializer
+ .new(project: @project, current_user: @current_user)
+ .represent(@pipelines),
+ count: {
+ all: @pipelines.count
+ }
+ }
end
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index a573b392591..70c41da4de5 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -112,9 +112,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
Gitlab::PollingInterval.set_header(response, interval: 10_000)
- render json: PipelineSerializer
- .new(project: @project, current_user: @current_user)
- .represent(@pipelines)
+ render json: {
+ pipelines: PipelineSerializer
+ .new(project: @project, current_user: @current_user)
+ .represent(@pipelines),
+ count: {
+ all: @pipelines.count
+ }
+ }
end
def edit
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index a5b17fa65ea..e04145dd0b3 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -69,8 +69,7 @@ class Projects::TriggersController < Projects::ApplicationController
def trigger_params
params.require(:trigger).permit(
- :description,
- trigger_schedule_attributes: [:id, :active, :cron, :cron_timezone, :ref]
+ :description
)
end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 87a69e8e6f9..c769693255c 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -50,10 +50,13 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format|
if result[:status] == :success
flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name }
+
format.html do
redirect_to(edit_project_path(@project))
end
else
+ flash[:alert] = result[:message]
+
format.html { render 'edit' }
end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index e0e72170d1e..0e8a57f8e03 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -48,7 +48,7 @@ class SessionsController < Devise::SessionsController
private
def login_counter
- @login_counter ||= Gitlab::Metrics.counter(:user_session_logins, 'User sign in count')
+ @login_counter ||= Gitlab::Metrics.counter(:user_session_logins_total, 'User sign in count')
end
# Handle an "initial setup" state, where there's only one user, it's an admin,
diff --git a/app/models/group.rb b/app/models/group.rb
index 70a4ceeffd8..dfa4e8adedd 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -2,7 +2,6 @@ require 'carrierwave/orm/activerecord'
class Group < Namespace
include Gitlab::ConfigHelper
- include Gitlab::VisibilityLevel
include AccessRequestable
include Avatarable
include Referable
@@ -103,10 +102,6 @@ class Group < Namespace
full_name
end
- def visibility_level_field
- :visibility_level
- end
-
def visibility_level_allowed_by_projects
allowed_by_projects = self.projects.where('visibility_level > ?', self.visibility_level).none?
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 15713fc5f6d..0bb04194bdb 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -5,6 +5,7 @@ class Namespace < ActiveRecord::Base
include Sortable
include Gitlab::ShellAdapter
include Gitlab::CurrentSettings
+ include Gitlab::VisibilityLevel
include Routable
include AfterCommitQueue
@@ -105,6 +106,10 @@ class Namespace < ActiveRecord::Base
end
end
+ def visibility_level_field
+ :visibility_level
+ end
+
def to_param
full_path
end
diff --git a/app/models/project.rb b/app/models/project.rb
index c0a1aadc76d..0b357d5d003 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -486,7 +486,9 @@ class Project < ActiveRecord::Base
end
def has_container_registry_tags?
- container_repositories.to_a.any?(&:has_tags?) ||
+ return @images if defined?(@images)
+
+ @images = container_repositories.to_a.any?(&:has_tags?) ||
has_root_container_repository_tags?
end
@@ -977,8 +979,6 @@ class Project < ActiveRecord::Base
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
- expire_caches_before_rename(old_path_with_namespace)
-
if has_container_registry_tags?
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
@@ -986,6 +986,8 @@ class Project < ActiveRecord::Base
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end
+ expire_caches_before_rename(old_path_with_namespace)
+
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
# If repository moved successfully we need to send update instructions to users.
# However we cannot allow rollback since we moved repository
diff --git a/app/models/user.rb b/app/models/user.rb
index dd1a391773a..8f40af24e20 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -314,7 +314,7 @@ class User < ActiveRecord::Base
table[:name].matches(pattern)
.or(table[:email].matches(pattern))
.or(table[:username].matches(pattern))
- ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, id: :desc)
+ ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
end
# searches user by given pattern
diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb
index 55d9cb13ae4..30ca95eef7a 100644
--- a/app/services/projects/update_service.rb
+++ b/app/services/projects/update_service.rb
@@ -1,22 +1,16 @@
module Projects
class UpdateService < BaseService
def execute
- # check that user is allowed to set specified visibility_level
- new_visibility = params[:visibility_level]
-
- if new_visibility && new_visibility.to_i != project.visibility_level
- unless can?(current_user, :change_visibility_level, project) &&
- Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
-
- deny_visibility_level(project, new_visibility)
- return error('Visibility level unallowed')
- end
+ unless visibility_level_allowed?
+ return error('New visibility level not allowed!')
end
- new_branch = params[:default_branch]
+ if project.has_container_registry_tags?
+ return error('Cannot rename project because it contains container registry tags!')
+ end
- if project.repository.exists? && new_branch && new_branch != project.default_branch
- project.change_head(new_branch)
+ if changing_default_branch?
+ project.change_head(params[:default_branch])
end
if project.update_attributes(params.except(:default_branch))
@@ -28,8 +22,33 @@ module Projects
success
else
- error('Project could not be updated')
+ error('Project could not be updated!')
end
end
+
+ private
+
+ def visibility_level_allowed?
+ # check that user is allowed to set specified visibility_level
+ new_visibility = params[:visibility_level]
+
+ if new_visibility && new_visibility.to_i != project.visibility_level
+ unless can?(current_user, :change_visibility_level, project) &&
+ Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
+
+ deny_visibility_level(project, new_visibility)
+ return false
+ end
+ end
+
+ true
+ end
+
+ def changing_default_branch?
+ new_branch = params[:default_branch]
+
+ project.repository.exists? &&
+ new_branch && new_branch != project.default_branch
+ end
end
end
diff --git a/app/views/layouts/nav/_new_group_sidebar.html.haml b/app/views/layouts/nav/_new_group_sidebar.html.haml
index 7b68d11041e..c80308ed0de 100644
--- a/app/views/layouts/nav/_new_group_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_group_sidebar.html.haml
@@ -55,7 +55,22 @@
%span
Members
- if current_user && can?(current_user, :admin_group, @group)
- = nav_link(path: %w[groups#projects groups#edit]) do
+ = nav_link(path: %w[groups#projects groups#edit ci_cd#show]) do
= link_to edit_group_path(@group), title: 'Settings' do
%span
Settings
+ %ul.sidebar-sub-level-items
+ = nav_link(path: 'groups#edit') do
+ = link_to edit_group_path(@group), title: 'General' do
+ %span
+ General
+
+ = nav_link(path: 'groups#projects') do
+ = link_to projects_group_path(@group), title: 'Projects' do
+ %span
+ Projects
+
+ = nav_link(controller: :ci_cd) do
+ = link_to group_settings_ci_cd_path(@group), title: 'Pipelines' do
+ %span
+ Pipelines
diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml
index 8838852803b..7c9822c5a6a 100644
--- a/app/views/layouts/nav/_new_project_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_project_sidebar.html.haml
@@ -165,7 +165,7 @@
Snippets
- if project_nav_tab? :settings
- = nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do
+ = nav_link(path: %w[projects#edit project_members#index integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span
Settings
@@ -177,8 +177,8 @@
= link_to edit_project_path(@project), title: 'General' do
%span
General
- = nav_link(controller: :members) do
- = link_to project_settings_members_path(@project), title: 'Members' do
+ = nav_link(controller: :project_members) do
+ = link_to project_project_members_path(@project), title: 'Members' do
%span
Members
- if can_edit
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
index f3f11b5b405..7338468967f 100644
--- a/app/views/projects/commit/_ci_menu.html.haml
+++ b/app/views/projects/commit/_ci_menu.html.haml
@@ -7,4 +7,4 @@
= nav_link(path: 'commit#pipelines') do
= link_to pipelines_project_commit_path(@project, @commit.id) do
Pipelines
- %span.badge= @commit.pipelines.size
+ %span.badge.js-pipelines-mr-count= @commit.pipelines.size
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 7000b289f75..7e7b7335597 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -9,8 +9,8 @@
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
.landing.content-block{ "v-if" => "!isOverviewDialogDismissed" }
- %button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box' }
- = icon("times", "@click" => "dismissOverviewDialog()")
+ %button.dismiss-button{ type: 'button', 'aria-label': 'Dismiss Cycle Analytics introduction box', "@click" => "dismissOverviewDialog()" }
+ = icon("times")
.svg-container
= custom_icon('icon_cycle_analytics_splash')
.inner-content
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 13012151349..2efc1d68190 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -47,7 +47,7 @@
%li.pipelines-tab
= link_to pipelines_project_merge_request_path(@project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines
- %span.badge= @pipelines.size
+ %span.badge.js-pipelines-mr-count= @pipelines.size
%li.diffs-tab
= link_to diffs_project_merge_request_path(@project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes
diff --git a/app/views/shared/_personal_access_tokens_table.html.haml b/app/views/shared/_personal_access_tokens_table.html.haml
index ab7a2db002e..c5e4d6e2871 100644
--- a/app/views/shared/_personal_access_tokens_table.html.haml
+++ b/app/views/shared/_personal_access_tokens_table.html.haml
@@ -39,22 +39,3 @@
- else
.settings-message.text-center
This user has no active #{type} Tokens.
-
-%hr
-
-%h5 Inactive #{type} Tokens (#{inactive_tokens.length})
-- if inactive_tokens.present?
- .table-responsive
- %table.table.inactive-tokens
- %thead
- %tr
- %th Name
- %th Created
- %tbody
- - inactive_tokens.each do |token|
- %tr
- %td= token.name
- %td= token.created_at.to_date.to_s(:medium)
-- else
- .settings-message.text-center
- This user has no inactive #{type} Tokens.
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index bdb573cb8fd..6f0b7600698 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -98,7 +98,7 @@
- if type == :boards
- if can?(current_user, :admin_list, @project)
.dropdown.prepend-left-10#js-add-list
- %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:path), project_path: @project.try(:path) } }
+ %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: { toggle: "dropdown", labels: labels_filter_path, namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path) } }
Add list
.dropdown-menu.dropdown-menu-paging.dropdown-menu-align-right.dropdown-menu-issues-board-new.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
index fdfdeab7b41..4883d848c53 100644
--- a/app/workers/project_service_worker.rb
+++ b/app/workers/project_service_worker.rb
@@ -2,6 +2,8 @@ class ProjectServiceWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ sidekiq_options dead: false
+
def perform(hook_id, data)
data = data.with_indifferent_access
Service.find(hook_id).execute(data)
diff --git a/app/workers/web_hook_worker.rb b/app/workers/web_hook_worker.rb
index ad5ddf02a12..713c0228040 100644
--- a/app/workers/web_hook_worker.rb
+++ b/app/workers/web_hook_worker.rb
@@ -2,7 +2,7 @@ class WebHookWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
- sidekiq_options retry: 4
+ sidekiq_options retry: 4, dead: false
def perform(hook_id, data, hook_name)
hook = WebHook.find(hook_id)
diff --git a/changelogs/unreleased/19629-remove-inactive-tokens-list.yml b/changelogs/unreleased/19629-remove-inactive-tokens-list.yml
new file mode 100644
index 00000000000..414e3d49e29
--- /dev/null
+++ b/changelogs/unreleased/19629-remove-inactive-tokens-list.yml
@@ -0,0 +1,4 @@
+---
+title: Remove Inactive Personal Access Tokens list from Access Tokens page
+merge_request: 12866
+author:
diff --git a/changelogs/unreleased/31571-don-t-let-webhooks-jobs-go-to-the-dead-jobs-queue.yml b/changelogs/unreleased/31571-don-t-let-webhooks-jobs-go-to-the-dead-jobs-queue.yml
new file mode 100644
index 00000000000..69900f0b314
--- /dev/null
+++ b/changelogs/unreleased/31571-don-t-let-webhooks-jobs-go-to-the-dead-jobs-queue.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent web hook and project service background jobs from going to the dead
+ jobs queue
+merge_request:
+author:
diff --git a/changelogs/unreleased/34075-pipelines-count-mt.yml b/changelogs/unreleased/34075-pipelines-count-mt.yml
new file mode 100644
index 00000000000..3846e7b06a4
--- /dev/null
+++ b/changelogs/unreleased/34075-pipelines-count-mt.yml
@@ -0,0 +1,5 @@
+---
+title: Update Pipeline's badge count in Merge Request and Commits view to match real-time
+ content
+merge_request:
+author:
diff --git a/changelogs/unreleased/35155-upgrade-fog-core-to-1-44-3-and-its-providers-to-the-latest.yml b/changelogs/unreleased/35155-upgrade-fog-core-to-1-44-3-and-its-providers-to-the-latest.yml
new file mode 100644
index 00000000000..9d9558347ba
--- /dev/null
+++ b/changelogs/unreleased/35155-upgrade-fog-core-to-1-44-3-and-its-providers-to-the-latest.yml
@@ -0,0 +1,4 @@
+---
+title: Bump fog-core to 1.44.3 and fog providers' plugins to latest
+merge_request: 12897
+author: Takuya Noguchi
diff --git a/changelogs/unreleased/35164-cycle-analytics-firefox.yml b/changelogs/unreleased/35164-cycle-analytics-firefox.yml
new file mode 100644
index 00000000000..0b7115136ca
--- /dev/null
+++ b/changelogs/unreleased/35164-cycle-analytics-firefox.yml
@@ -0,0 +1,4 @@
+---
+title: allow closing Cycle Analytics intro box in firefox
+merge_request:
+author:
diff --git a/changelogs/unreleased/35181-cannot-create-label-from-board-page.yml b/changelogs/unreleased/35181-cannot-create-label-from-board-page.yml
new file mode 100644
index 00000000000..4afe603720d
--- /dev/null
+++ b/changelogs/unreleased/35181-cannot-create-label-from-board-page.yml
@@ -0,0 +1,4 @@
+---
+title: Fix label creation from new list for subgroup projects
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml b/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml
new file mode 100644
index 00000000000..2e0573beab6
--- /dev/null
+++ b/changelogs/unreleased/fix-exact-matches-of-username-and-email-on-top-of-the-user-search.yml
@@ -0,0 +1,4 @@
+---
+title: Exact matches of username and email are now on top of the user search
+merge_request: 12868
+author:
diff --git a/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml b/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml
new file mode 100644
index 00000000000..7adc53eb8fa
--- /dev/null
+++ b/changelogs/unreleased/fix-gb-recover-from-renaming-project-with-container-images.yml
@@ -0,0 +1,4 @@
+---
+title: Recover from renaming project that has container images
+merge_request: 12840
+author:
diff --git a/changelogs/unreleased/pass-before-script-as-is.yml b/changelogs/unreleased/pass-before-script-as-is.yml
new file mode 100644
index 00000000000..ac6513dcff6
--- /dev/null
+++ b/changelogs/unreleased/pass-before-script-as-is.yml
@@ -0,0 +1,4 @@
+---
+title: Pass before_script and script as-is preserving arrays
+merge_request:
+author:
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index c80d28746d6..25630b298ce 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -123,7 +123,7 @@ Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_
Gitlab::Application.configure do |config|
# 0 should be Sentry to catch errors in this middleware
- config.middleware.insert(1, Gitlab::Metrics::ConnectionRackMiddleware)
+ config.middleware.insert(1, Gitlab::Metrics::RequestsRackMiddleware)
end
if Gitlab::Metrics.enabled?
diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml
index d33fae4182d..60355e9140c 100644
--- a/config/prometheus/additional_metrics.yml
+++ b/config/prometheus/additional_metrics.yml
@@ -7,7 +7,7 @@
- aws_elb_request_count_sum
weight: 1
queries:
- - query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) * 60'
+ - query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) / 60'
label: Total
unit: req / sec
- title: "Latency"
diff --git a/config/webpack.config.js b/config/webpack.config.js
index c3fdca59a86..1113241e402 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -66,7 +66,7 @@ var config = {
stl_viewer: './blob/stl_viewer.js',
terminal: './terminal/terminal_bundle.js',
u2f: ['vendor/u2f'],
- users: './users/users_bundle.js',
+ users: './users/index.js',
raven: './raven/index.js',
vue_merge_request_widget: './vue_merge_request_widget/index.js',
test: './test.js',
diff --git a/doc/README.md b/doc/README.md
index 9b81c409570..1a7638b3d7e 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -50,8 +50,7 @@ Shortcuts to GitLab's most visited docs:
- [Fork a project](gitlab-basics/fork-project.md)
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
- [Project access](public_access/public_access.md): Setting up your project's visibility to public, internal, or private.
-- [Groups](workflow/groups.md): Organize your projects in groups.
- - [Create a group](gitlab-basics/create-group.md)
+- [Groups](user/group/index.md): Organize your projects in groups.
- [GitLab Subgroups](user/group/subgroups/index.md)
- [Search through GitLab](user/search/index.md): Search for issues, merge requests, projects, groups, todos, and issues in Issue Boards.
- [Snippets](user/snippets.md): Snippets allow you to create little bits of code.
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index 725fc1f6076..c8987dea5e2 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -228,9 +228,14 @@ Tip: If you want to limit access to the nested members of an Active Directory
group you can use the following syntax:
```
-(memberOf=CN=My Group,DC=Example,DC=com)
+(memberOf:1.2.840.113556.1.4.1941=CN=My Group,DC=Example,DC=com)
```
+Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at
+https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx. Support for
+nested members in the user filter should not be confused with
+[group sync nested groups support (EE only)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#supported-ldap-group-types-attributes).
+
Please note that GitLab does not support the custom filter syntax used by
omniauth-ldap.
diff --git a/doc/administration/monitoring/ip_whitelist.md b/doc/administration/monitoring/ip_whitelist.md
new file mode 100644
index 00000000000..ad2773de132
--- /dev/null
+++ b/doc/administration/monitoring/ip_whitelist.md
@@ -0,0 +1,39 @@
+# IP whitelist
+
+> Introduced in GitLab 9.4.
+
+GitLab provides some [monitoring endpoints] that provide health check information
+when probed.
+
+To control access to those endpoints via IP whitelisting, you can add single
+hosts or use IP ranges:
+
+**For Omnibus installations**
+
+1. Open `/etc/gitlab/gitlab.rb` and add or uncomment the following:
+
+ ```ruby
+ gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '192.168.0.1']
+ ```
+
+1. Save the file and [reconfigure] GitLab for the changes to take effect.
+
+---
+
+**For installations from source**
+
+1. Edit `config/gitlab.yml`:
+
+ ```yaml
+ monitoring:
+ # by default only local IPs are allowed to access monitoring resources
+ ip_whitelist:
+ - 127.0.0.0/8
+ - 192.168.0.1
+ ```
+
+1. Save the file and [restart] GitLab for the changes to take effect.
+
+[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
+[restart]: ../restart_gitlab.md#installations-from-source
+[monitoring endpoints]: ../../user/admin_area/monitoring/health_check.md
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 07c05b5a6fb..7c5505de8a2 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -1,10 +1,8 @@
# GitLab Prometheus metrics
>**Note:**
-Available since [Omnibus GitLab 9.3][29118]. Currently experimental. For installations from source
-you'll have to configure it yourself.
-
-GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other [Prometheus] exporters, this endpoint requires authentication as it is available on the same URL and port as user traffic.
+Available since [Omnibus GitLab 9.3][29118]. Currently experimental. For
+installations from source you'll have to configure it yourself.
To enable the GitLab Prometheus metrics:
@@ -15,33 +13,42 @@ To enable the GitLab Prometheus metrics:
## Collecting the metrics
-Since the metrics endpoint is available on the same host and port as other traffic, it requires authentication. The token and URL to access is displayed on the [Health Check][health-check] page.
+GitLab monitors its own internal service metrics, and makes them available at the
+`/-/metrics` endpoint. Unlike other [Prometheus] exporters, in order to access
+it, the client IP needs to be [included in a whitelist][whitelist].
-Currently the embedded Prometheus server is not automatically configured to collect metrics from this endpoint. We recommend setting up another Prometheus server, because the embedded server configuration is overwritten one every reconfigure of GitLab. In the future this will not be required.
+Currently the embedded Prometheus server is not automatically configured to
+collect metrics from this endpoint. We recommend setting up another Prometheus
+server, because the embedded server configuration is overwritten once every
+[reconfigure of GitLab][reconfigure]. In the future this will not be required.
## Metrics available
In this experimental phase, only a few metrics are available:
-| Metric | Type | Description |
-| ------ | ---- | ----------- |
-| db_ping_timeout | Gauge | Whether or not the last database ping timed out |
-| db_ping_success | Gauge | Whether or not the last database ping succeeded |
-| db_ping_latency | Gauge | Round trip time of the database ping |
-| redis_ping_timeout | Gauge | Whether or not the last redis ping timed out |
-| redis_ping_success | Gauge | Whether or not the last redis ping succeeded |
-| redis_ping_latency | Gauge | Round trip time of the redis ping |
-| filesystem_access_latency | gauge | Latency in accessing a specific filesystem |
-| filesystem_accessible | gauge | Whether or not a specific filesystem is accessible |
-| filesystem_write_latency | gauge | Write latency of a specific filesystem |
-| filesystem_writable | gauge | Whether or not the filesystem is writable |
-| filesystem_read_latency | gauge | Read latency of a specific filesystem |
-| filesystem_readable | gauge | Whether or not the filesystem is readable |
-| user_sessions_logins | Counter | Counter of how many users have logged in |
+| Metric | Type | Description |
+| --------------------------------- | --------- | ----------- |
+| db_ping_timeout | Gauge | Whether or not the last database ping timed out |
+| db_ping_success | Gauge | Whether or not the last database ping succeeded |
+| db_ping_latency_seconds | Gauge | Round trip time of the database ping |
+| filesystem_access_latency_seconds | Gauge | Latency in accessing a specific filesystem |
+| filesystem_accessible | Gauge | Whether or not a specific filesystem is accessible |
+| filesystem_write_latency_seconds | Gauge | Write latency of a specific filesystem |
+| filesystem_writable | Gauge | Whether or not the filesystem is writable |
+| filesystem_read_latency_seconds | Gauge | Read latency of a specific filesystem |
+| filesystem_readable | Gauge | Whether or not the filesystem is readable |
+| http_requests_total | Counter | Rack request count |
+| http_request_duration_seconds | Histogram | HTTP response time from rack middleware |
+| rack_uncaught_errors_total | Counter | Rack connections handling uncaught errors count |
+| redis_ping_timeout | Gauge | Whether or not the last redis ping timed out |
+| redis_ping_success | Gauge | Whether or not the last redis ping succeeded |
+| redis_ping_latency_seconds | Gauge | Round trip time of the redis ping |
+| user_session_logins_total | Counter | Counter of how many users have logged in |
[← Back to the main Prometheus page](index.md)
[29118]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29118
[Prometheus]: https://prometheus.io
[restart]: ../../restart_gitlab.md#omnibus-gitlab-restart
-[health-check]: ../../../user/admin_area/monitoring/health_check.md
+[whitelist]: ../ip_whitelist.md
+[reconfigure]: ../../restart_gitlab.md#omnibus-gitlab-reconfigure
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index 5b09f79f143..36c55cbaceb 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -388,8 +388,8 @@ the style below as a guide:
1. Save the file and [restart] GitLab for the changes to take effect.
-[reconfigure]: path/to/administration/gitlab_restart.md#omnibus-gitlab-reconfigure
-[restart]: path/to/administration/gitlab_restart.md#installations-from-source
+[reconfigure]: path/to/administration/restart_gitlab.md#omnibus-gitlab-reconfigure
+[restart]: path/to/administration/restart_gitlab.md#installations-from-source
````
In this case:
diff --git a/doc/gitlab-basics/README.md b/doc/gitlab-basics/README.md
index 12466437edc..3d893ba53dd 100644
--- a/doc/gitlab-basics/README.md
+++ b/doc/gitlab-basics/README.md
@@ -6,7 +6,7 @@ Step-by-step guides on the basics of working with Git and GitLab.
- [Start using Git on the command line](start-using-git.md)
- [Create and add your SSH Keys](create-your-ssh-keys.md)
- [Create a project](create-project.md)
-- [Create a group](create-group.md)
+- [Create a group](../user/group/index.md#create-a-new-group)
- [Create a branch](create-branch.md)
- [Fork a project](fork-project.md)
- [Add a file](add-file.md)
diff --git a/doc/gitlab-basics/create-group.md b/doc/gitlab-basics/create-group.md
index b4889bb8818..985a52d88f5 100644
--- a/doc/gitlab-basics/create-group.md
+++ b/doc/gitlab-basics/create-group.md
@@ -1,50 +1,2 @@
-# How to create a group in GitLab
-Your projects in GitLab can be organized in 2 different ways:
-under your own namespace for single projects, such as `your-name/project-1` or
-under groups.
-
-If you organize your projects under a group, it works like a folder. You can
-manage your group members' permissions and access to the projects.
-
----
-
-To create a group:
-
-1. Expand the left sidebar by clicking the three bars at the upper left corner
- and then navigate to **Groups**.
-
- ![Go to groups](img/create_new_group_sidebar.png)
-
-1. Once in your groups dashboard, click on **New group**.
-
- ![Create new group information](img/create_new_group_info.png)
-
-1. Fill out the needed information:
-
- 1. Set the "Group path" which will be the namespace under which your projects
- will be hosted (path can contain only letters, digits, underscores, dashes
- and dots; it cannot start with dashes or end in dot).
- 1. The "Group name" will populate with the path. Optionally, you can change
- it. This is the name that will display in the group views.
- 1. Optionally, you can add a description so that others can briefly understand
- what this group is about.
- 1. Optionally, choose and avatar for your project.
- 1. Choose the [visibility level](../public_access/public_access.md).
-
-1. Finally, click the **Create group** button.
-
-## Add a new project to a group
-
-There are 2 different ways to add a new project to a group:
-
-- Select a group and then click on the **New project** button.
-
- ![New project](img/create_new_project_from_group.png)
-
- You can then continue on [creating a project](create-project.md).
-
-- While you are [creating a project](create-project.md), select a group namespace
- you've already created from the dropdown menu.
-
- ![Select group](img/select_group_dropdown.png)
+This document was moved to [another location](../user/group/index.md#create-a-new-group).
diff --git a/doc/gitlab-basics/img/create_new_group_sidebar.png b/doc/gitlab-basics/img/create_new_group_sidebar.png
deleted file mode 100644
index fa88d1d51c0..00000000000
--- a/doc/gitlab-basics/img/create_new_group_sidebar.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
index 35220119e9b..c6b767fff02 100644
--- a/doc/install/google_cloud_platform/index.md
+++ b/doc/install/google_cloud_platform/index.md
@@ -2,13 +2,13 @@
![GCP landing page](img/gcp_landing.png)
->**Important note:**
-GitLab has no official images in Google Cloud Platform yet. This guide serves
-as a template for when the GitLab VM will be available.
-
The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through
the [Google Cloud Launcher][launcher] program.
+GitLab's official Google Launcher apps:
+1. [GitLab Community Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-community-edition?project=gitlab-public)
+2. [GitLab Enterprise Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-enterprise-edition?project=gitlab-public)
+
## Prerequisites
There are only two prerequisites in order to install GitLab on GCP:
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index 69a9dfc3500..70934f9960a 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -6,7 +6,7 @@
be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior)
section.
- [Access token](#access-token) has been deprecated in GitLab 9.4
- in favor of [IP Whitelist](#ip-whitelist)
+ in favor of [IP whitelist](#ip-whitelist)
GitLab provides liveness and readiness probes to indicate service health and
reachability to required services. These probes report on the status of the
@@ -14,109 +14,101 @@ database connection, Redis connection, and access to the filesystem. These
endpoints [can be provided to schedulers like Kubernetes][kubernetes] to hold
traffic until the system is ready or restart the container as needed.
-## IP Whitelist
+## IP whitelist
-To access monitoring resources the client IP needs to be included in the whitelist.
-To add or remove hosts or IP ranges from the list you can edit `gitlab.rb` or `gitlab.yml`.
+To access monitoring resources, the client IP needs to be included in a whitelist.
-Example whitelist configuration:
-```yaml
-monitoring:
- ip_whitelist:
- - 127.0.0.0/8 # by default only local IPs are allowed to access monitoring resources
-```
+[Read how to add IPs to a whitelist for the monitoring endpoints.][admin].
-## Access Token (Deprecated)
+## Using the endpoint
-An access token needs to be provided while accessing the probe endpoints. The current
-accepted token can be found under the **Admin area ➔ Monitoring ➔ Health check**
-(`admin/health_check`) page of your GitLab instance.
+With default whitelist settings, the probes can be accessed from localhost:
-![access token](img/health_check_token.png)
+- `http://localhost/-/readiness`
+- `http://localhost/-/liveness`
-The access token can be passed as a URL parameter:
+which will then provide a report of system health in JSON format.
+
+Readiness example output:
```
-https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN
+{
+ "queues_check" : {
+ "status" : "ok"
+ },
+ "redis_check" : {
+ "status" : "ok"
+ },
+ "shared_state_check" : {
+ "status" : "ok"
+ },
+ "fs_shards_check" : {
+ "labels" : {
+ "shard" : "default"
+ },
+ "status" : "ok"
+ },
+ "db_check" : {
+ "status" : "ok"
+ },
+ "cache_check" : {
+ "status" : "ok"
+ }
+}
```
-which will then provide a report of system health in JSON format:
+Liveness example output:
```
{
- "db_check": {
- "status": "ok"
- },
- "redis_check": {
- "status": "ok"
- },
- "fs_shards_check": {
- "status": "ok",
- "labels": {
- "shard": "default"
- }
- }
+ "fs_shards_check" : {
+ "status" : "ok"
+ },
+ "cache_check" : {
+ "status" : "ok"
+ },
+ "db_check" : {
+ "status" : "ok"
+ },
+ "redis_check" : {
+ "status" : "ok"
+ },
+ "queues_check" : {
+ "status" : "ok"
+ },
+ "shared_state_check" : {
+ "status" : "ok"
+ }
}
```
-## Using the Endpoint
-
-With default whitelist settings, the probes can be accessed from localhost:
-
-- `http://localhost/-/readiness`
-- `http://localhost/-/liveness`
-
## Status
On failure, the endpoint will return a `500` HTTP status code. On success, the endpoint
will return a valid successful HTTP status code, and a `success` message.
-## Old behavior
+## Access token (Deprecated)
->**Notes:**
- - Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1.
- - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and will
- be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior)
- section.
-
-GitLab provides a health check endpoint for uptime monitoring on the `health_check` web
-endpoint. The health check reports on the overall system status based on the status of
-the database connection, the state of the database migrations, and the ability to write
-and access the cache. This endpoint can be provided to uptime monitoring services like
-[Pingdom][pingdom], [Nagios][nagios-health], and [NewRelic][newrelic-health].
-
-Once you have the [access token](#access-token) or your client IP is [whitelisted](#ip-whitelist),
-health information can be retrieved as plain text, JSON, or XML using the `health_check` endpoint:
-
-- `https://gitlab.example.com/health_check?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check.xml?token=ACCESS_TOKEN`
-
-You can also ask for the status of specific services:
-
-- `https://gitlab.example.com/health_check/cache.json?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check/database.json?token=ACCESS_TOKEN`
-- `https://gitlab.example.com/health_check/migrations.json?token=ACCESS_TOKEN`
+>**Note:**
+Access token has been deprecated in GitLab 9.4
+in favor of [IP whitelist](#ip-whitelist)
-For example, the JSON output of the following health check:
+An access token needs to be provided while accessing the probe endpoints. The current
+accepted token can be found under the **Admin area ➔ Monitoring ➔ Health check**
+(`admin/health_check`) page of your GitLab instance.
-```bash
-curl --header "TOKEN: ACCESS_TOKEN" https://gitlab.example.com/health_check.json
-```
+![access token](img/health_check_token.png)
-would be like:
+The access token can be passed as a URL parameter:
```
-{"healthy":true,"message":"success"}
+https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN
```
-On failure, the endpoint will return a `500` HTTP status code. On success, the endpoint
-will return a valid successful HTTP status code, and a `success` message. Ideally your
-uptime monitoring should look for the success message.
-
[ce-10416]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/10416
[ce-3888]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3888
[pingdom]: https://www.pingdom.com
[nagios-health]: https://nagios-plugins.org/doc/man/check_http.html
[newrelic-health]: https://docs.newrelic.com/docs/alerts/alert-policies/downtime-alerts/availability-monitoring
[kubernetes]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/
+[admin]: ../../../administration/monitoring/ip_whitelist.md
diff --git a/doc/workflow/groups/access_requests_management.png b/doc/user/group/img/access_requests_management.png
index 36deaa89a70..36deaa89a70 100644
--- a/doc/workflow/groups/access_requests_management.png
+++ b/doc/user/group/img/access_requests_management.png
Binary files differ
diff --git a/doc/user/group/img/add_new_members.png b/doc/user/group/img/add_new_members.png
new file mode 100644
index 00000000000..53f5596de23
--- /dev/null
+++ b/doc/user/group/img/add_new_members.png
Binary files differ
diff --git a/doc/gitlab-basics/img/create_new_group_info.png b/doc/user/group/img/create_new_group_info.png
index 8d2501d9f7a..8d2501d9f7a 100644
--- a/doc/gitlab-basics/img/create_new_group_info.png
+++ b/doc/user/group/img/create_new_group_info.png
Binary files differ
diff --git a/doc/gitlab-basics/img/create_new_project_from_group.png b/doc/user/group/img/create_new_project_from_group.png
index c35234660db..c35234660db 100644
--- a/doc/gitlab-basics/img/create_new_project_from_group.png
+++ b/doc/user/group/img/create_new_project_from_group.png
Binary files differ
diff --git a/doc/user/group/img/group_settings.png b/doc/user/group/img/group_settings.png
new file mode 100644
index 00000000000..629cd0729aa
--- /dev/null
+++ b/doc/user/group/img/group_settings.png
Binary files differ
diff --git a/doc/user/group/img/groups.png b/doc/user/group/img/groups.png
new file mode 100644
index 00000000000..6211f999d5e
--- /dev/null
+++ b/doc/user/group/img/groups.png
Binary files differ
diff --git a/doc/user/group/img/membership_lock.png b/doc/user/group/img/membership_lock.png
new file mode 100644
index 00000000000..d31fbb43375
--- /dev/null
+++ b/doc/user/group/img/membership_lock.png
Binary files differ
diff --git a/doc/workflow/groups/new_group_form.png b/doc/user/group/img/new_group_form.png
index 91727ab5336..91727ab5336 100644
--- a/doc/workflow/groups/new_group_form.png
+++ b/doc/user/group/img/new_group_form.png
Binary files differ
diff --git a/doc/user/group/img/new_group_from_groups.png b/doc/user/group/img/new_group_from_groups.png
new file mode 100644
index 00000000000..baf34244cb2
--- /dev/null
+++ b/doc/user/group/img/new_group_from_groups.png
Binary files differ
diff --git a/doc/user/group/img/new_group_from_other_pages.png b/doc/user/group/img/new_group_from_other_pages.png
new file mode 100644
index 00000000000..014a7088af2
--- /dev/null
+++ b/doc/user/group/img/new_group_from_other_pages.png
Binary files differ
diff --git a/doc/workflow/groups/request_access_button.png b/doc/user/group/img/request_access_button.png
index f1aae6afed7..f1aae6afed7 100644
--- a/doc/workflow/groups/request_access_button.png
+++ b/doc/user/group/img/request_access_button.png
Binary files differ
diff --git a/doc/gitlab-basics/img/select_group_dropdown.png b/doc/user/group/img/select_group_dropdown.png
index 68fc950304c..68fc950304c 100644
--- a/doc/gitlab-basics/img/select_group_dropdown.png
+++ b/doc/user/group/img/select_group_dropdown.png
Binary files differ
diff --git a/doc/user/group/img/share_with_group_lock.png b/doc/user/group/img/share_with_group_lock.png
new file mode 100644
index 00000000000..8df41bf9465
--- /dev/null
+++ b/doc/user/group/img/share_with_group_lock.png
Binary files differ
diff --git a/doc/user/group/img/transfer_project_to_other_group.png b/doc/user/group/img/transfer_project_to_other_group.png
new file mode 100644
index 00000000000..042c002f83f
--- /dev/null
+++ b/doc/user/group/img/transfer_project_to_other_group.png
Binary files differ
diff --git a/doc/workflow/groups/withdraw_access_request_button.png b/doc/user/group/img/withdraw_access_request_button.png
index c5d8ef6c04f..c5d8ef6c04f 100644
--- a/doc/workflow/groups/withdraw_access_request_button.png
+++ b/doc/user/group/img/withdraw_access_request_button.png
Binary files differ
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
new file mode 100644
index 00000000000..2691cf7d671
--- /dev/null
+++ b/doc/user/group/index.md
@@ -0,0 +1,208 @@
+# Groups
+
+With GitLab Groups you can assemble related projects together
+and grant members access to several projects at once.
+
+Groups can also be nested in [subgroups](subgroups/index.md).
+
+Find your groups by expanding the left menu and clicking **Groups**:
+
+![GitLab Groups](img/groups.png)
+
+The Groups page displays all groups you are a member of, how many projects it holds,
+how many members it has, the group visibility, and, if you have enough permissions,
+a link to the group settings. By clicking the last button you can leave that group.
+
+## Use cases
+
+You can create groups for numerous reasons. To name a few:
+
+- Organize related projects under the same [namespace](#namespaces), add members to that
+group and grant access to all their projects at once
+- Create a group, include members of your team, and make it easier to
+`@mention` all the team at once in issues and merge requests
+ - Create a group for your company members, and create [subgroups](subgroups/index.md)
+ for each individual team. Let's say you create a group called `company-team`, and among others,
+ you created subgroups in this group for each individual team `backend-team`,
+ `frontend-team`, and `production-team`:
+ 1. When you start a new implementation from an issue, you add a comment:
+ _"`@company-team`, let's do it! `@company-team/backend-team` you're good to go!"_
+ 1. When your backend team needs help from frontend, they add a comment:
+ _"`@company-team/frontend-team` could you help us here please?"_
+ 1. When the frontend team completes their implementation, they comment:
+ _"`@company-team/backend-team`, it's done! Let's ship it `@company-team/production-team`!"_
+
+## Namespaces
+
+In GitLab, a namespace is a unique name to be used as a user name, a group name, or a subgroup name.
+
+- `http://gitlab.example.com/username`
+- `http://gitlab.example.com/groupname`
+- `http://gitlab.example.com/groupname/subgroup_name`
+
+For example, consider a user called John:
+
+1. John creates his account on GitLab.com with the username `jonh`;
+his profile will be accessed under `https://gitlab.example.com/john`
+1. John creates a group for his team with the groupname `john-team`;
+his group and its projects will be accessed under `https://gitlab.example.com/john-team`
+1. John creates a subgroup of `john-team` with the subgroup name `marketing`;
+his subgroup and its projects will be accessed under `https://gitlab.example.com/john-team/marketing`
+
+By doing so:
+
+- Any team member mentions John with `@john`
+- John mentions everyone from his team with `@john-team`
+- John mentions only his marketing team with `@john-team/marketing`
+
+## Create a new group
+
+You can create a group in GitLab from:
+
+1. The Groups page: expand the left menu, click **Groups**, and click the green button **New group**:
+
+ ![new group from groups page](img/new_group_from_groups.png)
+
+1. Elsewhere: expand the `plus` sign button on the top navbar and choose **New group**:
+
+ ![new group from elsewhere](img/new_group_from_other_pages.png)
+
+Add the following information:
+
+![new group info](img/create_new_group_info.png)
+
+1. Set the **Group path** which will be the **namespace** under which your projects
+ will be hosted (path can contain only letters, digits, underscores, dashes
+ and dots; it cannot start with dashes or end in dot).
+1. The **Group name** will populate with the path. Optionally, you can change
+ it. This is the name that will display in the group views.
+1. Optionally, you can add a description so that others can briefly understand
+ what this group is about.
+1. Optionally, choose an avatar for your project.
+1. Choose the [visibility level](../../public_access/public_access.md).
+
+## Add users to a group
+
+Add members to a group by navigating to the group's dashboard, and clicking **Members**:
+
+![add members to group](img/add_new_members.png)
+
+Select the [permission level][permissions] and add the new member. You can also set the expiring
+date for that user, from which they will no longer have access to your group.
+
+One of the benefits of putting multiple projects in one group is that you can
+give a user to access to all projects in the group with one action.
+
+Consider we have a group with two projects:
+
+- On the **Group Members** page we can now add a new user to the group.
+- Now because this user is a **Developer** member of the group, he automatically
+gets **Developer** access to **all projects** within that group.
+
+If necessary, you can increase the access level of an individual user for a specific project,
+by adding them again as a new member to the project with the new permission levels.
+
+## Request access to a group
+
+As a group owner you can enable or disable non members to request access to
+your group. Go to the group settings and click on **Allow users to request access**.
+
+As a user, you can request to be a member of a group. Go to the group you'd
+like to be a member of, and click the **Request Access** button on the right
+side of your screen.
+
+![Request access button](img/request_access_button.png)
+
+---
+
+Group owners and masters will be notified of your request and will be able to approve or
+decline it on the members page.
+
+![Manage access requests](img/access_requests_management.png)
+
+---
+
+If you change your mind before your request is approved, just click the
+**Withdraw Access Request** button.
+
+![Withdraw access request button](img/withdraw_access_request_button.png)
+
+## Add projects to a group
+
+There are two different ways to add a new project to a group:
+
+- Select a group and then click on the **New project** button.
+
+ ![New project](img/create_new_project_from_group.png)
+
+ You can then continue on [creating a project](../../gitlab-basics/create-project.md).
+
+- While you are creating a project, select a group namespace
+ you've already created from the dropdown menu.
+
+ ![Select group](img/select_group_dropdown.png)
+
+## Transfer an existing project into a group
+
+You can transfer an existing project into a group as long as you have at least **Master** [permissions][permissions] to that group
+and if you are an **Owner** of the project.
+
+![Transfer a project to a new namespace](img/transfer_project_to_other_group.png)
+
+Find this option under your project's settings.
+
+GitLab administrators can use the admin interface to move any project to any namespace if needed.
+
+## Manage group memberships via LDAP
+
+In GitLab Enterprise Edition it is possible to manage GitLab group memberships using LDAP groups.
+See [the GitLab Enterprise Edition documentation](../../integration/ldap.md) for more information.
+
+## Group settings
+
+Once you have created a group, you can manage its settings by navigating to
+the group's dashboard, and clicking **Settings**.
+
+![group settings](img/group_settings.png)
+
+### General settings
+
+Besides giving you the option to edit any settings you've previously
+set when [creating the group](#create-a-new-group), you can also
+access further configurations for your group.
+
+#### Enforce 2FA to group members
+
+Add a secury layer to your group by
+[enforcing two-factor authentication (2FA)](../../security/two_factor_authentication.md#enforcing-2fa-for-all-users-in-a-group)
+to all group members.
+
+#### Member Lock (EES/EEP)
+
+Available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/),
+with **Member Lock** it is possible to lock membership in project to the
+level of members in group.
+
+Learn more about [Member Lock](https://docs.gitlab.com/ee/user/group/index.html#member-lock-ees-eep).
+
+#### Share with group lock (EES/EEP)
+
+In [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)
+it is possible to prevent projects in a group from [sharing
+a project with another group](../../workflow/share_projects_with_other_groups.md).
+This allows for tighter control over project access.
+
+Learn more about [Share with group lock](https://docs.gitlab.com/ee/user/group/index.html#share-with-group-lock-ees-eep).
+
+### Advanced settings
+
+- **Projects**: view all projects within that group, add members to each project,
+access each project's settings, and remove any project from the same screen.
+- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md)
+and [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group (Push Rules is available in [GitLab Enteprise Edition Starter][ee].)
+- **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events)
+for the group (GitLab admins only, available in [GitLab Enterprise Edition Starter][ee]).
+- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
+
+[permissions]: ../permissions.md#permissions
+[ee]: https://about.gitlab.com/products/ \ No newline at end of file
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index e55e2aea023..1f78849a92c 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -5,13 +5,13 @@ for tracking the evolution of a new idea or the process
of solving a problem.
It allows you, your team, and your collaborators to share
-and discuss proposals, before and while implementing them.
+and discuss proposals before and while implementing them.
Issues and the GitLab Issue Tracker are available in all
[GitLab Products](https://about.gitlab.com/products/) as
part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
-## Use-Cases
+## Use cases
Issues can have endless applications. Just to exemplify, these are
some cases for which creating issues are most used:
@@ -23,7 +23,28 @@ some cases for which creating issues are most used:
- Obtaining support
- Elaborating new code implementations
-See also the blog post [Always start a discussion with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/).
+See also the blog post "[Always start a discussion with an issue](https://about.gitlab.com/2016/03/03/start-with-an-issue/)".
+
+### Keep private things private
+
+For instance, let's assume you have a public project but want to start a discussion on something
+you don't want to be public. With [Confidential Issues](#confidential-issues),
+you can discuss private matters among the project members, and still keep
+your project public, open to collaboration.
+
+### Streamline collaboration
+
+With [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
+available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)
+you can streamline collaboration and allow shared responsibilities to be clearly displayed.
+All assignees are shown across your workflows and receive notifications (as they
+would as single assignees), simplifying communication and ownership.
+
+### Consistent collaboration
+
+Create [issue templates](#issue-templates) to make collaboration consistent and
+containing all information you need. For example, you can create a template
+for feature proposals and another one for bug reports.
## Issue Tracker
@@ -96,8 +117,8 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue
Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature.
-[Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards)
-are available only in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/).
+With [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/), you can also
+create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
### Issue's API
diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md
index 294176e61f9..138276edf07 100644
--- a/doc/user/project/issues/issues_functionalities.md
+++ b/doc/user/project/issues/issues_functionalities.md
@@ -43,7 +43,7 @@ assigned to them if they created the issue themselves.
##### 3.1. Multiple Assignees (EES/EEP)
-Issue Weights are only available in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/).
+Multiple Assignees are only available in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/).
Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams
@@ -52,9 +52,7 @@ where there is shared ownership of an issue.
In GitLab Enterprise Edition, you can also select multiple assignees
to an issue.
-> **Note:**
-Multiple Assignees was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1904)
-in [GitLab Enterprise Edition 9.2](https://about.gitlab.com/2017/05/22/gitlab-9-2-released/#multiple-assignees-for-issues).
+Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html).
#### 4. Milestone
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 954454f7e7a..9bdf2a998d3 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -3,6 +3,59 @@
Merge requests allow you to exchange changes you made to source code and
collaborate with other people on the same project.
+## Overview
+
+A Merge Request (**MR**) is the basis of GitLab as a code collaboration
+and version control platform.
+Is it simple as the name implies: a _request_ to _merge_ one branch into another.
+
+With GitLab merge requests, you can:
+
+- Compare the changes between two [branches](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell#_git_branching)
+- [Review and discuss](../../discussions/index.md#discussions) the proposed modifications inline
+- Live preview the changes when [Review Apps](../../../ci/review_apps/index.md) is configured for your project
+- Build, test, and deploy your code in a per-branch basis with built-in [GitLab CI/CD](../../../ci/README.md)
+- Prevent the merge request from being merged before it's ready with [WIP MRs](#work-in-progress-merge-requests)
+- View the deployment process through [Pipeline Graphs](../../../ci/pipelines.md#pipeline-graphs)
+- [Automatically close the issue(s)](../../project/issues/closing_issues.md#via-merge-request) that originated the implementation proposed in the merge request
+- Assign it to any registered user, and change the assignee how many times you need
+- Assign a [milestone](../../project/milestones/index.md) and track the development of a broader implementation
+- Organize your issues and merge requests consistently throughout the project with [labels](../../project/labels.md)
+- Add a time estimation and the time spent with that merge request with [Time Tracking](../../../workflow/time_tracking.html#time-tracking)
+- [Resolve merge conflicts from the UI](#resolve-conflicts)
+
+With **[GitLab Enterprise Edition][ee]**, you can also:
+
+- View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) (available only in GitLab Enterprise Edition Premium)
+- Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers (available in GitLab Enterprise Edition Starter)
+- Enable [fast-forward merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/fast_forward_merge.html) (available in GitLab Enterprise Edition Starter)
+- [Squash and merge](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html) for a cleaner commit history (available in GitLab Enterprise Edition Starter)
+- Enable [semi-linear history merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch (available in GitLab Enterprise Edition Starter)
+- Analise the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Enterprise Edition Starter)
+
+## Use cases
+
+A. Consider you are a software developer working in a team:
+
+1. You checkout a new branch, and submit your changes through a merge request
+1. You gather feedback from your team
+1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Enterprise Edition Starter)
+1. You build and test your changes with GitLab CI/CD
+1. You request the approval from your manager
+1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Enterprise Edition Starter)
+1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#manual-actions) for GitLab CI/CD
+1. Your implementations were successfully shipped to your customer
+
+B. Consider you're a web developer writing a webpage for your company's:
+
+1. You checkout a new branch, and submit a new page through a merge request
+1. You gather feedback from your reviewers
+1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md)
+1. You request your web designers for their implementation
+1. You request the [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your manager (available in GitLab Enterprise Edition Starter)
+1. Once approved, your merge request is [squashed and merged](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) (Squash and Merge is available in GitLab Enterprise Edition Starter)
+1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production
+
## Authorization for merge requests
There are two main ways to have a merge request flow with GitLab:
@@ -79,6 +132,16 @@ specific commit page.
You can append `?w=1` while on the diffs page of a merge request to ignore any
whitespace changes.
+## Live preview with Review Apps
+
+If you configured [Review Apps](https://about.gitlab.com/features/review-apps/) for your project,
+you can preview the changes submitted to a feature-branch through a merge request
+in a per-branch basis. No need to checkout the branch, install and preview locally;
+all your changes will be available to preview by anyone with the Review Apps link.
+
+[Read more about Review Apps.](../../../ci/review_apps/index.md)
+
+
## Tips
Here are some tips that will help you be more efficient with merge requests in
@@ -167,3 +230,4 @@ git checkout origin/merge-requests/1
```
[protected branches]: ../protected_branches.md
+[ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition"
diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md
index 2f104c7becc..46fa4378fe7 100644
--- a/doc/user/project/pages/getting_started_part_one.md
+++ b/doc/user/project/pages/getting_started_part_one.md
@@ -41,7 +41,7 @@ server up and running for your GitLab instance.
Before we begin, let's understand a few concepts first.
-### Static sites
+## Static sites
GitLab Pages only supports static websites, meaning,
your output files must be HTML, CSS, and JavaScript only.
@@ -51,14 +51,14 @@ CSS, and JS, or use a [Static Site Generator (SSG)](https://www.staticgen.com/)
to simplify your code and build the static site for you,
which is highly recommendable and much faster than hardcoding.
-#### Further Reading
+### Further reading
- Read through this technical overview on [Static versus Dynamic Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/)
- Understand [how modern Static Site Generators work](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) and what you can add to your static site
- You can use [any SSG with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/)
- Fork an [example project](https://gitlab.com/pages) to build your website based upon
-### GitLab Pages domain
+## GitLab Pages domain
If you set up a GitLab Pages project on GitLab.com,
it will automatically be accessible under a
@@ -73,9 +73,9 @@ Pages wildcard domain. This guide is valid for any GitLab instance,
you just need to replace Pages wildcard domain on GitLab.com
(`*.gitlab.io`) with your own.
-#### Practical examples
+### Practical examples
-**Project Websites:**
+#### Project Websites
- You created a project called `blog` under your username `john`,
therefore your project URL is `https://gitlab.com/john/blog/`.
@@ -87,16 +87,21 @@ URL is `https://gitlab.com/websites/blog/`. Once you enable
GitLab Pages for this project, the site will live under
`https://websites.gitlab.io/blog/`.
-**User and Group Websites:**
+#### User and Group Websites
- Under your username, `john`, you created a project called
`john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`.
Once you enable GitLab Pages for your project, your website
will be published under `https://john.gitlab.io`.
- Under your group `websites`, you created a project called
-`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project,
+`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`.
+Once you enable GitLab Pages for your project,
your website will be published under `https://websites.gitlab.io`.
+>**Note:**
+GitLab Pages [does **not** support subgroups](../../group/subgroups/index.md#limitations).
+You can only create the highest level group website.
+
**General example:**
- On GitLab.com, a project site will always be available under
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index deaceabb7c5..9ecf7a3a8e7 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -398,6 +398,9 @@ don't redirect HTTP to HTTPS.
[rfc]: https://tools.ietf.org/html/rfc2818#section-3.1 "HTTP Over TLS RFC"
+GitLab Pages [does **not** support subgroups](../../group/subgroups/index.md#limitations).
+You can only create the highest level group website.
+
## Redirects in GitLab Pages
Since you cannot use any custom server configuration files, like `.htaccess` or
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 54d4028a50a..925bbf76d49 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -6,7 +6,7 @@
- [Description templates](../user/project/description_templates.md)
- [Feature branch workflow](workflow.md)
- [GitLab Flow](gitlab_flow.md)
-- [Groups](groups.md)
+- [Groups](../user/group/index.md)
- Issues - The GitLab Issue Tracker is an advanced and complete tool for
tracking the evolution of a new idea or the process of solving a problem.
- [Confidential issues](../user/project/issues/confidential_issues.md)
diff --git a/doc/workflow/groups.md b/doc/workflow/groups.md
index 1645e7e8d65..06eec1ed928 100644
--- a/doc/workflow/groups.md
+++ b/doc/workflow/groups.md
@@ -1,97 +1,2 @@
-# GitLab Groups
-GitLab groups allow you to group projects into directories and give users access to several projects at once.
-
-When you create a new project in GitLab, the default namespace for the project is the personal namespace associated with your GitLab user.
-In this document we will see how to create groups, put projects in groups and manage who can access the projects in a group.
-
-## Creating groups
-
-You can create a group by going to the 'Groups' tab of the GitLab dashboard and clicking the 'New group' button.
-
-![Click the 'New group' button in the 'Groups' tab](groups/new_group_button.png)
-
-Next, enter the path and name (required) and the optional description and group avatar.
-
-![Fill in the path for your new group](groups/new_group_form.png)
-
-When your group has been created you are presented with the group dashboard feed, which will be empty.
-
-![Group dashboard](groups/group_dashboard.png)
-
-You can use the 'New project' button to add a project to the new group.
-
-## Transferring an existing project into a group
-
-You can transfer an existing project into a group you have at least Master access in from the project settings page.
-The option to transfer a project is only available if you are the Owner of the project.
-First scroll down to the 'Dangerous settings' and click 'Show them to me'.
-Now you can pick any of the groups you have at least Master access in as the new namespace for the group.
-
-![Transfer a project to a new namespace](groups/transfer_project.png)
-
-GitLab administrators can use the admin interface to move any project to any namespace if needed.
-
-## Adding users to a group
-
-One of the benefits of putting multiple projects in one group is that you can give a user to access to all projects in the group with one action.
-
-Suppose we have a group with two projects.
-
-![Group with two projects](groups/group_with_two_projects.png)
-
-On the 'Group Members' page we can now add a new user Barry to the group.
-
-![Add user Barry to the group](groups/add_member_to_group.png)
-
-Now because Barry is a 'Developer' member of the 'Open Source' group, he automatically gets 'Developer' access to all projects in the 'Open Source' group.
-
-![Barry has 'Developer' access to GitLab CI](groups/project_members_via_group.png)
-
-If necessary, you can increase the access level of an individual user for a specific project, by adding them as a Member to the project.
-
-![Barry effectively has 'Master' access to GitLab CI now](groups/override_access_level.png)
-
-## Requesting access to a group
-
-As a group owner you can enable or disable non members to request access to
-your group. Go to the group settings and click on **Allow users to request access**.
-
-As a user, you can request to be a member of a group. Go to the group you'd
-like to be a member of, and click the **Request Access** button on the right
-side of your screen.
-
-![Request access button](groups/request_access_button.png)
-
----
-
-Group owners & masters will be notified of your request and will be able to approve or
-decline it on the members page.
-
-![Manage access requests](groups/access_requests_management.png)
-
----
-
-If you change your mind before your request is approved, just click the
-**Withdraw Access Request** button.
-
-![Withdraw access request button](groups/withdraw_access_request_button.png)
-
-## Managing group memberships via LDAP
-
-In GitLab Enterprise Edition it is possible to manage GitLab group memberships using LDAP groups.
-See [the GitLab Enterprise Edition documentation](http://docs.gitlab.com/ee/integration/ldap.html) for more information.
-
-## Allowing only admins to create groups
-
-By default, any GitLab user can create new groups.
-This ability can be disabled for individual users from the admin panel.
-It is also possible to configure GitLab so that new users default to not being able to create groups:
-
-```
-# For omnibus-gitlab, put the following in /etc/gitlab/gitlab.rb
-gitlab_rails['gitlab_default_can_create_group'] = false
-
-# For installations from source, uncomment the 'default_can_create_group'
-# line in /home/git/gitlab/config/gitlab.yml
-```
+This document was moved to [another location](../user/group/index.md).
diff --git a/doc/workflow/groups/add_member_to_group.png b/doc/workflow/groups/add_member_to_group.png
deleted file mode 100644
index a10d5032bb0..00000000000
--- a/doc/workflow/groups/add_member_to_group.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/groups/group_dashboard.png b/doc/workflow/groups/group_dashboard.png
deleted file mode 100644
index a5829f25808..00000000000
--- a/doc/workflow/groups/group_dashboard.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/groups/group_with_two_projects.png b/doc/workflow/groups/group_with_two_projects.png
deleted file mode 100644
index 76d0a1b8ab2..00000000000
--- a/doc/workflow/groups/group_with_two_projects.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/groups/new_group_button.png b/doc/workflow/groups/new_group_button.png
deleted file mode 100644
index 7155d6280bd..00000000000
--- a/doc/workflow/groups/new_group_button.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/groups/override_access_level.png b/doc/workflow/groups/override_access_level.png
deleted file mode 100644
index 2b3e9a49842..00000000000
--- a/doc/workflow/groups/override_access_level.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/groups/project_members_via_group.png b/doc/workflow/groups/project_members_via_group.png
deleted file mode 100644
index 878c9a03ac9..00000000000
--- a/doc/workflow/groups/project_members_via_group.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/groups/transfer_project.png b/doc/workflow/groups/transfer_project.png
deleted file mode 100644
index 52161817f11..00000000000
--- a/doc/workflow/groups/transfer_project.png
+++ /dev/null
Binary files differ
diff --git a/doc/workflow/share_projects_with_other_groups.md b/doc/workflow/share_projects_with_other_groups.md
index 8e50cb03e63..40d756bc199 100644
--- a/doc/workflow/share_projects_with_other_groups.md
+++ b/doc/workflow/share_projects_with_other_groups.md
@@ -5,7 +5,7 @@ to a project with a single action.
## Groups as collections of users
-Groups are used primarily to [create collections of projects](groups.md), but you can also
+Groups are used primarily to [create collections of projects](../user/group/index.md), but you can also
take advantage of the fact that groups define collections of _users_, namely the group
members.
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 56ad2c77c7d..cf3a0336792 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -80,6 +80,8 @@ module Ci
artifacts: job[:artifacts],
cache: job[:cache],
dependencies: job[:dependencies],
+ before_script: job[:before_script],
+ script: job[:script],
after_script: job[:after_script],
environment: job[:environment]
}.compact }
diff --git a/lib/gitlab/background_migration.rb b/lib/gitlab/background_migration.rb
index d95ecd7b291..b0741b1fba7 100644
--- a/lib/gitlab/background_migration.rb
+++ b/lib/gitlab/background_migration.rb
@@ -1,24 +1,45 @@
module Gitlab
module BackgroundMigration
+ def self.queue
+ @queue ||= BackgroundMigrationWorker.sidekiq_options['queue']
+ end
+
# Begins stealing jobs from the background migrations queue, blocking the
# caller until all jobs have been completed.
#
+ # When a migration raises a StandardError is is going to be retries up to
+ # three times, for example, to recover from a deadlock.
+ #
+ # When Exception is being raised, it enqueues the migration again, and
+ # re-raises the exception.
+ #
# steal_class - The name of the class for which to steal jobs.
def self.steal(steal_class)
- queue = Sidekiq::Queue
- .new(BackgroundMigrationWorker.sidekiq_options['queue'])
+ enqueued = Sidekiq::Queue.new(self.queue)
+ scheduled = Sidekiq::ScheduledSet.new
- queue.each do |job|
- migration_class, migration_args = job.args
+ [scheduled, enqueued].each do |queue|
+ queue.each do |job|
+ migration_class, migration_args = job.args
- next unless migration_class == steal_class
+ next unless job.queue == self.queue
+ next unless migration_class == steal_class
- perform(migration_class, migration_args)
+ begin
+ perform(migration_class, migration_args, retries: 3) if job.delete
+ rescue Exception # rubocop:disable Lint/RescueException
+ BackgroundMigrationWorker # enqueue this migration again
+ .perform_async(migration_class, migration_args)
- job.delete
+ raise
+ end
+ end
end
end
+ ##
+ # Performs a background migration.
+ #
# class_name - The name of the background migration class as defined in the
# Gitlab::BackgroundMigration namespace.
#
diff --git a/lib/gitlab/ci/build/step.rb b/lib/gitlab/ci/build/step.rb
index ee034d9cc56..411f67f8ce7 100644
--- a/lib/gitlab/ci/build/step.rb
+++ b/lib/gitlab/ci/build/step.rb
@@ -12,7 +12,8 @@ module Gitlab
class << self
def from_commands(job)
self.new(:script).tap do |step|
- step.script = job.commands.split("\n")
+ step.script = job.options[:before_script].to_a + job.options[:script].to_a
+ step.script = job.commands.split("\n") if step.script.empty?
step.timeout = job.timeout
step.when = WHEN_ON_SUCCESS
end
diff --git a/lib/gitlab/git/branch.rb b/lib/gitlab/git/branch.rb
index e2be9d784b9..c53882787f1 100644
--- a/lib/gitlab/git/branch.rb
+++ b/lib/gitlab/git/branch.rb
@@ -3,39 +3,8 @@
module Gitlab
module Git
class Branch < Ref
- def initialize(repository, name, target)
- if target.is_a?(Gitaly::FindLocalBranchResponse)
- target = target_from_gitaly_local_branches_response(target)
- end
-
- super(repository, name, target)
- end
-
- def target_from_gitaly_local_branches_response(response)
- # Git messages have no encoding enforcements. However, in the UI we only
- # handle UTF-8, so basically we cross our fingers that the message force
- # encoded to UTF-8 is readable.
- message = response.commit_subject.dup.force_encoding('UTF-8')
-
- # NOTE: For ease of parsing in Gitaly, we have only the subject of
- # the commit and not the full message. This is ok, since all the
- # code that uses `local_branches` only cares at most about the
- # commit message.
- # TODO: Once gitaly "takes over" Rugged consider separating the
- # subject from the message to make it clearer when there's one
- # available but not the other.
- hash = {
- id: response.commit_id,
- message: message,
- authored_date: Time.at(response.commit_author.date.seconds),
- author_name: response.commit_author.name,
- author_email: response.commit_author.email,
- committed_date: Time.at(response.commit_committer.date.seconds),
- committer_name: response.commit_committer.name,
- committer_email: response.commit_committer.email
- }
-
- Gitlab::Git::Commit.decorate(hash)
+ def initialize(repository, name, target, target_commit)
+ super(repository, name, target, target_commit)
end
end
end
diff --git a/lib/gitlab/git/ref.rb b/lib/gitlab/git/ref.rb
index c1a10688285..372ce005b94 100644
--- a/lib/gitlab/git/ref.rb
+++ b/lib/gitlab/git/ref.rb
@@ -33,10 +33,9 @@ module Gitlab
object
end
- def initialize(repository, name, target)
- encode! name
- @name = name.gsub(/\Arefs\/(tags|heads)\//, '')
- @dereferenced_target = Gitlab::Git::Commit.find(repository, target)
+ def initialize(repository, name, target, derefenced_target)
+ @name = Gitlab::Git.ref_name(name)
+ @dereferenced_target = derefenced_target
@target = if target.respond_to?(:oid)
target.oid
elsif target.respond_to?(:name)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index ac1b787cce4..c091bb9dcfe 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -80,16 +80,10 @@ module Gitlab
end
# Returns an Array of Branches
- def branches(filter: nil, sort_by: nil)
- branches = rugged.branches.each(filter).map do |rugged_ref|
- begin
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target)
- rescue Rugged::ReferenceError
- # Omit invalid branch
- end
- end.compact
-
- sort_branches(branches, sort_by)
+ #
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/389
+ def branches(sort_by: nil)
+ branches_filter(sort_by: sort_by)
end
def reload_rugged
@@ -107,7 +101,10 @@ module Gitlab
reload_rugged if force_reload
rugged_ref = rugged.branches[name]
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target) if rugged_ref
+ if rugged_ref
+ target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
+ Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
+ end
end
def local_branches(sort_by: nil)
@@ -115,7 +112,7 @@ module Gitlab
if is_enabled
gitaly_ref_client.local_branches(sort_by: sort_by)
else
- branches(filter: :local, sort_by: sort_by)
+ branches_filter(filter: :local, sort_by: sort_by)
end
end
end
@@ -162,6 +159,8 @@ module Gitlab
end
# Returns an Array of Tags
+ #
+ # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/390
def tags
rugged.references.each("refs/tags/*").map do |ref|
message = nil
@@ -174,7 +173,8 @@ module Gitlab
end
end
- Gitlab::Git::Tag.new(self, ref.name, ref.target, message)
+ target_commit = Gitlab::Git::Commit.find(self, ref.target)
+ Gitlab::Git::Tag.new(self, ref.name, ref.target, target_commit, message)
end.sort_by(&:name)
end
@@ -204,13 +204,6 @@ module Gitlab
branch_names + tag_names
end
- # Deprecated. Will be removed in 5.2
- def heads
- rugged.references.each("refs/heads/*").map do |head|
- Gitlab::Git::Ref.new(self, head.name, head.target)
- end.sort_by(&:name)
- end
-
def has_commits?
!empty?
end
@@ -297,28 +290,6 @@ module Gitlab
(size.to_f / 1024).round(2)
end
- # Returns an array of BlobSnippets for files at the specified +ref+ that
- # contain the +query+ string.
- def search_files(query, ref = nil)
- greps = []
- ref ||= root_ref
-
- populated_index(ref).each do |entry|
- # Discard submodules
- next if submodule?(entry)
-
- blob = Gitlab::Git::Blob.raw(self, entry[:oid])
-
- # Skip binary files
- next if blob.data.encoding == Encoding::ASCII_8BIT
-
- blob.load_all_data!(self)
- greps += build_greps(blob.data, query, ref, entry[:path])
- end
-
- greps
- end
-
# Use the Rugged Walker API to build an array of commits.
#
# Usage.
@@ -707,7 +678,8 @@ module Gitlab
# create_branch("other-feature", "master")
def create_branch(ref, start_point = "HEAD")
rugged_ref = rugged.branches.create(ref, start_point)
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target)
+ target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
+ Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
rescue Rugged::ReferenceError => e
raise InvalidRef.new("Branch #{ref} already exists") if e.to_s =~ /'refs\/heads\/#{ref}'/
raise InvalidRef.new("Invalid reference #{start_point}")
@@ -837,6 +809,20 @@ module Gitlab
private
+ # Gitaly note: JV: Trying to get rid of the 'filter' option so we can implement this with 'git'.
+ def branches_filter(filter: nil, sort_by: nil)
+ branches = rugged.branches.each(filter).map do |rugged_ref|
+ begin
+ target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
+ Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
+ rescue Rugged::ReferenceError
+ # Omit invalid branch
+ end
+ end.compact
+
+ sort_branches(branches, sort_by)
+ end
+
def raw_log(options)
default_options = {
limit: 10,
@@ -1091,73 +1077,6 @@ module Gitlab
index
end
- # Return an array of BlobSnippets for lines in +file_contents+ that match
- # +query+
- def build_greps(file_contents, query, ref, filename)
- # The file_contents string is potentially huge so we make sure to loop
- # through it one line at a time. This gives Ruby the chance to GC lines
- # we are not interested in.
- #
- # We need to do a little extra work because we are not looking for just
- # the lines that matches the query, but also for the context
- # (surrounding lines). We will use Enumerable#each_cons to efficiently
- # loop through the lines while keeping surrounding lines on hand.
- #
- # First, we turn "foo\nbar\nbaz" into
- # [
- # [nil, -3], [nil, -2], [nil, -1],
- # ['foo', 0], ['bar', 1], ['baz', 3],
- # [nil, 4], [nil, 5], [nil, 6]
- # ]
- lines_with_index = Enumerator.new do |yielder|
- # Yield fake 'before' lines for the first line of file_contents
- (-SEARCH_CONTEXT_LINES..-1).each do |i|
- yielder.yield [nil, i]
- end
-
- # Yield the actual file contents
- count = 0
- file_contents.each_line do |line|
- line.chomp!
- yielder.yield [line, count]
- count += 1
- end
-
- # Yield fake 'after' lines for the last line of file_contents
- (count + 1..count + SEARCH_CONTEXT_LINES).each do |i|
- yielder.yield [nil, i]
- end
- end
-
- greps = []
-
- # Loop through consecutive blocks of lines with indexes
- lines_with_index.each_cons(2 * SEARCH_CONTEXT_LINES + 1) do |line_block|
- # Get the 'middle' line and index from the block
- line, _ = line_block[SEARCH_CONTEXT_LINES]
-
- next unless line && line.match(/#{Regexp.escape(query)}/i)
-
- # Yay, 'line' contains a match!
- # Get an array with just the context lines (no indexes)
- match_with_context = line_block.map(&:first)
- # Remove 'nil' lines in case we are close to the first or last line
- match_with_context.compact!
-
- # Get the line number (1-indexed) of the first context line
- first_context_line_number = line_block[0][1] + 1
-
- greps << Gitlab::Git::BlobSnippet.new(
- ref,
- match_with_context,
- first_context_line_number,
- filename
- )
- end
-
- greps
- end
-
# Return the Rugged patches for the diff between +from+ and +to+.
def diff_patches(from, to, options = {}, *paths)
options ||= {}
diff --git a/lib/gitlab/git/tag.rb b/lib/gitlab/git/tag.rb
index 9a39de6ad07..bc4e160dce9 100644
--- a/lib/gitlab/git/tag.rb
+++ b/lib/gitlab/git/tag.rb
@@ -5,8 +5,8 @@ module Gitlab
class Tag < Ref
attr_reader :object_sha
- def initialize(repository, name, target, message = nil)
- super(repository, name, target)
+ def initialize(repository, name, target, target_commit, message = nil)
+ super(repository, name, target, target_commit)
@message = message
end
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index 01df6b862cd..f541887843d 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -72,11 +72,39 @@ module Gitlab
Gitlab::Git::Branch.new(
@repository,
encode!(gitaly_branch.name.dup),
- gitaly_branch.commit_id
+ gitaly_branch.commit_id,
+ commit_from_local_branches_response(gitaly_branch)
)
end
end
end
+
+ def commit_from_local_branches_response(response)
+ # Git messages have no encoding enforcements. However, in the UI we only
+ # handle UTF-8, so basically we cross our fingers that the message force
+ # encoded to UTF-8 is readable.
+ message = response.commit_subject.dup.force_encoding('UTF-8')
+
+ # NOTE: For ease of parsing in Gitaly, we have only the subject of
+ # the commit and not the full message. This is ok, since all the
+ # code that uses `local_branches` only cares at most about the
+ # commit message.
+ # TODO: Once gitaly "takes over" Rugged consider separating the
+ # subject from the message to make it clearer when there's one
+ # available but not the other.
+ hash = {
+ id: response.commit_id,
+ message: message,
+ authored_date: Time.at(response.commit_author.date.seconds),
+ author_name: response.commit_author.name,
+ author_email: response.commit_author.email,
+ committed_date: Time.at(response.commit_committer.date.seconds),
+ committer_name: response.commit_committer.name,
+ committer_email: response.commit_committer.email
+ }
+
+ Gitlab::Git::Commit.decorate(hash)
+ end
end
end
end
diff --git a/lib/gitlab/health_checks/fs_shards_check.rb b/lib/gitlab/health_checks/fs_shards_check.rb
index 70da4080cae..bebde857b16 100644
--- a/lib/gitlab/health_checks/fs_shards_check.rb
+++ b/lib/gitlab/health_checks/fs_shards_check.rb
@@ -35,9 +35,9 @@ module Gitlab
repository_storages.flat_map do |storage_name|
tmp_file_path = tmp_file_path(storage_name)
[
- operation_metrics(:filesystem_accessible, :filesystem_access_latency, -> { storage_stat_test(storage_name) }, shard: storage_name),
- operation_metrics(:filesystem_writable, :filesystem_write_latency, -> { storage_write_test(tmp_file_path) }, shard: storage_name),
- operation_metrics(:filesystem_readable, :filesystem_read_latency, -> { storage_read_test(tmp_file_path) }, shard: storage_name)
+ operation_metrics(:filesystem_accessible, :filesystem_access_latency_seconds, -> { storage_stat_test(storage_name) }, shard: storage_name),
+ operation_metrics(:filesystem_writable, :filesystem_write_latency_seconds, -> { storage_write_test(tmp_file_path) }, shard: storage_name),
+ operation_metrics(:filesystem_readable, :filesystem_read_latency_seconds, -> { storage_read_test(tmp_file_path) }, shard: storage_name)
].flatten
end
end
diff --git a/lib/gitlab/health_checks/simple_abstract_check.rb b/lib/gitlab/health_checks/simple_abstract_check.rb
index fbe1645c1b1..3dcb28a193c 100644
--- a/lib/gitlab/health_checks/simple_abstract_check.rb
+++ b/lib/gitlab/health_checks/simple_abstract_check.rb
@@ -20,7 +20,7 @@ module Gitlab
[
metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0),
metric("#{metric_prefix}_success", is_successful?(result) ? 1 : 0),
- metric("#{metric_prefix}_latency", elapsed)
+ metric("#{metric_prefix}_latency_seconds", elapsed)
]
end
end
diff --git a/lib/gitlab/metrics/connection_rack_middleware.rb b/lib/gitlab/metrics/connection_rack_middleware.rb
deleted file mode 100644
index b3da360be8f..00000000000
--- a/lib/gitlab/metrics/connection_rack_middleware.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-module Gitlab
- module Metrics
- class ConnectionRackMiddleware
- def initialize(app)
- @app = app
- end
-
- def self.rack_request_count
- @rack_request_count ||= Gitlab::Metrics.counter(:rack_request, 'Rack request count')
- end
-
- def self.rack_response_count
- @rack_response_count ||= Gitlab::Metrics.counter(:rack_response, 'Rack response count')
- end
-
- def self.rack_uncaught_errors_count
- @rack_uncaught_errors_count ||= Gitlab::Metrics.counter(:rack_uncaught_errors, 'Rack connections handling uncaught errors count')
- end
-
- def self.rack_execution_time
- @rack_execution_time ||= Gitlab::Metrics.histogram(:rack_execution_time, 'Rack connection handling execution time',
- {}, [0.05, 0.1, 0.25, 0.5, 0.7, 1, 1.5, 2, 2.5, 3, 5, 7, 10])
- end
-
- def call(env)
- method = env['REQUEST_METHOD'].downcase
- started = Time.now.to_f
- begin
- ConnectionRackMiddleware.rack_request_count.increment(method: method)
-
- status, headers, body = @app.call(env)
-
- ConnectionRackMiddleware.rack_response_count.increment(method: method, status: status)
- [status, headers, body]
- rescue
- ConnectionRackMiddleware.rack_uncaught_errors_count.increment
- raise
- ensure
- elapsed = Time.now.to_f - started
- ConnectionRackMiddleware.rack_execution_time.observe({}, elapsed)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/metrics/requests_rack_middleware.rb b/lib/gitlab/metrics/requests_rack_middleware.rb
new file mode 100644
index 00000000000..0dc19f31d03
--- /dev/null
+++ b/lib/gitlab/metrics/requests_rack_middleware.rb
@@ -0,0 +1,40 @@
+module Gitlab
+ module Metrics
+ class RequestsRackMiddleware
+ def initialize(app)
+ @app = app
+ end
+
+ def self.http_request_total
+ @http_request_total ||= Gitlab::Metrics.counter(:http_requests_total, 'Request count')
+ end
+
+ def self.rack_uncaught_errors_count
+ @rack_uncaught_errors_count ||= Gitlab::Metrics.counter(:rack_uncaught_errors_total, 'Request handling uncaught errors count')
+ end
+
+ def self.http_request_duration_seconds
+ @http_request_duration_seconds ||= Gitlab::Metrics.histogram(:http_request_duration_seconds, 'Request handling execution time',
+ {}, [0.05, 0.1, 0.25, 0.5, 0.7, 1, 2.5, 5, 10, 25])
+ end
+
+ def call(env)
+ method = env['REQUEST_METHOD'].downcase
+ started = Time.now.to_f
+ begin
+ RequestsRackMiddleware.http_request_total.increment(method: method)
+
+ status, headers, body = @app.call(env)
+
+ elapsed = Time.now.to_f - started
+ RequestsRackMiddleware.http_request_duration_seconds.observe({ method: method, status: status }, elapsed)
+
+ [status, headers, body]
+ rescue
+ RequestsRackMiddleware.rack_uncaught_errors_count.increment
+ raise
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/performance_bar.rb b/lib/gitlab/performance_bar.rb
index 2da2ce45ebc..56112ec2301 100644
--- a/lib/gitlab/performance_bar.rb
+++ b/lib/gitlab/performance_bar.rb
@@ -2,7 +2,8 @@ module Gitlab
module PerformanceBar
include Gitlab::CurrentSettings
- ALLOWED_USER_IDS_KEY = 'performance_bar_allowed_user_ids'.freeze
+ ALLOWED_USER_IDS_KEY = 'performance_bar_allowed_user_ids:v2'.freeze
+ EXPIRY_TIME = 5.minutes
def self.enabled?(user = nil)
return false unless user && allowed_group_id
@@ -15,7 +16,7 @@ module Gitlab
end
def self.allowed_user_ids
- Rails.cache.fetch(ALLOWED_USER_IDS_KEY) do
+ Rails.cache.fetch(ALLOWED_USER_IDS_KEY, expires_in: EXPIRY_TIME) do
group = Group.find_by_id(allowed_group_id)
if group
diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb
index 574ae8731a5..f97e895dbd0 100644
--- a/lib/gitlab/performance_bar/peek_query_tracker.rb
+++ b/lib/gitlab/performance_bar/peek_query_tracker.rb
@@ -1,4 +1,5 @@
# Inspired by https://github.com/peek/peek-pg/blob/master/lib/peek/views/pg.rb
+# PEEK_DB_CLIENT is a constant set in config/initializers/peek.rb
module Gitlab
module PerformanceBar
module PeekQueryTracker
@@ -23,7 +24,13 @@ module Gitlab
subscribe('sql.active_record') do |_, start, finish, _, data|
if RequestStore.active? && RequestStore.store[:peek_enabled]
- track_query(data[:sql].strip, data[:binds], start, finish)
+ # data[:cached] is only available starting from Rails 5.1.0
+ # https://github.com/rails/rails/blob/v5.1.0/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L113
+ # Before that, data[:name] was set to 'CACHE'
+ # https://github.com/rails/rails/blob/v4.2.9/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L80
+ unless data.fetch(:cached, data[:name] == 'CACHE')
+ track_query(data[:sql].strip, data[:binds], start, finish)
+ end
end
end
end
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index 7406629ef9d..0cc16404e1b 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -15,11 +15,11 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-msgid "%d additional commit has been omitted to prevent performance issues."
+msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural ""
-"%d additional commits have been omitted to prevent performance issues."
-msgstr[0] "%d подаване беше пропуснато, за да не се натоварва системата."
-msgstr[1] "%d подавания бяха пропуснати, за да не се натоварва системата."
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] "%s подаване беше пропуснато, за да не се натоварва системата."
+msgstr[1] "%s подавания бяха пропуснати, за да не се натоварва системата."
msgid "%d commit"
msgid_plural "%d commits"
diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po
index 7065b7a635f..46bf4e33997 100644
--- a/locale/en/gitlab.po
+++ b/locale/en/gitlab.po
@@ -17,8 +17,8 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"\n"
-msgid "%d additional commit has been omitted to prevent performance issues."
-msgid_plural "%d additional commits have been omitted to prevent performance issues."
+msgid "%s additional commit has been omitted to prevent performance issues."
+msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] ""
msgstr[1] ""
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index 015d96d6e53..5218f6ae7b9 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -15,11 +15,11 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-msgid "%d additional commit has been omitted to prevent performance issues."
+msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural ""
-"%d additional commits have been omitted to prevent performance issues."
-msgstr[0] "%d enmetado estis transsaltita, por ne troŝarĝi la sistemon."
-msgstr[1] "%d enmetadoj estis transsaltitaj, por ne troŝarĝi la sistemon."
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] "%s enmetado estis transsaltita, por ne troŝarĝi la sistemon."
+msgstr[1] "%s enmetadoj estis transsaltitaj, por ne troŝarĝi la sistemon."
msgid "%d commit"
msgid_plural "%d commits"
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index c5600721d49..d4fac6ab34e 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -15,14 +15,14 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-msgid "%d additional commit has been omitted to prevent performance issues."
+msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural ""
-"%d additional commits have been omitted to prevent performance issues."
+"%s additional commits have been omitted to prevent performance issues."
msgstr[0] ""
-"%d commit aggiuntivo è stato omesso per evitare degradi di prestazioni negli "
+"%s commit aggiuntivo è stato omesso per evitare degradi di prestazioni negli "
"issues."
msgstr[1] ""
-"%d commit aggiuntivi sono stati omessi per evitare degradi di prestazioni "
+"%s commit aggiuntivi sono stati omessi per evitare degradi di prestazioni "
"negli issues."
msgid "%d commit"
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 8a76121e0c1..b7a88aadeb9 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -15,10 +15,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n"
-msgid "%d additional commit has been omitted to prevent performance issues."
+msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural ""
-"%d additional commits have been omitted to prevent performance issues."
-msgstr[0] "为提高页面加载速度及性能,已省略了 %d 次提交。"
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] "为提高页面加载速度及性能,已省略了 %s 次提交。"
msgid "%d commit"
msgid_plural "%d commits"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index b679ed3f26b..f6add31db99 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -14,10 +14,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n"
-msgid "%d additional commit has been omitted to prevent performance issues."
+msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural ""
-"%d additional commits have been omitted to prevent performance issues."
-msgstr[0] "為提高頁面加載速度及性能,已省略了 %d 次提交。"
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] "為提高頁面加載速度及性能,已省略了 %s 次提交。"
msgid "%d commit"
msgid_plural "%d commits"
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index d5a3c5ea0b9..e61cf0e5152 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -18,10 +18,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n"
-msgid "%d additional commit has been omitted to prevent performance issues."
+msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural ""
-"%d additional commits have been omitted to prevent performance issues."
-msgstr[0] "因效能考量,不顯示 %d 個更動 (commit)。"
+"%s additional commits have been omitted to prevent performance issues."
+msgstr[0] "因效能考量,不顯示 %s 個更動 (commit)。"
msgid "%d commit"
msgid_plural "%d commits"
diff --git a/rubocop/cop/migration/hash_index.rb b/rubocop/cop/migration/hash_index.rb
new file mode 100644
index 00000000000..2cc59691d84
--- /dev/null
+++ b/rubocop/cop/migration/hash_index.rb
@@ -0,0 +1,51 @@
+require 'set'
+require_relative '../../migration_helpers'
+
+module RuboCop
+ module Cop
+ module Migration
+ # Cop that prevents the use of hash indexes in database migrations
+ class HashIndex < RuboCop::Cop::Cop
+ include MigrationHelpers
+
+ MSG = 'hash indexes should be avoided at all costs since they are not ' \
+ 'recorded in the PostgreSQL WAL, you should use a btree index instead'.freeze
+
+ NAMES = Set.new([:add_index, :index, :add_concurrent_index]).freeze
+
+ def on_send(node)
+ return unless in_migration?(node)
+
+ name = node.children[1]
+
+ return unless NAMES.include?(name)
+
+ opts = node.children.last
+
+ return unless opts && opts.type == :hash
+
+ opts.each_node(:pair) do |pair|
+ next unless hash_key_type(pair) == :sym &&
+ hash_key_name(pair) == :using
+
+ if hash_key_value(pair).to_s == 'hash'
+ add_offense(pair, :expression)
+ end
+ end
+ end
+
+ def hash_key_type(pair)
+ pair.children[0].type
+ end
+
+ def hash_key_name(pair)
+ pair.children[0].children[0]
+ end
+
+ def hash_key_value(pair)
+ pair.children[1].children[0]
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index f76144275c9..3fbd5b0163c 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -13,6 +13,7 @@ require_relative 'cop/migration/add_concurrent_index'
require_relative 'cop/migration/add_index'
require_relative 'cop/migration/add_timestamps'
require_relative 'cop/migration/datetime'
+require_relative 'cop/migration/hash_index'
require_relative 'cop/migration/remove_concurrent_index'
require_relative 'cop/migration/remove_index'
require_relative 'cop/migration/reversible_add_column_with_default'
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index b40f647644d..58486f33229 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -97,6 +97,21 @@ describe AutocompleteController do
it { expect(body.size).to eq User.count }
end
+ context 'user order' do
+ it 'shows exact matches first' do
+ reported_user = create(:user, username: 'reported_user', name: 'Doug')
+ user = create(:user, username: 'user', name: 'User')
+ user1 = create(:user, username: 'user1', name: 'Ian')
+
+ sign_in(user)
+ get(:users, search: 'user')
+
+ response_usernames = JSON.parse(response.body).map { |user| user['username'] }
+
+ expect(response_usernames.take(3)).to match_array([user.username, reported_user.username, user1.username])
+ end
+ end
+
context 'limited users per page' do
let(:per_page) { 2 }
diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb
index 86847c07c09..8964d89b438 100644
--- a/spec/controllers/metrics_controller_spec.rb
+++ b/spec/controllers/metrics_controller_spec.rb
@@ -24,7 +24,7 @@ describe MetricsController do
expect(response.body).to match(/^db_ping_timeout 0$/)
expect(response.body).to match(/^db_ping_success 1$/)
- expect(response.body).to match(/^db_ping_latency [0-9\.]+$/)
+ expect(response.body).to match(/^db_ping_latency_seconds [0-9\.]+$/)
end
it 'returns Redis ping metrics' do
@@ -32,7 +32,7 @@ describe MetricsController do
expect(response.body).to match(/^redis_ping_timeout 0$/)
expect(response.body).to match(/^redis_ping_success 1$/)
- expect(response.body).to match(/^redis_ping_latency [0-9\.]+$/)
+ expect(response.body).to match(/^redis_ping_latency_seconds [0-9\.]+$/)
end
it 'returns Caching ping metrics' do
@@ -40,7 +40,7 @@ describe MetricsController do
expect(response.body).to match(/^redis_cache_ping_timeout 0$/)
expect(response.body).to match(/^redis_cache_ping_success 1$/)
- expect(response.body).to match(/^redis_cache_ping_latency [0-9\.]+$/)
+ expect(response.body).to match(/^redis_cache_ping_latency_seconds [0-9\.]+$/)
end
it 'returns Queues ping metrics' do
@@ -48,7 +48,7 @@ describe MetricsController do
expect(response.body).to match(/^redis_queues_ping_timeout 0$/)
expect(response.body).to match(/^redis_queues_ping_success 1$/)
- expect(response.body).to match(/^redis_queues_ping_latency [0-9\.]+$/)
+ expect(response.body).to match(/^redis_queues_ping_latency_seconds [0-9\.]+$/)
end
it 'returns SharedState ping metrics' do
@@ -56,17 +56,17 @@ describe MetricsController do
expect(response.body).to match(/^redis_shared_state_ping_timeout 0$/)
expect(response.body).to match(/^redis_shared_state_ping_success 1$/)
- expect(response.body).to match(/^redis_shared_state_ping_latency [0-9\.]+$/)
+ expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/)
end
it 'returns file system check metrics' do
get :index
- expect(response.body).to match(/^filesystem_access_latency{shard="default"} [0-9\.]+$/)
+ expect(response.body).to match(/^filesystem_access_latency_seconds{shard="default"} [0-9\.]+$/)
expect(response.body).to match(/^filesystem_accessible{shard="default"} 1$/)
- expect(response.body).to match(/^filesystem_write_latency{shard="default"} [0-9\.]+$/)
+ expect(response.body).to match(/^filesystem_write_latency_seconds{shard="default"} [0-9\.]+$/)
expect(response.body).to match(/^filesystem_writable{shard="default"} 1$/)
- expect(response.body).to match(/^filesystem_read_latency{shard="default"} [0-9\.]+$/)
+ expect(response.body).to match(/^filesystem_read_latency_seconds{shard="default"} [0-9\.]+$/)
expect(response.body).to match(/^filesystem_readable{shard="default"} 1$/)
end
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index eb61a0c080c..df53863482d 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -343,7 +343,8 @@ describe Projects::CommitController do
get_pipelines(id: commit.id, format: :json)
expect(response).to be_ok
- expect(JSON.parse(response.body)).not_to be_empty
+ expect(JSON.parse(response.body)['pipelines']).not_to be_empty
+ expect(JSON.parse(response.body)['count']['all']).to eq 1
end
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 6f9ce60cf75..c193babead0 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -481,7 +481,8 @@ describe Projects::MergeRequestsController do
end
it 'responds with serialized pipelines' do
- expect(json_response).not_to be_empty
+ expect(json_response['pipelines']).not_to be_empty
+ expect(json_response['count']['all']).to eq 1
end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index f96fe7ad5cb..192cca45d99 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -211,24 +211,43 @@ describe ProjectsController do
let(:admin) { create(:admin) }
let(:project) { create(:project, :repository) }
- let(:new_path) { 'renamed_path' }
- let(:project_params) { { path: new_path } }
before do
sign_in(admin)
end
- it "sets the repository to the right path after a rename" do
- controller.instance_variable_set(:@project, project)
+ context 'when only renaming a project path' do
+ it "sets the repository to the right path after a rename" do
+ expect { update_project path: 'renamed_path' }
+ .to change { project.reload.path }
- put :update,
- namespace_id: project.namespace,
- id: project.id,
- project: project_params
+ expect(project.path).to include 'renamed_path'
+ expect(assigns(:repository).path).to include project.path
+ expect(response).to have_http_status(302)
+ end
+ end
- expect(project.repository.path).to include(new_path)
- expect(assigns(:repository).path).to eq(project.repository.path)
- expect(response).to have_http_status(302)
+ context 'when project has container repositories with tags' do
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: /image/, tags: %w[rc1])
+ create(:container_repository, project: project, name: :image)
+ end
+
+ it 'does not allow to rename the project' do
+ expect { update_project path: 'renamed_path' }
+ .not_to change { project.reload.path }
+
+ expect(controller).to set_flash[:alert].to(/container registry tags/)
+ expect(response).to have_http_status(200)
+ end
+ end
+
+ def update_project(**parameters)
+ put :update,
+ namespace_id: project.namespace.path,
+ id: project.path,
+ project: parameters
end
end
diff --git a/spec/factories/ci/triggers.rb b/spec/factories/ci/triggers.rb
index c3a29d8bf04..40c4663c6d8 100644
--- a/spec/factories/ci/triggers.rb
+++ b/spec/factories/ci/triggers.rb
@@ -2,13 +2,6 @@ FactoryGirl.define do
factory :ci_trigger_without_token, class: Ci::Trigger do
factory :ci_trigger do
sequence(:token) { |n| "token#{n}" }
-
- factory :ci_trigger_for_trigger_schedule do
- token { SecureRandom.hex(15) }
- owner factory: :user
- project factory: :project
- ref 'master'
- end
end
end
end
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index cd7040891e9..d01722805c4 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -8,8 +8,8 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do
find(".table.active-tokens")
end
- def inactive_impersonation_tokens
- find(".table.inactive-tokens")
+ def no_personal_access_tokens_message
+ find(".settings-message")
end
before do
@@ -60,15 +60,17 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do
click_on "Revoke"
- expect(inactive_impersonation_tokens).to have_text(impersonation_token.name)
+ expect(page).to have_selector(".settings-message")
+ expect(no_personal_access_tokens_message).to have_text("This user has no active Impersonation Tokens.")
end
- it "moves expired tokens to the 'inactive' section" do
+ it "removes expired tokens from 'active' section" do
impersonation_token.update(expires_at: 5.days.ago)
visit admin_user_impersonation_tokens_path(user_id: user.username)
- expect(inactive_impersonation_tokens).to have_text(impersonation_token.name)
+ expect(page).to have_selector(".settings-message")
+ expect(no_personal_access_tokens_message).to have_text("This user has no active Impersonation Tokens.")
end
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 3d7e26c7e19..b939fb5e89e 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -3,7 +3,8 @@ require 'rails_helper'
describe 'Issue Boards', feature: true, js: true do
include DragTo
- let(:project) { create(:empty_project, :public) }
+ let(:group) { create(:group, :nested) }
+ let(:project) { create(:empty_project, :public, namespace: group) }
let(:board) { create(:board, project: project) }
let(:user) { create(:user) }
let!(:user2) { create(:user) }
diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb
index 44b7ee101c9..3c08b6bc091 100644
--- a/spec/features/profiles/personal_access_tokens_spec.rb
+++ b/spec/features/profiles/personal_access_tokens_spec.rb
@@ -7,8 +7,8 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
find(".table.active-tokens")
end
- def inactive_personal_access_tokens
- find(".table.inactive-tokens")
+ def no_personal_access_tokens_message
+ find(".settings-message")
end
def created_personal_access_token
@@ -80,14 +80,16 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
visit profile_personal_access_tokens_path
click_on "Revoke"
- expect(inactive_personal_access_tokens).to have_text(personal_access_token.name)
+ expect(page).to have_selector(".settings-message")
+ expect(no_personal_access_tokens_message).to have_text("This user has no active Personal Access Tokens.")
end
- it "moves expired tokens to the 'inactive' section" do
+ it "removes expired tokens from 'active' section" do
personal_access_token.update(expires_at: 5.days.ago)
visit profile_personal_access_tokens_path
- expect(inactive_personal_access_tokens).to have_text(personal_access_token.name)
+ expect(page).to have_selector(".settings-message")
+ expect(no_personal_access_tokens_message).to have_text("This user has no active Personal Access Tokens.")
end
context "when revocation fails" do
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 694f94efcff..a34cadec0ab 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -85,6 +85,41 @@ describe('Pipelines table in Commits and Merge requests', () => {
}, 0);
});
});
+
+ describe('pipeline badge counts', () => {
+ const pipelinesResponse = (request, next) => {
+ next(request.respondWith(JSON.stringify([pipeline]), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(pipelinesResponse);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pipelinesResponse);
+ this.component.$destroy();
+ });
+
+ it('should receive update-pipelines-count event', (done) => {
+ const element = document.createElement('div');
+ document.body.appendChild(element);
+
+ element.addEventListener('update-pipelines-count', (event) => {
+ expect(event.detail.pipelines).toEqual([pipeline]);
+ done();
+ });
+
+ this.component = new PipelinesTable({
+ propsData: {
+ endpoint: 'endpoint',
+ helpPagePath: 'foo',
+ },
+ }).$mount();
+ element.appendChild(this.component.$el);
+ });
+ });
});
describe('unsuccessfull request', () => {
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index ef58ef1b0cd..ea79389e67e 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -163,7 +163,10 @@ module Ci
commands: "pwd\nrspec",
coverage_regex: nil,
tag_list: [],
- options: {},
+ options: {
+ before_script: ["pwd"],
+ script: ["rspec"]
+ },
allow_failure: false,
when: "on_success",
environment: nil,
@@ -616,10 +619,12 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
- image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] },
- services: [{ name: "mysql" },
- { name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"],
- command: ["/usr/local/bin/init", "run"] }]
+ before_script: ["pwd"],
+ script: ["rspec"],
+ image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] },
+ services: [{ name: "mysql" },
+ { name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"],
+ command: ["/usr/local/bin/init", "run"] }]
},
allow_failure: false,
when: "on_success",
@@ -649,10 +654,12 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
- image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] },
- services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"],
- command: ["/usr/local/bin/init", "run"] },
- { name: "docker:dind" }]
+ before_script: ["pwd"],
+ script: ["rspec"],
+ image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] },
+ services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"],
+ command: ["/usr/local/bin/init", "run"] },
+ { name: "docker:dind" }]
},
allow_failure: false,
when: "on_success",
@@ -680,6 +687,8 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
+ before_script: ["pwd"],
+ script: ["rspec"],
image: { name: "ruby:2.1" },
services: [{ name: "mysql" }, { name: "docker:dind" }]
},
@@ -707,8 +716,10 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
- image: { name: "ruby:2.5" },
- services: [{ name: "postgresql" }, { name: "docker:dind" }]
+ before_script: ["pwd"],
+ script: ["rspec"],
+ image: { name: "ruby:2.5" },
+ services: [{ name: "postgresql" }, { name: "docker:dind" }]
},
allow_failure: false,
when: "on_success",
@@ -951,6 +962,8 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
+ before_script: ["pwd"],
+ script: ["rspec"],
image: { name: "ruby:2.1" },
services: [{ name: "mysql" }],
artifacts: {
@@ -1162,7 +1175,9 @@ module Ci
commands: "test",
coverage_regex: nil,
tag_list: [],
- options: {},
+ options: {
+ script: ["test"]
+ },
when: "on_success",
allow_failure: false,
environment: nil,
@@ -1208,7 +1223,9 @@ module Ci
commands: "execute-script-for-job",
coverage_regex: nil,
tag_list: [],
- options: {},
+ options: {
+ script: ["execute-script-for-job"]
+ },
when: "on_success",
allow_failure: false,
environment: nil,
@@ -1221,7 +1238,9 @@ module Ci
commands: "execute-script-for-job",
coverage_regex: nil,
tag_list: [],
- options: {},
+ options: {
+ script: ["execute-script-for-job"]
+ },
when: "on_success",
allow_failure: false,
environment: nil,
diff --git a/spec/lib/gitlab/background_migration_spec.rb b/spec/lib/gitlab/background_migration_spec.rb
index 64f82fe27b2..cfa59280139 100644
--- a/spec/lib/gitlab/background_migration_spec.rb
+++ b/spec/lib/gitlab/background_migration_spec.rb
@@ -1,46 +1,120 @@
require 'spec_helper'
describe Gitlab::BackgroundMigration do
+ describe '.queue' do
+ it 'returns background migration worker queue' do
+ expect(described_class.queue)
+ .to eq BackgroundMigrationWorker.sidekiq_options['queue']
+ end
+ end
+
describe '.steal' do
- it 'steals jobs from a queue' do
- queue = [double(:job, args: ['Foo', [10, 20]])]
+ context 'when there are enqueued jobs present' do
+ let(:queue) do
+ [double(args: ['Foo', [10, 20]], queue: described_class.queue)]
+ end
+
+ before do
+ allow(Sidekiq::Queue).to receive(:new)
+ .with(described_class.queue)
+ .and_return(queue)
+ end
+
+ context 'when queue contains unprocessed jobs' do
+ it 'steals jobs from a queue' do
+ expect(queue[0]).to receive(:delete).and_return(true)
+
+ expect(described_class).to receive(:perform)
+ .with('Foo', [10, 20], anything)
+
+ described_class.steal('Foo')
+ end
+
+ it 'does not steal job that has already been taken' do
+ expect(queue[0]).to receive(:delete).and_return(false)
+
+ expect(described_class).not_to receive(:perform)
+
+ described_class.steal('Foo')
+ end
+
+ it 'does not steal jobs for a different migration' do
+ expect(described_class).not_to receive(:perform)
- allow(Sidekiq::Queue).to receive(:new)
- .with(BackgroundMigrationWorker.sidekiq_options['queue'])
- .and_return(queue)
+ expect(queue[0]).not_to receive(:delete)
- expect(queue[0]).to receive(:delete)
+ described_class.steal('Bar')
+ end
+ end
- expect(described_class).to receive(:perform).with('Foo', [10, 20])
+ context 'when one of the jobs raises an error' do
+ let(:migration) { spy(:migration) }
- described_class.steal('Foo')
+ let(:queue) do
+ [double(args: ['Foo', [10, 20]], queue: described_class.queue),
+ double(args: ['Foo', [20, 30]], queue: described_class.queue)]
+ end
+
+ before do
+ stub_const("#{described_class}::Foo", migration)
+
+ allow(queue[0]).to receive(:delete).and_return(true)
+ allow(queue[1]).to receive(:delete).and_return(true)
+ end
+
+ it 'enqueues the migration again and re-raises the error' do
+ allow(migration).to receive(:perform).with(10, 20)
+ .and_raise(Exception, 'Migration error').once
+
+ expect(BackgroundMigrationWorker).to receive(:perform_async)
+ .with('Foo', [10, 20]).once
+
+ expect { described_class.steal('Foo') }.to raise_error(Exception)
+ end
+ end
end
- it 'does not steal jobs for a different migration' do
- queue = [double(:job, args: ['Foo', [10, 20]])]
+ context 'when there are scheduled jobs present', :sidekiq, :redis do
+ it 'steals all jobs from the scheduled sets' do
+ Sidekiq::Testing.disable! do
+ BackgroundMigrationWorker.perform_in(10.minutes, 'Object')
- allow(Sidekiq::Queue).to receive(:new)
- .with(BackgroundMigrationWorker.sidekiq_options['queue'])
- .and_return(queue)
+ expect(Sidekiq::ScheduledSet.new).to be_one
+ expect(described_class).to receive(:perform).with('Object', any_args)
- expect(described_class).not_to receive(:perform)
+ described_class.steal('Object')
- expect(queue[0]).not_to receive(:delete)
+ expect(Sidekiq::ScheduledSet.new).to be_none
+ end
+ end
+ end
- described_class.steal('Bar')
+ context 'when there are enqueued and scheduled jobs present', :sidekiq, :redis do
+ it 'steals from the scheduled sets queue first' do
+ Sidekiq::Testing.disable! do
+ expect(described_class).to receive(:perform)
+ .with('Object', [1], anything).ordered
+ expect(described_class).to receive(:perform)
+ .with('Object', [2], anything).ordered
+
+ BackgroundMigrationWorker.perform_async('Object', [2])
+ BackgroundMigrationWorker.perform_in(10.minutes, 'Object', [1])
+
+ described_class.steal('Object')
+ end
+ end
end
end
describe '.perform' do
- it 'performs a background migration' do
- instance = double(:instance)
- klass = double(:klass, new: instance)
+ let(:migration) { spy(:migration) }
- expect(described_class).to receive(:const_get)
- .with('Foo')
- .and_return(klass)
+ before do
+ stub_const("#{described_class.name}::Foo", migration)
+ end
- expect(instance).to receive(:perform).with(10, 20)
+ it 'performs a background migration' do
+ expect(migration).to receive(:perform).with(10, 20).once
described_class.perform('Foo', [10, 20])
end
diff --git a/spec/lib/gitlab/ci/build/step_spec.rb b/spec/lib/gitlab/ci/build/step_spec.rb
index 49457b129e3..5a21282712a 100644
--- a/spec/lib/gitlab/ci/build/step_spec.rb
+++ b/spec/lib/gitlab/ci/build/step_spec.rb
@@ -1,21 +1,50 @@
require 'spec_helper'
describe Gitlab::Ci::Build::Step do
- let(:job) { create(:ci_build, :no_options, commands: "ls -la\ndate") }
-
describe '#from_commands' do
- subject { described_class.from_commands(job) }
-
- it 'fabricates an object' do
- expect(subject.name).to eq(:script)
- expect(subject.script).to eq(['ls -la', 'date'])
- expect(subject.timeout).to eq(job.timeout)
- expect(subject.when).to eq('on_success')
- expect(subject.allow_failure).to be_falsey
+ shared_examples 'has correct script' do
+ subject { described_class.from_commands(job) }
+
+ it 'fabricates an object' do
+ expect(subject.name).to eq(:script)
+ expect(subject.script).to eq(script)
+ expect(subject.timeout).to eq(job.timeout)
+ expect(subject.when).to eq('on_success')
+ expect(subject.allow_failure).to be_falsey
+ end
+ end
+
+ context 'when commands are specified' do
+ it_behaves_like 'has correct script' do
+ let(:job) { create(:ci_build, :no_options, commands: "ls -la\ndate") }
+ let(:script) { ['ls -la', 'date'] }
+ end
+ end
+
+ context 'when script option is specified' do
+ it_behaves_like 'has correct script' do
+ let(:job) { create(:ci_build, :no_options, options: { script: ["ls -la\necho aaa", "date"] }) }
+ let(:script) { ["ls -la\necho aaa", 'date'] }
+ end
+ end
+
+ context 'when before and script option is specified' do
+ it_behaves_like 'has correct script' do
+ let(:job) do
+ create(:ci_build, options: {
+ before_script: ["ls -la\necho aaa"],
+ script: ["date"]
+ })
+ end
+
+ let(:script) { ["ls -la\necho aaa", 'date'] }
+ end
end
end
describe '#from_after_script' do
+ let(:job) { create(:ci_build) }
+
subject { described_class.from_after_script(job) }
context 'when after_script is empty' do
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index d1d7ed1d02a..cdf1b8beee3 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -7,51 +7,6 @@ describe Gitlab::Git::Branch, seed_helper: true do
it { is_expected.to be_kind_of Array }
- describe 'initialize' do
- let(:commit_id) { 'f00' }
- let(:commit_subject) { "My commit".force_encoding('ASCII-8BIT') }
- let(:committer) do
- Gitaly::FindLocalBranchCommitAuthor.new(
- name: generate(:name),
- email: generate(:email),
- date: Google::Protobuf::Timestamp.new(seconds: 123)
- )
- end
- let(:author) do
- Gitaly::FindLocalBranchCommitAuthor.new(
- name: generate(:name),
- email: generate(:email),
- date: Google::Protobuf::Timestamp.new(seconds: 456)
- )
- end
- let(:gitaly_branch) do
- Gitaly::FindLocalBranchResponse.new(
- name: 'foo', commit_id: commit_id, commit_subject: commit_subject,
- commit_author: author, commit_committer: committer
- )
- end
- let(:attributes) do
- {
- id: commit_id,
- message: commit_subject,
- authored_date: Time.at(author.date.seconds),
- author_name: author.name,
- author_email: author.email,
- committed_date: Time.at(committer.date.seconds),
- committer_name: committer.name,
- committer_email: committer.email
- }
- end
- let(:branch) { described_class.new(repository, 'foo', gitaly_branch) }
-
- it 'parses Gitaly::FindLocalBranchResponse correctly' do
- expect(Gitlab::Git::Commit).to receive(:decorate)
- .with(hash_including(attributes)).and_call_original
-
- expect(branch.dereferenced_target.message).to be_utf8
- end
- end
-
describe '#size' do
subject { super().size }
it { is_expected.to eq(SeedRepo::Repo::BRANCHES.size) }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index afa2fde2a5b..83d067b2c31 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -234,33 +234,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { expect(repository.bare?).to be_truthy }
end
- describe '#heads' do
- let(:heads) { repository.heads }
- subject { heads }
-
- it { is_expected.to be_kind_of Array }
-
- describe '#size' do
- subject { super().size }
- it { is_expected.to eq(SeedRepo::Repo::BRANCHES.size) }
- end
-
- context :head do
- subject { heads.first }
-
- describe '#name' do
- subject { super().name }
- it { is_expected.to eq("feature") }
- end
-
- context :commit do
- subject { heads.first.dereferenced_target.sha }
-
- it { is_expected.to eq("0b4bc9a49b562e85de7cc9e834518ea6828729b9") }
- end
- end
- end
-
describe '#ref_names' do
let(:ref_names) { repository.ref_names }
subject { ref_names }
@@ -278,42 +251,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#search_files' do
- let(:results) { repository.search_files('rails', 'master') }
- subject { results }
-
- it { is_expected.to be_kind_of Array }
-
- describe '#first' do
- subject { super().first }
- it { is_expected.to be_kind_of Gitlab::Git::BlobSnippet }
- end
-
- context 'blob result' do
- subject { results.first }
-
- describe '#ref' do
- subject { super().ref }
- it { is_expected.to eq('master') }
- end
-
- describe '#filename' do
- subject { super().filename }
- it { is_expected.to eq('CHANGELOG') }
- end
-
- describe '#startline' do
- subject { super().startline }
- it { is_expected.to eq(35) }
- end
-
- describe '#data' do
- subject { super().data }
- it { is_expected.to include "Ability to filter by multiple labels" }
- end
- end
- end
-
describe '#submodule_url_for' do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) }
let(:ref) { 'master' }
@@ -521,7 +458,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
it "should refresh the repo's #heads collection" do
- head_names = @normal_repo.heads.map { |h| h.name }
+ head_names = @normal_repo.branches.map { |h| h.name }
expect(head_names).to include(new_branch)
end
@@ -542,7 +479,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
eq(normal_repo.rugged.branches["master"].target.oid)
)
- head_names = normal_repo.heads.map { |h| h.name }
+ head_names = normal_repo.branches.map { |h| h.name }
expect(head_names).not_to include(new_branch)
end
@@ -589,10 +526,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(@repo.rugged.branches["feature"]).to be_nil
end
- it "should update the repo's #heads collection" do
- expect(@repo.heads).not_to include("feature")
- end
-
after(:all) do
FileUtils.rm_rf(TEST_MUTABLE_REPO_PATH)
ensure_seeds
diff --git a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
index b333e162909..3de73a9ff65 100644
--- a/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/fs_shards_check_spec.rb
@@ -109,9 +109,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do
expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency, value: be >= 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency, value: be >= 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
end
end
@@ -127,9 +127,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do
expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 1))
expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 1))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency, value: be >= 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency, value: be >= 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
end
end
end
@@ -159,9 +159,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do
expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency, value: be >= 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency, value: be >= 0))
- expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
+ expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
end
end
end
diff --git a/spec/lib/gitlab/health_checks/simple_check_shared.rb b/spec/lib/gitlab/health_checks/simple_check_shared.rb
index 1abebeac4dd..e2643458aca 100644
--- a/spec/lib/gitlab/health_checks/simple_check_shared.rb
+++ b/spec/lib/gitlab/health_checks/simple_check_shared.rb
@@ -8,7 +8,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 1)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 0)) }
- it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency", value: be >= 0)) }
+ it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency_seconds", value: be >= 0)) }
end
context 'Check is misbehaving' do
@@ -18,7 +18,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 0)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 0)) }
- it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency", value: be >= 0)) }
+ it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency_seconds", value: be >= 0)) }
end
context 'Check is timeouting' do
@@ -28,7 +28,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 0)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 1)) }
- it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency", value: be >= 0)) }
+ it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency_seconds", value: be >= 0)) }
end
end
diff --git a/spec/lib/gitlab/metrics/connection_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
index 94251af305f..461b1e4182a 100644
--- a/spec/lib/gitlab/metrics/connection_rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Metrics::ConnectionRackMiddleware do
+describe Gitlab::Metrics::RequestsRackMiddleware do
let(:app) { double('app') }
subject { described_class.new(app) }
@@ -22,14 +22,8 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
allow(app).to receive(:call).and_return([200, nil, nil])
end
- it 'increments response count with status label' do
- expect(described_class).to receive_message_chain(:rack_response_count, :increment).with(include(status: 200, method: 'get'))
-
- subject.call(env)
- end
-
it 'increments requests count' do
- expect(described_class).to receive_message_chain(:rack_request_count, :increment).with(method: 'get')
+ expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get')
subject.call(env)
end
@@ -38,20 +32,21 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
execution_time = 10
allow(app).to receive(:call) do |*args|
Timecop.freeze(execution_time.seconds)
+ [200, nil, nil]
end
- expect(described_class).to receive_message_chain(:rack_execution_time, :observe).with({}, execution_time)
+ expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ status: 200, method: 'get' }, execution_time)
subject.call(env)
end
end
context '@app.call throws exception' do
- let(:rack_response_count) { double('rack_response_count') }
+ let(:http_request_duration_seconds) { double('http_request_duration_seconds') }
before do
allow(app).to receive(:call).and_raise(StandardError)
- allow(described_class).to receive(:rack_response_count).and_return(rack_response_count)
+ allow(described_class).to receive(:http_request_duration_seconds).and_return(http_request_duration_seconds)
end
it 'increments exceptions count' do
@@ -61,25 +56,13 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
end
it 'increments requests count' do
- expect(described_class).to receive_message_chain(:rack_request_count, :increment).with(method: 'get')
-
- expect { subject.call(env) }.to raise_error(StandardError)
- end
-
- it "does't increment response count" do
- expect(described_class.rack_response_count).not_to receive(:increment)
+ expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get')
expect { subject.call(env) }.to raise_error(StandardError)
end
- it 'measures execution time' do
- execution_time = 10
- allow(app).to receive(:call) do |*args|
- Timecop.freeze(execution_time.seconds)
- raise StandardError
- end
-
- expect(described_class).to receive_message_chain(:rack_execution_time, :observe).with({}, execution_time)
+ it "does't measure request execution time" do
+ expect(described_class.http_request_duration_seconds).not_to receive(:increment)
expect { subject.call(env) }.to raise_error(StandardError)
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 62c4ea01ce1..89ea5ceda95 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -63,6 +63,14 @@ describe Namespace, models: true do
it { is_expected.to respond_to(:has_parent?) }
end
+ describe 'inclusions' do
+ it { is_expected.to include_module(Gitlab::VisibilityLevel) }
+ end
+
+ describe '#visibility_level_field' do
+ it { expect(namespace.visibility_level_field).to eq(:visibility_level) }
+ end
+
describe '#to_param' do
it { expect(namespace.to_param).to eq(namespace.full_path) }
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c4bc129dd37..e636250c37d 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1236,7 +1236,7 @@ describe Project, models: true do
subject { project.rename_repo }
- it { expect{subject}.to raise_error(Exception) }
+ it { expect{subject}.to raise_error(StandardError) }
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c614df8e98a..69f2570eec2 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -763,7 +763,7 @@ describe User, models: true do
end
it 'returns users with a partially matching name' do
- expect(described_class.search(user.name[0..2])).to eq([user2, user])
+ expect(described_class.search(user.name[0..2])).to eq([user, user2])
end
it 'returns users with a matching name regardless of the casing' do
@@ -777,7 +777,7 @@ describe User, models: true do
end
it 'returns users with a partially matching Email' do
- expect(described_class.search(user.email[0..2])).to eq([user2, user])
+ expect(described_class.search(user.email[0..2])).to eq([user, user2])
end
it 'returns users with a matching Email regardless of the casing' do
@@ -791,7 +791,7 @@ describe User, models: true do
end
it 'returns users with a partially matching username' do
- expect(described_class.search(user.username[0..2])).to eq([user2, user])
+ expect(described_class.search(user.username[0..2])).to eq([user, user2])
end
it 'returns users with a matching username regardless of the casing' do
diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb
index 8870d48bbc9..7bbf34422b8 100644
--- a/spec/requests/api/version_spec.rb
+++ b/spec/requests/api/version_spec.rb
@@ -6,7 +6,7 @@ describe API::Version do
it 'returns authentication error' do
get api('/version')
- expect(response).to have_http_status(401)
+ expect(response).to have_gitlab_http_status(401)
end
end
@@ -16,7 +16,7 @@ describe API::Version do
it 'returns the version information' do
get api('/version', user)
- expect(response).to have_http_status(200)
+ expect(response).to have_gitlab_http_status(200)
expect(json_response['version']).to eq(Gitlab::VERSION)
expect(json_response['revision']).to eq(Gitlab::REVISION)
end
diff --git a/spec/rubocop/cop/migration/hash_index_spec.rb b/spec/rubocop/cop/migration/hash_index_spec.rb
new file mode 100644
index 00000000000..9a8576a19e5
--- /dev/null
+++ b/spec/rubocop/cop/migration/hash_index_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../../rubocop/cop/migration/hash_index'
+
+describe RuboCop::Cop::Migration::HashIndex do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'in migration' do
+ before do
+ allow(cop).to receive(:in_migration?).and_return(true)
+ end
+
+ it 'registers an offense when creating a hash index' do
+ inspect_source(cop, 'def change; add_index :table, :column, using: :hash; end')
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ end
+ end
+
+ it 'registers an offense when creating a concurrent hash index' do
+ inspect_source(cop, 'def change; add_concurrent_index :table, :column, using: :hash; end')
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ end
+ end
+
+ it 'registers an offense when creating a hash index using t.index' do
+ inspect_source(cop, 'def change; t.index :table, :column, using: :hash; end')
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ end
+ end
+ end
+
+ context 'outside of migration' do
+ it 'registers no offense' do
+ inspect_source(cop, 'def change; index :table, :column, using: :hash; end')
+
+ expect(cop.offenses.size).to eq(0)
+ end
+ end
+end
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index 05b18fef061..fd4011ad606 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -1,11 +1,14 @@
require 'spec_helper'
-describe Projects::UpdateService, services: true do
+describe Projects::UpdateService, '#execute', :services do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
- let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
- describe 'update_by_user' do
+ let(:project) do
+ create(:empty_project, creator: user, namespace: user.namespace)
+ end
+
+ context 'when changing visibility level' do
context 'when visibility_level is INTERNAL' do
it 'updates the project to internal' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
@@ -40,7 +43,7 @@ describe Projects::UpdateService, services: true do
it 'does not update the project to public' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- expect(result).to eq({ status: :error, message: 'Visibility level unallowed' })
+ expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
expect(project).to be_private
end
@@ -55,12 +58,13 @@ describe Projects::UpdateService, services: true do
end
end
- describe 'visibility_level' do
+ describe 'when updating project that has forks' do
let(:project) { create(:empty_project, :internal) }
let(:forked_project) { create(:forked_project_with_submodules, :internal) }
before do
- forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id)
+ forked_project.build_forked_project_link(forked_to_project_id: forked_project.id,
+ forked_from_project_id: project.id)
forked_project.save
end
@@ -89,10 +93,38 @@ describe Projects::UpdateService, services: true do
end
end
- it 'returns an error result when record cannot be updated' do
- result = update_project(project, admin, { name: 'foo&bar' })
+ context 'when updating a default branch' do
+ let(:project) { create(:project, :repository) }
+
+ it 'changes a default branch' do
+ update_project(project, admin, default_branch: 'feature')
+
+ expect(Project.find(project.id).default_branch).to eq 'feature'
+ end
+ end
- expect(result).to eq({ status: :error, message: 'Project could not be updated' })
+ context 'when renaming project that contains container images' do
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: /image/, tags: %w[rc1])
+ create(:container_repository, project: project, name: :image)
+ end
+
+ it 'does not allow to rename the project' do
+ result = update_project(project, admin, path: 'renamed')
+
+ expect(result).to include(status: :error)
+ expect(result[:message]).to match(/contains container registry tags/)
+ end
+ end
+
+ context 'when passing invalid parameters' do
+ it 'returns an error result when record cannot be updated' do
+ result = update_project(project, admin, { name: 'foo&bar' })
+
+ expect(result).to eq({ status: :error,
+ message: 'Project could not be updated!' })
+ end
end
def update_project(project, user, opts)
diff --git a/spec/support/matchers/have_gitlab_http_status.rb b/spec/support/matchers/have_gitlab_http_status.rb
new file mode 100644
index 00000000000..3198f1b9edd
--- /dev/null
+++ b/spec/support/matchers/have_gitlab_http_status.rb
@@ -0,0 +1,14 @@
+RSpec::Matchers.define :have_gitlab_http_status do |expected|
+ match do |actual|
+ expect(actual).to have_http_status(expected)
+ end
+
+ description do
+ "respond with numeric status code #{expected}"
+ end
+
+ failure_message do |actual|
+ "expected the response to have status code #{expected.inspect}" \
+ " but it was #{actual.response_code}. The response was: #{actual.body}"
+ end
+end
diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb
index 5478fea4e64..d143014692d 100644
--- a/spec/support/sidekiq.rb
+++ b/spec/support/sidekiq.rb
@@ -8,4 +8,8 @@ RSpec.configure do |config|
config.after(:each, :sidekiq) do
Sidekiq::Worker.clear_all
end
+
+ config.after(:each, :sidekiq, :redis) do
+ Sidekiq.redis { |redis| redis.flushdb }
+ end
end