summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/dispatcher.js10
-rw-r--r--app/assets/javascripts/pipelines/pipelines_charts.js38
-rw-r--r--app/assets/javascripts/pipelines/pipelines_times.js27
-rw-r--r--app/assets/javascripts/projects/project_new.js43
-rw-r--r--app/assets/javascripts/ref_select_dropdown.js3
-rw-r--r--app/assets/stylesheets/framework/header.scss4
-rw-r--r--app/assets/stylesheets/framework/layout.scss4
-rw-r--r--app/assets/stylesheets/framework/nav.scss20
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss4
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/assets/stylesheets/new_nav.scss3
-rw-r--r--app/assets/stylesheets/new_sidebar.scss12
-rw-r--r--app/assets/stylesheets/pages/builds.scss15
-rw-r--r--app/assets/stylesheets/pages/issuable.scss8
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/performance_bar.scss14
-rw-r--r--app/helpers/application_helper.rb6
-rw-r--r--app/helpers/nav_helper.rb33
-rw-r--r--app/views/layouts/_page.html.haml4
-rw-r--r--app/views/layouts/application.html.haml5
-rw-r--r--app/views/peek/views/_host.html.haml2
-rw-r--r--app/views/projects/new.html.haml45
-rw-r--r--app/views/projects/pipelines/charts/_pipeline_times.haml25
-rw-r--r--app/views/projects/pipelines/charts/_pipelines.haml36
-rw-r--r--app/views/projects/pipelines/new.html.haml5
-rw-r--r--app/views/projects/tags/new.html.haml5
-rw-r--r--app/views/projects/tree/_tree_content.html.haml8
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml3
-rw-r--r--app/workers/git_garbage_collect_worker.rb39
-rw-r--r--changelogs/unreleased/35672-edge-top-bar.yml4
-rw-r--r--changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml4
-rw-r--r--config/webpack.config.js5
-rw-r--r--db/schema.rb20
-rw-r--r--doc/README.md13
-rw-r--r--doc/administration/monitoring/performance/img/performance_bar.pngbin186116 -> 170256 bytes
-rw-r--r--doc/user/index.md2
-rw-r--r--doc/user/profile/index.md2
-rw-r--r--doc/user/project/index.md107
-rw-r--r--doc/user/project/koding.md2
-rw-r--r--doc/user/project/repository/index.md2
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb18
-rw-r--r--spec/features/admin/admin_users_impersonation_tokens_spec.rb4
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb52
44 files changed, 491 insertions, 172 deletions
diff --git a/Gemfile.lock b/Gemfile.lock
index 627750e2c1d..05a70704513 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -323,7 +323,7 @@ GEM
multi_json (~> 1.10)
retriable (~> 1.4)
signet (~> 0.6)
- google-protobuf (3.2.0.2)
+ google-protobuf (3.3.0)
googleauth (0.5.1)
faraday (~> 0.9)
jwt (~> 1.4)
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 9d706b5ba59..d5923266c60 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -8,6 +8,7 @@
/* global LabelsSelect */
/* global MilestoneSelect */
/* global Commit */
+/* global NewBranchForm */
/* global NotificationsForm */
/* global NotificationsDropdown */
/* global GroupAvatar */
@@ -258,7 +259,7 @@ import GpgBadges from './gpg_badges';
case 'projects:tags:new':
new ZenMode();
new gl.GLForm($('.tag-form'), true);
- new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
+ new RefSelectDropdown($('.js-branch-select'));
break;
case 'projects:snippets:show':
initNotes();
@@ -330,6 +331,9 @@ import GpgBadges from './gpg_badges';
case 'projects:edit':
setupProjectEdit();
break;
+ case 'projects:pipelines:new':
+ new NewBranchForm($('.js-new-pipeline-form'));
+ break;
case 'projects:pipelines:builds':
case 'projects:pipelines:failures':
case 'projects:pipelines:show':
@@ -383,6 +387,9 @@ import GpgBadges from './gpg_badges';
shortcut_handler = new ShortcutsNavigation();
new TreeView();
new BlobViewer();
+ $('#tree-slider').waitForImages(function() {
+ gl.utils.ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
+ });
break;
case 'projects:find_file:show':
shortcut_handler = true;
@@ -540,6 +547,7 @@ import GpgBadges from './gpg_badges';
shortcut_handler = new ShortcutsWiki();
new ZenMode();
new gl.GLForm($('.wiki-form'), true);
+ new Sidebar();
break;
case 'snippets':
shortcut_handler = new ShortcutsNavigation();
diff --git a/app/assets/javascripts/pipelines/pipelines_charts.js b/app/assets/javascripts/pipelines/pipelines_charts.js
new file mode 100644
index 00000000000..001faf4be33
--- /dev/null
+++ b/app/assets/javascripts/pipelines/pipelines_charts.js
@@ -0,0 +1,38 @@
+import Chart from 'vendor/Chart';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
+ const buildChart = (chartScope) => {
+ const data = {
+ labels: chartScope.labels,
+ datasets: [{
+ fillColor: '#7f8fa4',
+ strokeColor: '#7f8fa4',
+ pointColor: '#7f8fa4',
+ pointStrokeColor: '#EEE',
+ data: chartScope.totalValues,
+ },
+ {
+ fillColor: '#44aa22',
+ strokeColor: '#44aa22',
+ pointColor: '#44aa22',
+ pointStrokeColor: '#fff',
+ data: chartScope.successValues,
+ },
+ ],
+ };
+ const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
+ const options = {
+ scaleOverlay: true,
+ responsive: true,
+ maintainAspectRatio: false,
+ };
+ if (window.innerWidth < 768) {
+ // Scale fonts if window width lower than 768px (iPad portrait)
+ options.scaleFontSize = 8;
+ }
+ new Chart(ctx).Line(data, options);
+ };
+
+ chartData.forEach(scope => buildChart(scope));
+});
diff --git a/app/assets/javascripts/pipelines/pipelines_times.js b/app/assets/javascripts/pipelines/pipelines_times.js
new file mode 100644
index 00000000000..b5e7a0e53d9
--- /dev/null
+++ b/app/assets/javascripts/pipelines/pipelines_times.js
@@ -0,0 +1,27 @@
+import Chart from 'vendor/Chart';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
+ const data = {
+ labels: chartData.labels,
+ datasets: [{
+ fillColor: 'rgba(220,220,220,0.5)',
+ strokeColor: 'rgba(220,220,220,1)',
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
+ data: chartData.values,
+ }],
+ };
+ const ctx = $('#build_timesChart').get(0).getContext('2d');
+ const options = {
+ scaleOverlay: true,
+ responsive: true,
+ maintainAspectRatio: false,
+ };
+ if (window.innerWidth < 768) {
+ // Scale fonts if window width lower than 768px (iPad portrait)
+ options.scaleFontSize = 8;
+ }
+ new Chart(ctx).Bar(data, options);
+});
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
new file mode 100644
index 00000000000..2091b275c3d
--- /dev/null
+++ b/app/assets/javascripts/projects/project_new.js
@@ -0,0 +1,43 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const importBtnTooltip = 'Please enter a valid project name.';
+ const $importBtnWrapper = $('.import_gitlab_project');
+
+ $('.how_to_import_link').on('click', (e) => {
+ e.preventDefault();
+ $('.how_to_import_link').next('.modal').show();
+ });
+
+ $('.modal-header .close').on('click', () => {
+ $('.modal').hide();
+ });
+
+ $('.btn_import_gitlab_project').on('click', () => {
+ const importHref = $('a.btn_import_gitlab_project').attr('href');
+ $('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$('#project_path').val()}`);
+ });
+
+ $('.btn_import_gitlab_project').attr('disabled', !$('#project_path').val().trim().length);
+ $importBtnWrapper.attr('title', importBtnTooltip);
+
+ $('#new_project').on('submit', () => {
+ const $path = $('#project_path');
+ $path.val($path.val().trim());
+ });
+
+ $('#project_path').on('keyup', () => {
+ if ($('#project_path').val().trim().length) {
+ $('.btn_import_gitlab_project').attr('disabled', false);
+ $importBtnWrapper.attr('title', '');
+ $importBtnWrapper.removeClass('has-tooltip');
+ } else {
+ $('.btn_import_gitlab_project').attr('disabled', true);
+ $importBtnWrapper.addClass('has-tooltip');
+ }
+ });
+
+ $('#project_import_url').disable();
+ $('.import_git').on('click', () => {
+ const $projectImportUrl = $('#project_import_url');
+ $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
+ });
+});
diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js
index 215cd6fbdfd..65e4101352c 100644
--- a/app/assets/javascripts/ref_select_dropdown.js
+++ b/app/assets/javascripts/ref_select_dropdown.js
@@ -1,7 +1,8 @@
class RefSelectDropdown {
constructor($dropdownButton, availableRefs) {
+ const availableRefsValue = availableRefs || JSON.parse(document.getElementById('availableRefs').innerHTML);
$dropdownButton.glDropdown({
- data: availableRefs,
+ data: availableRefsValue,
filterable: true,
filterByText: true,
remote: false,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 605f4284bb5..df847094864 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -315,6 +315,10 @@ header {
}
}
+.with-performance-bar header.navbar-gitlab {
+ top: $performance-bar-height;
+}
+
.navbar-nav {
li {
.badge {
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 4a9d41b4fda..67c3287ed74 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -120,3 +120,7 @@ of the body element here, we negate cascading side effects but allow momentum sc
.page-with-sidebar {
-webkit-overflow-scrolling: auto;
}
+
+.with-performance-bar .page-with-sidebar {
+ margin-top: $header-height + $performance-bar-height;
+}
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 35b4d77a5ab..88e7ba117d5 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -347,6 +347,10 @@
}
}
+.with-performance-bar .layout-nav {
+ margin-top: $header-height + $performance-bar-height;
+}
+
.scrolling-tabs-container {
position: relative;
@@ -441,6 +445,22 @@
}
}
+.with-performance-bar .page-with-layout-nav {
+ .right-sidebar {
+ top: ($header-height + 1) * 2 + $performance-bar-height;
+ }
+
+ &.page-with-sub-nav {
+ .right-sidebar {
+ top: ($header-height + 1) * 3 + $performance-bar-height;
+
+ &.affix {
+ top: $header-height + $performance-bar-height;
+ }
+ }
+ }
+}
+
.nav-block {
&.activities {
border-bottom: 1px solid $border-color;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 49b2f0e43a4..09b60ad1676 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -89,6 +89,10 @@
}
}
+.with-performance-bar .right-sidebar.affix {
+ top: $header-height + $performance-bar-height;
+}
+
@mixin maintain-sidebar-dimensions {
display: block;
width: $gutter-width;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index cf0a1ad57d0..0df6f24bfe6 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -204,6 +204,7 @@ $divergence-graph-separator-bg: #ccc;
$general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
+$performance-bar-height: 35px;
/*
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 360ffda8d71..781d4cc8833 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -325,6 +325,7 @@ header.navbar-gitlab-new {
.breadcrumbs-links {
flex: 1;
+ min-width: 0;
align-self: center;
color: $gl-text-color-quaternary;
@@ -343,7 +344,7 @@ header.navbar-gitlab-new {
}
.title {
- white-space: nowrap;
+ display: inline-block;
> a {
&:last-of-type:not(:first-child) {
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index ae43197a1a6..54f3e8d882c 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -118,7 +118,7 @@ $new-sidebar-width: 220px;
z-index: 400;
width: $new-sidebar-width;
transition: left $sidebar-transition-duration;
- top: 50px;
+ top: $header-height;
bottom: 0;
left: 0;
overflow: auto;
@@ -163,6 +163,10 @@ $new-sidebar-width: 220px;
}
}
+.with-performance-bar .nav-sidebar {
+ top: $header-height + $performance-bar-height;
+}
+
.sidebar-sub-level-items {
display: none;
padding-bottom: 8px;
@@ -260,7 +264,7 @@ $new-sidebar-width: 220px;
// Make issue boards full-height now that sub-nav is gone
.boards-list {
- height: calc(100vh - 50px);
+ height: calc(100vh - #{$header-height});
@media (min-width: $screen-sm-min) {
height: 475px; // Needed for PhantomJS
@@ -270,6 +274,10 @@ $new-sidebar-width: 220px;
}
}
+.with-performance-bar .boards-list {
+ height: calc(100vh - #{$header-height} - #{$performance-bar-height});
+}
+
// Change color of all horizontal tabs to match the new indigo color
.nav-links li.active a {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index b6fc628c02b..acf3719e9d2 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -64,10 +64,10 @@
color: $gl-text-color;
position: sticky;
position: -webkit-sticky;
- top: 50px;
+ top: $header-height;
&.affix {
- top: 50px;
+ top: $header-height;
}
// with sidebar
@@ -86,6 +86,7 @@
position: absolute;
right: 0;
left: 0;
+ top: 0;
}
.truncated-info {
@@ -171,6 +172,16 @@
}
}
+.with-performance-bar .build-page {
+ .top-bar {
+ top: $header-height + $performance-bar-height;
+
+ &.affix {
+ top: $header-height + $performance-bar-height;
+ }
+ }
+}
+
.build-header {
.ci-header-container,
.header-action-buttons {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index eb269df46fe..6da14320914 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -445,6 +445,14 @@
}
}
+.with-performance-bar .right-sidebar {
+ top: $header-height + $performance-bar-height;
+
+ .issuable-sidebar {
+ height: calc(100% - #{$header-height} - #{$performance-bar-height});
+ }
+}
+
.detail-page-description {
padding: 16px 0;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 2db967547dd..4693b2434c7 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -759,6 +759,10 @@
}
}
+.with-performance-bar .merge-request-tabs-holder {
+ top: $header-height + $performance-bar-height;
+}
+
.merge-request-tabs {
display: flex;
margin-bottom: 0;
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 2890b6b1e49..6e539e39ca1 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -3,9 +3,16 @@
@import "peek/views/rblineprof";
#peek {
- height: 35px;
+ position: fixed;
+ left: 0;
+ top: 0;
+ width: 100%;
+ z-index: 2000;
+ overflow-x: hidden;
+
+ height: $performance-bar-height;
background: $black;
- line-height: 35px;
+ line-height: $performance-bar-height;
color: $perf-bar-text;
&.disabled {
@@ -25,7 +32,8 @@
}
.wrapper {
- width: 1000px;
+ width: 80%;
+ height: $performance-bar-height;
margin: 0 auto;
}
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 1c165700b19..14dc9bd9d62 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -264,7 +264,11 @@ module ApplicationHelper
end
def page_class
- "issue-boards-page" if current_controller?(:boards)
+ class_names = []
+ class_names << 'issue-boards-page' if current_controller?(:boards)
+ class_names << 'with-performance-bar' if performance_bar_enabled?
+
+ class_names
end
# Returns active css class when condition returns true
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index b769462abc2..b1205b8529b 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -1,38 +1,49 @@
module NavHelper
+ def page_with_sidebar_class
+ class_name = page_gutter_class
+ class_name << 'page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar
+
+ class_name
+ end
+
def page_gutter_class
if current_path?('merge_requests#show') ||
current_path?('projects/merge_requests/conflicts#show') ||
current_path?('issues#show') ||
current_path?('milestones#show')
if cookies[:collapsed_gutter] == 'true'
- "page-gutter right-sidebar-collapsed"
+ %w[page-gutter right-sidebar-collapsed]
else
- "page-gutter right-sidebar-expanded"
+ %w[page-gutter right-sidebar-expanded]
end
elsif current_path?('jobs#show')
- "page-gutter build-sidebar right-sidebar-expanded"
+ %w[page-gutter build-sidebar right-sidebar-expanded]
elsif current_path?('wikis#show') ||
current_path?('wikis#edit') ||
current_path?('wikis#update') ||
current_path?('wikis#history') ||
current_path?('wikis#git_access')
- "page-gutter wiki-sidebar right-sidebar-expanded"
+ %w[page-gutter wiki-sidebar right-sidebar-expanded]
+ else
+ []
end
end
def nav_header_class
- class_name = ''
- class_name << " with-horizontal-nav" if defined?(nav) && nav
+ class_names = []
+ class_names << 'with-horizontal-nav' if defined?(nav) && nav
- class_name
+ class_names
end
def layout_nav_class
- class_name = ''
- class_name << " page-with-layout-nav" if defined?(nav) && nav
- class_name << " page-with-sub-nav" if content_for?(:sub_nav)
+ return [] if show_new_nav?
- class_name
+ class_names = []
+ class_names << 'page-with-layout-nav' if defined?(nav) && nav
+ class_names << 'page-with-sub-nav' if content_for?(:sub_nav)
+
+ class_names
end
def nav_control_class
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 873220cc73d..c4f8cd71395 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,4 +1,4 @@
-.page-with-sidebar{ class: "#{('page-with-new-sidebar' if defined?(@new_sidebar) && @new_sidebar)} #{page_gutter_class}" }
+.page-with-sidebar{ class: page_with_sidebar_class }
- if show_new_nav?
- if defined?(nav) && nav
= render "layouts/nav/#{nav}"
@@ -9,7 +9,7 @@
= render "layouts/nav/#{nav}"
- if content_for?(:sub_nav)
= yield :sub_nav
- .content-wrapper{ class: "#{(layout_nav_class unless show_new_nav?)}" }
+ .content-wrapper{ class: layout_nav_class }
- if show_new_nav?
.mobile-overlay
.alert-wrapper
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 38b95d11fd4..b53f382fa3d 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,8 +1,9 @@
!!! 5
-%html{ lang: I18n.locale, class: "#{page_class}" }
+%html{ lang: I18n.locale, class: page_class }
= render "layouts/head"
%body{ class: @body_class, data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}", find_file: find_file_path } }
= render "layouts/init_auto_complete" if @gfm_form
+ = render 'peek/bar'
- if show_new_nav?
= render "layouts/header/new"
- else
@@ -10,5 +11,3 @@
= render 'layouts/page', sidebar: sidebar, nav: nav
= yield :scripts_body
-
- = render 'peek/bar'
diff --git a/app/views/peek/views/_host.html.haml b/app/views/peek/views/_host.html.haml
new file mode 100644
index 00000000000..40769b5c6f6
--- /dev/null
+++ b/app/views/peek/views/_host.html.haml
@@ -0,0 +1,2 @@
+%span.current-host
+ = truncate(view.hostname)
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 87cc23fc649..25109f0f414 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -4,6 +4,8 @@
- page_title 'New Project'
- header_title "Projects", dashboard_projects_path
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag 'project_new'
.project-edit-container
.project-edit-errors
@@ -111,46 +113,3 @@
%i.fa.fa-spinner.fa-spin
Creating project &amp; repository.
%p Please wait a moment, this page will automatically refresh when ready.
-
-:javascript
- var importBtnTooltip = "Please enter a valid project name.";
- var $importBtnWrapper = $('.import_gitlab_project');
-
- $('.how_to_import_link').bind('click', function (e) {
- e.preventDefault();
- var import_modal = $(this).next(".modal").show();
- });
-
- $('.modal-header .close').bind('click', function() {
- $(".modal").hide();
- });
-
- $('.btn_import_gitlab_project').bind('click', function() {
- var _href = $("a.btn_import_gitlab_project").attr("href");
- $(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val());
- });
-
- $('.btn_import_gitlab_project').attr('disabled', $('#project_path').val().trim().length === 0);
- $importBtnWrapper.attr('title', importBtnTooltip);
-
- $('#new_project').submit(function(){
- var $path = $('#project_path');
- $path.val($path.val().trim());
- });
-
- $('#project_path').keyup(function(){
- if($(this).val().trim().length !== 0) {
- $('.btn_import_gitlab_project').attr('disabled', false);
- $importBtnWrapper.attr('title','');
- $importBtnWrapper.removeClass('has-tooltip');
- } else {
- $('.btn_import_gitlab_project').attr('disabled',true);
- $importBtnWrapper.addClass('has-tooltip');
- }
- });
-
- $('#project_import_url').disable();
- $('.import_git').click(function( event ) {
- $projectImportUrl = $('#project_import_url');
- $projectImportUrl.attr('disabled', !$projectImportUrl.attr('disabled'));
- });
diff --git a/app/views/projects/pipelines/charts/_pipeline_times.haml b/app/views/projects/pipelines/charts/_pipeline_times.haml
index 1292f580a81..a5dbd1b1532 100644
--- a/app/views/projects/pipelines/charts/_pipeline_times.haml
+++ b/app/views/projects/pipelines/charts/_pipeline_times.haml
@@ -1,27 +1,10 @@
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag('pipelines_times')
+
%div
%p.light
= _("Commit duration in minutes for last 30 commits")
%canvas#build_timesChart{ height: 200 }
-:javascript
- var data = {
- labels : #{@charts[:pipeline_times].labels.to_json},
- datasets : [
- {
- fillColor : "rgba(220,220,220,0.5)",
- strokeColor : "rgba(220,220,220,1)",
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data : #{@charts[:pipeline_times].pipeline_times.to_json}
- }
- ]
- }
- var ctx = $("#build_timesChart").get(0).getContext("2d");
- var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
- if (window.innerWidth < 768) {
- // Scale fonts if window width lower than 768px (iPad portrait)
- options.scaleFontSize = 8
- }
- new Chart(ctx).Bar(data, options);
+%script#pipelinesTimesChartsData{ type: "application/json" }= { :labels => @charts[:pipeline_times].labels, :values => @charts[:pipeline_times].pipeline_times }.to_json.html_safe
diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml
index be884448087..02f1ef4b6da 100644
--- a/app/views/projects/pipelines/charts/_pipelines.haml
+++ b/app/views/projects/pipelines/charts/_pipelines.haml
@@ -1,3 +1,6 @@
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag('pipelines_charts')
+
%h4= _("Pipelines charts")
%p
&nbsp;
@@ -26,31 +29,8 @@
= _("Jobs for last year")
%canvas#yearChart.padded{ height: 250 }
-- [:week, :month, :year].each do |scope|
- :javascript
- var data = {
- labels : #{@charts[scope].labels.to_json},
- datasets : [
- {
- fillColor : "#7f8fa4",
- strokeColor : "#7f8fa4",
- pointColor : "#7f8fa4",
- pointStrokeColor : "#EEE",
- data : #{@charts[scope].total.to_json}
- },
- {
- fillColor : "#44aa22",
- strokeColor : "#44aa22",
- pointColor : "#44aa22",
- pointStrokeColor : "#fff",
- data : #{@charts[scope].success.to_json}
- }
- ]
- }
- var ctx = $("##{scope}Chart").get(0).getContext("2d");
- var options = { scaleOverlay: true, responsive: true, maintainAspectRatio: false };
- if (window.innerWidth < 768) {
- // Scale fonts if window width lower than 768px (iPad portrait)
- options.scaleFontSize = 8
- }
- new Chart(ctx).Line(data, options);
+%script#pipelinesChartsData{ type: "application/json" }
+ - chartData = []
+ - [:week, :month, :year].each do |scope|
+ - chartData.push({ 'scope' => scope, 'labels' => @charts[scope].labels, 'totalValues' => @charts[scope].total, 'successValues' => @charts[scope].success })
+ = chartData.to_json.html_safe
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index c966df62856..4ad37d0e882 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -20,7 +20,4 @@
= f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-cancel'
-:javascript
- var availableRefs = #{@project.repository.ref_names.to_json};
-
- new NewBranchForm($('.js-new-pipeline-form'), availableRefs)
+%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index f1bbaf40387..521b4d927bc 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -40,7 +40,4 @@
.form-actions
= button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
= link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
-
-:javascript
- window.gl = window.gl || { };
- window.gl.availableRefs = #{@project.repository.ref_names.to_json};
+%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 6560bd5ab3f..820b947804e 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -1,4 +1,4 @@
-.tree-content-holder
+.tree-content-holder.js-tree-content{ 'data-logs-path': @logs_path }
.table-holder
%table.table#tree-slider{ class: "table_#{@hex_path} tree-table" }
%thead
@@ -22,9 +22,3 @@
- if can_edit_tree?
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post
= render 'projects/blob/new_dir'
-
-:javascript
- // Load last commit log for each file in tree
- $('#tree-slider').waitForImages(function() {
- gl.utils.ajaxGet("#{escape_javascript(@logs_path)}");
- });
diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml
index 62873d3aa66..e71ce1f357f 100644
--- a/app/views/projects/wikis/_sidebar.html.haml
+++ b/app/views/projects/wikis/_sidebar.html.haml
@@ -19,6 +19,3 @@
More Pages
= render 'projects/wikis/new'
-
-:javascript
- new Sidebar();
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index d369b639ae9..c95497dfaba 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -5,6 +5,12 @@ class GitGarbageCollectWorker
sidekiq_options retry: false
+ GITALY_MIGRATED_TASKS = {
+ gc: :garbage_collect,
+ full_repack: :repack_full,
+ incremental_repack: :repack_incremental
+ }.freeze
+
def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
project = Project.find(project_id)
task = task.to_sym
@@ -15,8 +21,14 @@ class GitGarbageCollectWorker
Gitlab::GitLogger.info(description)
- output, status = Gitlab::Popen.popen(cmd, repo_path)
- Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
+ gitaly_migrate(GITALY_MIGRATED_TASKS[task]) do |is_enabled|
+ if is_enabled
+ gitaly_call(task, project.repository.raw_repository)
+ else
+ output, status = Gitlab::Popen.popen(cmd, repo_path)
+ Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
+ end
+ end
# Refresh the branch cache in case garbage collection caused a ref lookup to fail
flush_ref_caches(project) if task == :gc
@@ -26,6 +38,19 @@ class GitGarbageCollectWorker
private
+ ## `repository` has to be a Gitlab::Git::Repository
+ def gitaly_call(task, repository)
+ client = Gitlab::GitalyClient::RepositoryService.new(repository)
+ case task
+ when :gc
+ client.garbage_collect(bitmaps_enabled?)
+ when :full_repack
+ client.repack_full(bitmaps_enabled?)
+ when :incremental_repack
+ client.repack_incremental
+ end
+ end
+
def command(task)
case task
when :gc
@@ -55,4 +80,14 @@ class GitGarbageCollectWorker
config_value = write_bitmaps ? 'true' : 'false'
%W[git -c repack.writeBitmaps=#{config_value}]
end
+
+ def gitaly_migrate(method, &block)
+ Gitlab::GitalyClient.migrate(method, &block)
+ rescue GRPC::NotFound => e
+ Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
+ raise Gitlab::Git::Repository::NoRepository.new(e)
+ rescue GRPC::BadStatus => e
+ Gitlab::GitLogger.error("#{method} failed:\n#{e}")
+ raise Gitlab::Git::CommandError.new(e)
+ end
end
diff --git a/changelogs/unreleased/35672-edge-top-bar.yml b/changelogs/unreleased/35672-edge-top-bar.yml
new file mode 100644
index 00000000000..0424dee19af
--- /dev/null
+++ b/changelogs/unreleased/35672-edge-top-bar.yml
@@ -0,0 +1,4 @@
+---
+title: Properly affixes nav bar in job view in microsoft edge
+merge_request:
+author:
diff --git a/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml b/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml
new file mode 100644
index 00000000000..988fdacb5fd
--- /dev/null
+++ b/changelogs/unreleased/breadcrumbs-collapsed-title-width-fix.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed breadcrumbs title aggressively collapsing
+merge_request:
+author:
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 41d3ed12b14..2f85b89d523 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -54,8 +54,11 @@ var config = {
notebook_viewer: './blob/notebook_viewer.js',
pdf_viewer: './blob/pdf_viewer.js',
pipelines: './pipelines/pipelines_bundle.js',
- pipelines_details: './pipelines/pipeline_details_bundle.js',
+ pipelines_charts: './pipelines/pipelines_charts.js',
+ pipelines_details: './pipelines/pipeline_details_bundle.js',
+ pipelines_times: './pipelines/pipelines_times.js',
profile: './profile/profile_bundle.js',
+ project_new: './projects/project_new.js',
prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches',
protected_tags: './protected_tags',
diff --git a/db/schema.rb b/db/schema.rb
index 0abdb987b77..5fbbdea6eaa 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -254,32 +254,32 @@ ActiveRecord::Schema.define(version: 20170725145659) do
add_index "ci_builds", ["updated_at"], name: "index_ci_builds_on_updated_at", using: :btree
add_index "ci_builds", ["user_id"], name: "index_ci_builds_on_user_id", using: :btree
- create_table "ci_pipeline_schedule_variables", force: :cascade do |t|
+ create_table "ci_group_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
- t.integer "pipeline_schedule_id", null: false
+ t.integer "group_id", null: false
+ t.boolean "protected", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
- add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
+ add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
- create_table "ci_group_variables", force: :cascade do |t|
+ create_table "ci_pipeline_schedule_variables", force: :cascade do |t|
t.string "key", null: false
t.text "value"
t.text "encrypted_value"
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
- t.integer "group_id", null: false
- t.boolean "protected", default: false, null: false
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ t.integer "pipeline_schedule_id", null: false
+ t.datetime "created_at"
+ t.datetime "updated_at"
end
- add_index "ci_group_variables", ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
+ add_index "ci_pipeline_schedule_variables", ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree
create_table "ci_pipeline_schedules", force: :cascade do |t|
t.string "description"
@@ -1624,8 +1624,8 @@ ActiveRecord::Schema.define(version: 20170725145659) do
add_foreign_key "ci_builds", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_a2141b1522", on_delete: :nullify
add_foreign_key "ci_builds", "ci_stages", column: "stage_id", name: "fk_3a9eaa254d", on_delete: :cascade
add_foreign_key "ci_builds", "projects", name: "fk_befce0568a", on_delete: :cascade
- add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade
add_foreign_key "ci_group_variables", "namespaces", column: "group_id", name: "fk_33ae4d58d8", on_delete: :cascade
+ add_foreign_key "ci_pipeline_schedule_variables", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_41c35fda51", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "projects", name: "fk_8ead60fcc4", on_delete: :cascade
add_foreign_key "ci_pipeline_schedules", "users", column: "owner_id", name: "fk_9ea99f58d2", on_delete: :nullify
add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index cc63ecb7eab..8bb8e147cd1 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -44,16 +44,17 @@ Shortcuts to GitLab's most visited docs:
### Projects and groups
-- [Create a project](gitlab-basics/create-project.md)
-- [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.
+- [Projects](user/project/index.md):
+ - [Create a project](gitlab-basics/create-project.md)
+ - [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.
+ - [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages.
- [Groups](user/group/index.md): Organize your projects in groups.
- - [GitLab Subgroups](user/group/subgroups/index.md)
+ - [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.
- [Wikis](user/project/wiki/index.md): Enhance your repository documentation with built-in wikis.
-- [GitLab Pages](user/project/pages/index.md): Build, test, and deploy your static website with GitLab Pages.
### Repository
diff --git a/doc/administration/monitoring/performance/img/performance_bar.png b/doc/administration/monitoring/performance/img/performance_bar.png
index d38293d2ed6..b3c6bc474e3 100644
--- a/doc/administration/monitoring/performance/img/performance_bar.png
+++ b/doc/administration/monitoring/performance/img/performance_bar.png
Binary files differ
diff --git a/doc/user/index.md b/doc/user/index.md
index 522cf932349..1281cc6e4f0 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -66,7 +66,7 @@ For more use cases please check our [Technical Articles](../articles/index.md).
## Projects
-In GitLab, you can create projects for numerous reasons, such as, host
+In GitLab, you can create [projects](project/index.md) for numerous reasons, such as, host
your code, use it as an issue tracker, collaborate on code, and continuously
build, test, and deploy your app with built-in GitLab CI/CD. Or, you can do
it all at once, from one single project.
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 0e3747817d2..7d25970fcb1 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -23,7 +23,7 @@ On your profile page, you will see the following information:
- Personal information
- Activity stream: see your activity streamline and the history of your contributions
- Groups: [groups](../group/index.md) you're a member of
-- Contributed projects: projects you contributed to
+- Contributed projects: [projects](../project/index.md) you contributed to
- Personal projects: your personal projects (respecting the project's visibility level)
- Snippets: your personal code [snippets](../snippets.md#personal-snippets)
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
new file mode 100644
index 00000000000..91a19600951
--- /dev/null
+++ b/doc/user/project/index.md
@@ -0,0 +1,107 @@
+# Projects
+
+In GitLab, you can create projects for hosting
+your codebase, use it as an issue tracker, collaborate on code, and continuously
+build, test, and deploy your app with built-in GitLab CI/CD.
+
+Your projects can be [available](../../public_access/public_access.md)
+publicly, internally, or privately, at your choice. GitLab does not limit
+the number of private projects you create.
+
+## Project's features
+
+When you create a project in GitLab, you'll have access to a large number of
+[features](https://about.gitlab.com/features/):
+
+**Issues and merge requests:**
+
+- [Issue tracker](issues/index.md): Discuss implementations with your team within issues
+ - [Issue Boards](issue_board.md): Organize and prioritize your workflow
+ - [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards) (**EES/EEP**): Allow your teams to create their own workflows (Issue Boards) for the same project
+- [Repositories](repository/index.md): Host your code in a fully
+integrated platform
+ - [Protected branches](protected_branches.md): Prevent collaborators
+ from messing with history or pushing code without review
+ - [Protected tags](protected_tags.md): Control over who has
+ permission to create tags, and prevent accidental update or deletion
+- [Merge Requests](merge_requests/index.md): Apply your branching
+strategy and get reviewed by your team
+ - [Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) (**EES/EEP**): Ask for approval before
+ implementing a change
+ - [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md):
+ Your Git diff tool right from GitLab's UI
+ - [Review Apps](../../ci/review_apps/index.md): Live preview the results
+ of the changes proposed in a merge request in a per-branch basis
+- [Labels](labels.md): Organize issues and merge requests by labels
+- [Time Tracking](../../workflow/time_tracking.md): Track estimate time
+and time spent on
+ the conclusion of an issue or merge request
+- [Milestones](milestones/index.md): Work towards a target date
+- [Description templates](description_templates.md): Define context-specific
+templates for issue and merge request description fields for your project
+- [Slash commands (quick actions)](quick_actions.md): Textual shortcuts for
+common actions on issues or merge requests
+
+**GitLab CI/CD:**
+
+- [GitLab CI/CD](../../ci/README.md): GitLab's built-in [Continuous Integration, Delivery, and Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) tool
+ - [Container Registry](container_registry.md): Build and push Docker
+ images out-of-the-box
+ - [Auto Deploy](../../ci/autodeploy/index.md): Configure GitLab CI/CD
+ to automatically set up your app's deployment
+ - [Enable and disable GitLab CI](../../ci/enable_or_disable_ci.md)
+ - [Pipelines](../../ci/pipelines.md#pipelines): Configure and visualize
+ your GitLab CI/CD pipelines from the UI
+ - [Scheduled Pipelines](pipelines/schedules.md): Schedule a pipeline
+ to start at a chosen time
+ - [Pipeline Graphs](../../ci/pipelines.md#pipeline-graphs): View your
+ entire pipeline from the UI
+ - [Job artifacts](pipelines/job_artifacts.md): Define,
+ browse, and download job artifacts
+ - [Pipeline settings](pipelines/settings.md): Set up Git strategy (choose the default way your repository is fetched from GitLab in a job),
+ timeout (defines the maximum amount of time in minutes that a job is able run), custom path for `.gitlab-ci.yml`, test coverage parsing, pipeline's visibility, and much more
+- [GitLab Pages](pages/index.md): Build, test, and deploy your static
+website with GitLab Pages
+
+**Other features:**
+
+- [Cycle Analytics](cycle_analytics.md): Review your development lifecycle
+- [Koding integration](koding.md) (not available on GitLab.com): Integrate
+with Koding to have access to a web terminal right from the GitLab UI
+- [Syntax highlighting](highlighting.md): An alternative to customize
+your code blocks, overriding GitLab's default choice of language
+
+### Project's integrations
+
+[Integrate your project](integrations/index.md) with Jira, Mattermost,
+Kubernetes, Slack, and a lot more.
+
+## New project
+
+Learn how to [create a new project](../../gitlab-basics/create-project.md) in GitLab.
+
+### Fork a project
+
+You can [fork a project](../../gitlab-basics/fork-project.md) in order to:
+
+- Collaborate on code by forking a project and creating a merge request
+from your fork to the upstream project
+- Fork a sample project to work on the top of that
+
+## Import or export a project
+
+- Import a project from:
+ - [GitHub to GitLab](../../workflow/importing/import_projects_from_github.md)
+ - [BitBucket to GitLab](../../workflow/importing/import_projects_from_bitbucket.md)
+ - [Gitea to GitLab](../../workflow/importing/import_projects_from_gitea.md)
+ - [FogBugz to GitLab](../../workflow/importing/import_projects_from_fogbugz.md)
+- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data)
+- [Importing and exporting projects between GitLab instances](settings/import_export.md)
+
+## Leave a project
+
+**Leave project** will only display on the project's dashboard
+when a project is part of a group (under a
+[group namespace](../group/index.md#namespaces)).
+If you choose to leave a project you will no longer be a project
+member, therefore, unable to contribute.
diff --git a/doc/user/project/koding.md b/doc/user/project/koding.md
index c56a1efe3c2..455e2ee47b4 100644
--- a/doc/user/project/koding.md
+++ b/doc/user/project/koding.md
@@ -1,4 +1,4 @@
-# Koding & GitLab
+# Koding integration
> [Introduced][ce-5909] in GitLab 8.11.
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index bb41eb41795..4b2c435a120 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -2,7 +2,7 @@
A [repository](https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository)
is what you use to store your codebase in GitLab and change it with version control.
-A repository is part of a project, which has a lot of other features.
+A repository is part of a [project](../index.md), which has a lot of other features.
## Create a repository
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index f5d84ea8762..13e75b256a7 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -4,12 +4,28 @@ module Gitlab
def initialize(repository)
@repository = repository
@gitaly_repo = repository.gitaly_repository
+ @storage = repository.storage
end
def exists?
request = Gitaly::RepositoryExistsRequest.new(repository: @gitaly_repo)
- GitalyClient.call(@repository.storage, :repository_service, :exists, request).exists
+ GitalyClient.call(@storage, :repository_service, :exists, request).exists
+ end
+
+ def garbage_collect(create_bitmap)
+ request = Gitaly::GarbageCollectRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap)
+ GitalyClient.call(@storage, :repository_service, :garbage_collect, request)
+ end
+
+ def repack_full(create_bitmap)
+ request = Gitaly::RepackFullRequest.new(repository: @gitaly_repo, create_bitmap: create_bitmap)
+ GitalyClient.call(@storage, :repository_service, :repack_full, request)
+ end
+
+ def repack_incremental
+ request = Gitaly::RepackIncrementalRequest.new(repository: @gitaly_repo)
+ GitalyClient.call(@storage, :repository_service, :repack_incremental, request)
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 97ffc54415c..034682dae27 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -32,11 +32,13 @@ describe 'Admin > Users > Impersonation Tokens', js: true do
check "api"
check "read_user"
- expect { click_on "Create impersonation token" }.to change { PersonalAccessTokensFinder.new(impersonation: true).execute.count }
+ click_on "Create impersonation token"
+
expect(active_impersonation_tokens).to have_text(name)
expect(active_impersonation_tokens).to have_text('In')
expect(active_impersonation_tokens).to have_text('api')
expect(active_impersonation_tokens).to have_text('read_user')
+ expect(PersonalAccessTokensFinder.new(impersonation: true).execute.count).to equal(1)
end
end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index 309b3172da1..05f971dfd13 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -9,17 +9,51 @@ describe GitGarbageCollectWorker do
subject { described_class.new }
describe "#perform" do
- it "flushes ref caches when the task is 'gc'" do
- expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
- expect(Gitlab::Popen).to receive(:popen)
- .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
+ shared_examples 'flushing ref caches' do |gitaly|
+ it "flushes ref caches when the task if 'gc'" do
+ expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
+
+ if gitaly
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
+ .and_return(nil)
+ else
+ expect(Gitlab::Popen).to receive(:popen)
+ .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
+ end
+
+ expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
+ expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
+ expect_any_instance_of(Repository).to receive(:branch_count).and_call_original
+ expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
+
+ subject.perform(project.id)
+ end
+ end
+
+ context "with Gitaly turned on" do
+ it_should_behave_like 'flushing ref caches', true
+ end
+
+ context "with Gitaly turned off", skip_gitaly_mock: true do
+ it_should_behave_like 'flushing ref caches', false
+ end
- expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
- expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
- expect_any_instance_of(Repository).to receive(:branch_count).and_call_original
- expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
+ context "repack_full" do
+ it "calls Gitaly" do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:repack_full)
+ .and_return(nil)
- subject.perform(project.id)
+ subject.perform(project.id, :full_repack)
+ end
+ end
+
+ context "repack_incremental" do
+ it "calls Gitaly" do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:repack_incremental)
+ .and_return(nil)
+
+ subject.perform(project.id, :incremental_repack)
+ end
end
shared_examples 'gc tasks' do