Commit 1644c1ac authored by Simon Knox's avatar Simon Knox

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into dispatcher-projects-c

parents c6884cfe 9e70ff34
...@@ -403,7 +403,7 @@ group :ed25519 do ...@@ -403,7 +403,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.69.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.73.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -284,7 +284,7 @@ GEM ...@@ -284,7 +284,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.69.0) gitaly-proto (0.73.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -1054,7 +1054,7 @@ DEPENDENCIES ...@@ -1054,7 +1054,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.69.0) gitaly-proto (~> 0.73.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2) gitlab-markup (~> 1.6.2)
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
import projectSelect from './project_select'; import projectSelect from './project_select';
import IssuableIndex from './issuable_index';
import Milestone from './milestone'; import Milestone from './milestone';
import IssuableForm from './issuable_form'; import IssuableForm from './issuable_form';
import LabelsSelect from './labels_select'; import LabelsSelect from './labels_select';
import MilestoneSelect from './milestone_select'; import MilestoneSelect from './milestone_select';
import NewBranchForm from './new_branch_form';
import NotificationsForm from './notifications_form'; import NotificationsForm from './notifications_form';
import notificationsDropdown from './notifications_dropdown'; import notificationsDropdown from './notifications_dropdown';
import groupAvatar from './group_avatar'; import groupAvatar from './group_avatar';
import GroupLabelSubscription from './group_label_subscription'; import GroupLabelSubscription from './group_label_subscription';
import LineHighlighter from './line_highlighter'; import LineHighlighter from './line_highlighter';
import NewCommitForm from './new_commit_form';
import Project from './project'; import Project from './project';
import projectAvatar from './project_avatar'; import projectAvatar from './project_avatar';
import MergeRequest from './merge_request'; import MergeRequest from './merge_request';
import Compare from './compare'; import Compare from './compare';
import initCompareAutocomplete from './compare_autocomplete'; import initCompareAutocomplete from './compare_autocomplete';
import ProjectFindFile from './project_find_file';
import ProjectNew from './project_new'; import ProjectNew from './project_new';
import projectImport from './project_import';
import Labels from './labels'; import Labels from './labels';
import LabelManager from './label_manager'; import LabelManager from './label_manager';
import Sidebar from './right_sidebar'; import Sidebar from './right_sidebar';
import IssuableTemplateSelectors from './templates/issuable_template_selectors'; import IssuableTemplateSelectors from './templates/issuable_template_selectors';
import Flash from './flash'; import Flash from './flash';
import Issue from './issue';
import BindInOut from './behaviors/bind_in_out'; import BindInOut from './behaviors/bind_in_out';
import SecretValues from './behaviors/secret_values'; import SecretValues from './behaviors/secret_values';
import Group from './group'; import Group from './group';
import ProjectsList from './projects_list'; import ProjectsList from './projects_list';
import setupProjectEdit from './project_edit';
import UserCallout from './user_callout'; import UserCallout from './user_callout';
import ShortcutsWiki from './shortcuts_wiki'; import ShortcutsWiki from './shortcuts_wiki';
import BlobViewer from './blob/viewer/index'; import BlobViewer from './blob/viewer/index';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
import UsersSelect from './users_select'; import UsersSelect from './users_select';
import RefSelectDropdown from './ref_select_dropdown';
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
import Star from './star'; import Star from './star';
import TreeView from './tree'; import TreeView from './tree';
...@@ -53,7 +45,6 @@ import GlFieldErrors from './gl_field_errors'; ...@@ -53,7 +45,6 @@ import GlFieldErrors from './gl_field_errors';
import GLForm from './gl_form'; import GLForm from './gl_form';
import Shortcuts from './shortcuts'; import Shortcuts from './shortcuts';
import ShortcutsNavigation from './shortcuts_navigation'; import ShortcutsNavigation from './shortcuts_navigation';
import ShortcutsFindFile from './shortcuts_find_file';
import ShortcutsIssuable from './shortcuts_issuable'; import ShortcutsIssuable from './shortcuts_issuable';
import U2FAuthenticate from './u2f/authenticate'; import U2FAuthenticate from './u2f/authenticate';
import Members from './members'; import Members from './members';
...@@ -116,22 +107,22 @@ import Activities from './activities'; ...@@ -116,22 +107,22 @@ import Activities from './activities';
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:merge_requests:index': case 'projects:merge_requests:index':
import('./pages/projects/merge_requests/index')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:issues:index': case 'projects:issues:index':
if (filteredSearchEnabled) { import('./pages/projects/issues/index')
const filteredSearchManager = new gl.FilteredSearchManager(page === 'projects:issues:index' ? 'issues' : 'merge_requests'); .then(callDefault)
filteredSearchManager.setup(); .catch(fail);
} shortcut_handler = true;
const pagePrefix = page === 'projects:merge_requests:index' ? 'merge_request_' : 'issue_';
new IssuableIndex(pagePrefix);
shortcut_handler = new ShortcutsNavigation();
new UsersSelect();
break; break;
case 'projects:issues:show': case 'projects:issues:show':
new Issue(); import('./pages/projects/issues/show')
shortcut_handler = new ShortcutsIssuable(); .then(callDefault)
new ZenMode(); .catch(fail);
initIssuableSidebar(); shortcut_handler = true;
break; break;
case 'dashboard:milestones:index': case 'dashboard:milestones:index':
import('./pages/dashboard/milestones/index') import('./pages/dashboard/milestones/index')
...@@ -207,8 +198,14 @@ import Activities from './activities'; ...@@ -207,8 +198,14 @@ import Activities from './activities';
.catch(fail); .catch(fail);
break; break;
case 'projects:branches:new': case 'projects:branches:new':
import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break;
case 'projects:branches:create': case 'projects:branches:create':
new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML)); import('./pages/projects/branches/new')
.then(callDefault)
.catch(fail);
break; break;
case 'projects:branches:index': case 'projects:branches:index':
import('./pages/projects/branches/index') import('./pages/projects/branches/index')
...@@ -216,13 +213,16 @@ import Activities from './activities'; ...@@ -216,13 +213,16 @@ import Activities from './activities';
.catch(fail); .catch(fail);
break; break;
case 'projects:issues:new': case 'projects:issues:new':
import('./pages/projects/issues/new')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
case 'projects:issues:edit': case 'projects:issues:edit':
shortcut_handler = new ShortcutsNavigation(); import('./pages/projects/issues/edit')
new GLForm($('.issue-form'), true); .then(callDefault)
new IssuableForm($('.issue-form')); .catch(fail);
new LabelsSelect(); shortcut_handler = true;
new MilestoneSelect();
new IssuableTemplateSelectors();
break; break;
case 'projects:merge_requests:creations:new': case 'projects:merge_requests:creations:new':
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare'); const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
...@@ -250,9 +250,9 @@ import Activities from './activities'; ...@@ -250,9 +250,9 @@ import Activities from './activities';
new AutoWidthDropdownSelect($('.js-target-branch-select')).init(); new AutoWidthDropdownSelect($('.js-target-branch-select')).init();
break; break;
case 'projects:tags:new': case 'projects:tags:new':
new ZenMode(); import('./pages/projects/tags/new')
new GLForm($('.tag-form'), true); .then(callDefault)
new RefSelectDropdown($('.js-branch-select')); .catch(fail);
break; break;
case 'projects:snippets:show': case 'projects:snippets:show':
initNotes(); initNotes();
...@@ -266,11 +266,24 @@ import Activities from './activities'; ...@@ -266,11 +266,24 @@ import Activities from './activities';
new ZenMode(); new ZenMode();
break; break;
case 'snippets:new': case 'snippets:new':
import('./pages/snippets/new')
.then(callDefault)
.catch(fail);
break;
case 'snippets:edit': case 'snippets:edit':
import('./pages/snippets/edit')
.then(callDefault)
.catch(fail);
break;
case 'snippets:create': case 'snippets:create':
import('./pages/snippets/new')
.then(callDefault)
.catch(fail);
break;
case 'snippets:update': case 'snippets:update':
new GLForm($('.snippet-form'), false); import('./pages/snippets/edit')
new ZenMode(); .then(callDefault)
.catch(fail);
break; break;
case 'projects:releases:edit': case 'projects:releases:edit':
new ZenMode(); new ZenMode();
...@@ -334,12 +347,14 @@ import Activities from './activities'; ...@@ -334,12 +347,14 @@ import Activities from './activities';
}); });
break; break;
case 'projects:edit': case 'projects:edit':
setupProjectEdit(); import('./pages/projects/edit')
// Initialize expandable settings panels .then(callDefault)
initSettingsPanels(); .catch(fail);
break; break;
case 'projects:imports:show': case 'projects:imports:show':
projectImport(); import('./pages/projects/imports/show')
.then(callDefault)
.catch(fail);
break; break;
case 'projects:pipelines:new': case 'projects:pipelines:new':
case 'projects:pipelines:create': case 'projects:pipelines:create':
...@@ -399,22 +414,15 @@ import Activities from './activities'; ...@@ -399,22 +414,15 @@ import Activities from './activities';
groupAvatar(); groupAvatar();
break; break;
case 'projects:tree:show': case 'projects:tree:show':
shortcut_handler = new ShortcutsNavigation(); import('./pages/projects/tree/show')
new TreeView(); .then(callDefault)
new BlobViewer(); .catch(fail);
new NewCommitForm($('.js-create-dir-form')); shortcut_handler = true;
$('#tree-slider').waitForImages(function() {
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
break; break;
case 'projects:find_file:show': case 'projects:find_file:show':
const findElement = document.querySelector('.js-file-finder'); import('./pages/projects/find_file/show')
const projectFindFile = new ProjectFindFile($(".file-finder-holder"), { .then(callDefault)
url: findElement.dataset.fileFindUrl, .catch(fail);
treeUrl: findElement.dataset.findTreeUrl,
blobUrlTemplate: findElement.dataset.blobUrlTemplate,
});
new ShortcutsFindFile(projectFindFile);
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:blob:show': case 'projects:blob:show':
...@@ -468,7 +476,7 @@ import Activities from './activities'; ...@@ -468,7 +476,7 @@ import Activities from './activities';
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:forks:new': case 'projects:forks:new':
import(/* webpackChunkName: 'project_fork' */ './project_fork') import('./pages/projects/forks/new')
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
...@@ -525,7 +533,9 @@ import Activities from './activities'; ...@@ -525,7 +533,9 @@ import Activities from './activities';
import('./pages/admin/conversational_development_index/show').then(m => m.default()).catch(fail); import('./pages/admin/conversational_development_index/show').then(m => m.default()).catch(fail);
break; break;
case 'snippets:show': case 'snippets:show':
import('./pages/snippets/show').then(m => m.default()).catch(fail); import('./pages/snippets/show')
.then(callDefault)
.catch(fail);
break; break;
case 'import:fogbugz:new_user_map': case 'import:fogbugz:new_user_map':
import('./pages/import/fogbugz/new_user_map').then(m => m.default()).catch(fail); import('./pages/import/fogbugz/new_user_map').then(m => m.default()).catch(fail);
......
import NewBranchForm from '~/new_branch_form';
export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML));
import initSettingsPanels from '~/settings_panels';
import setupProjectEdit from '~/project_edit';
export default () => {
setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
};
import ProjectFindFile from '~/project_find_file';
import ShortcutsFindFile from '~/shortcuts_find_file';
export default () => {
const findElement = document.querySelector('.js-file-finder');
const projectFindFile = new ProjectFindFile($('.file-finder-holder'), {
url: findElement.dataset.fileFindUrl,
treeUrl: findElement.dataset.findTreeUrl,
blobUrlTemplate: findElement.dataset.blobUrlTemplate,
});
new ShortcutsFindFile(projectFindFile); // eslint-disable-line no-new
};
import ProjectFork from '~/project_fork';
export default () => {
new ProjectFork(); // eslint-disable-line no-new
};
import ProjectImport from '~/project_import';
export default () => {
new ProjectImport(); // eslint-disable-line no-new
};
import initForm from '../form';
export default () => {
initForm();
};
/* eslint-disable no-new */
import GLForm from '~/gl_form';
import IssuableForm from '~/issuable_form';
import LabelsSelect from '~/labels_select';
import MilestoneSelect from '~/milestone_select';
import ShortcutsNavigation from '~/shortcuts_navigation';
import IssuableTemplateSelectors from '~/templates/issuable_template_selectors';
export default () => {
new ShortcutsNavigation();
new GLForm($('.issue-form'), true);
new IssuableForm($('.issue-form'));
new LabelsSelect();
new MilestoneSelect();
new IssuableTemplateSelectors();
};
/* eslint-disable no-new */
import IssuableIndex from '~/issuable_index';
import ShortcutsNavigation from '~/shortcuts_navigation';
import UsersSelect from '~/users_select';
export default () => {
const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
if (filteredSearchEnabled) {
const filteredSearchManager = new gl.FilteredSearchManager('issues');
filteredSearchManager.setup();
}
new IssuableIndex('issue_');
new ShortcutsNavigation();
new UsersSelect();
};
import initForm from '../form';
export default () => {
initForm();
};
/* eslint-disable no-new */
import initIssuableSidebar from '~/init_issuable_sidebar';
import Issue from '~/issue';
import ShortcutsIssuable from '~/shortcuts_issuable';
import ZenMode from '~/zen_mode';
export default () => {
new Issue();
new ShortcutsIssuable();
new ZenMode();
initIssuableSidebar();
};
import IssuableIndex from '~/issuable_index';
import ShortcutsNavigation from '~/shortcuts_navigation';
import UsersSelect from '~/users_select';
export default () => {
const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
if (filteredSearchEnabled) {
const filteredSearchManager = new gl.FilteredSearchManager('merge_requests');
filteredSearchManager.setup();
}
new IssuableIndex('merge_request_'); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
new UsersSelect(); // eslint-disable-line no-new
};
import RefSelectDropdown from '../../../../ref_select_dropdown';
import ZenMode from '../../../../zen_mode';
import GLForm from '../../../../gl_form';
export default () => {
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.tag-form'), true); // eslint-disable-line no-new
new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new
};
import TreeView from '../../../../tree';
import ShortcutsNavigation from '../../../../shortcuts_navigation';
import BlobViewer from '../../../../blob/viewer';
import NewCommitForm from '../../../../new_commit_form';
import { ajaxGet } from '../../../../lib/utils/common_utils';
export default () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new TreeView(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
new NewCommitForm($('.js-create-dir-form')); // eslint-disable-line no-new
$('#tree-slider').waitForImages(() =>
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
};
import form from '../form';
export default form;
import GLForm from '~/gl_form';
import ZenMode from '~/zen_mode';
export default () => {
new GLForm($('.snippet-form'), false); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
};
import form from '../form';
export default form;
<script>
import tooltip from '~/vue_shared/directives/tooltip';
export default {
directives: {
tooltip,
},
props: {
cssClass: {
type: String,
required: false,
default: '',
},
successLabel: {
type: String,
required: true,
},
failureLabel: {
type: String,
required: true,
},
neutralLabel: {
type: String,
required: true,
},
successCount: {
type: Number,
required: true,
},
failureCount: {
type: Number,
required: true,
},
totalCount: {
type: Number,
required: true,
},
},
computed: {
neutralCount() {
return this.totalCount - this.successCount - this.failureCount;
},
successPercent() {
return this.getPercent(this.successCount);
},
successBarStyle() {
return this.barStyle(this.successPercent);
},
successTooltip() {
return this.getTooltip(this.successLabel, this.successCount);
},
failurePercent() {
return this.getPercent(this.failureCount);
},
failureBarStyle() {
return this.barStyle(this.failurePercent);
},
failureTooltip() {
return this.getTooltip(this.failureLabel, this.failureCount);
},
neutralPercent() {
return this.getPercent(this.neutralCount);
},
neutralBarStyle() {
return this.barStyle(this.neutralPercent);
},
neutralTooltip() {
return this.getTooltip(this.neutralLabel, this.neutralCount);
},
},
methods: {
getPercent(count) {
return Math.ceil((count / this.totalCount) * 100);
},
barStyle(percent) {
return `width: ${percent}%;`;
},
getTooltip(label, count) {
return `${label}: ${count}`;
},
},
};
</script>
<template>
<div
class="stacked-progress-bar"
:class="cssClass"
>
<span
v-if="!totalCount"
class="status-unavailable"
>
{{ __("Not available") }}
</span>
<span
v-tooltip
v-if="successPercent"
class="status-green"
data-placement="bottom"
:title="successTooltip"
:style="successBarStyle"
>
{{ successPercent }}%
</span>
<span
v-tooltip
v-if="neutralPercent"
class="status-neutral"
data-placement="bottom"
:title="neutralTooltip"
:style="neutralBarStyle"
>
{{ neutralPercent }}%
</span>
<span
v-tooltip
v-if="failurePercent"
class="status-red"
data-placement="bottom"
:title="failureTooltip"
:style="failureBarStyle"
>
{{ failurePercent }}%
</span>
</div>
</template>
...@@ -59,3 +59,4 @@ ...@@ -59,3 +59,4 @@
@import "framework/snippets"; @import "framework/snippets";
@import "framework/memory_graph"; @import "framework/memory_graph";
@import "framework/responsive_tables"; @import "framework/responsive_tables";
@import "framework/stacked-progress-bar";
.stacked-progress-bar {
display: flex;
height: 16px;
border-radius: 10px;
overflow: hidden;
background-color: $theme-gray-100;
.status-unavailable,
.status-green,
.status-neutral,
.status-red, {
height: 100%;
min-width: 25px;
padding: 0 5px;
font-size: $tooltip-font-size;
font-weight: normal;
color: $white-light;
line-height: 16px;
&:hover {
cursor: pointer;
}
}
.status-unavailable {
padding: 0 10px;
color: $theme-gray-700;
}
.status-green {
background-color: $green-500;
&:hover {
background-color: $green-600;
}
}
.status-neutral {
background-color: $theme-gray-200;
color: $gl-gray-dark;
&:hover {
background-color: $theme-gray-300;
}
}
.status-red {
background-color: $red-500;
&:hover {
background-color: $red-600;
}
}
}
...@@ -3,6 +3,7 @@ module ShaAttribute ...@@ -3,6 +3,7 @@ module ShaAttribute
module ClassMethods module ClassMethods
def sha_attribute(name) def sha_attribute(name)
return if ENV['STATIC_VERIFICATION']
return unless table_exists? return unless table_exists?
column = columns.find { |c| c.name == name.to_s } column = columns.find { |c| c.name == name.to_s }
......
...@@ -831,13 +831,12 @@ class Repository ...@@ -831,13 +831,12 @@ class Repository
end end
def can_be_merged?(source_sha, target_branch) def can_be_merged?(source_sha, target_branch)
our_commit = rugged.branches[target_branch].target raw_repository.gitaly_migrate(:can_be_merged) do |is_enabled|
their_commit = rugged.lookup(source_sha) if is_enabled
gitaly_can_be_merged?(source_sha, find_branch(target_branch).target)
if our_commit && their_commit else
!rugged.merge_commits(our_commit, their_commit).conflicts? rugged_can_be_merged?(source_sha, target_branch)
else end
false
end end
end end
...@@ -1132,6 +1131,14 @@ class Repository ...@@ -1132,6 +1131,14 @@ class Repository
Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki)) Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki))
end end
def gitaly_can_be_merged?(their_commit, our_commit)
!raw_repository.gitaly_conflicts_client(our_commit, their_commit).conflicts?
end
def rugged_can_be_merged?(their_commit, our_commit)
!rugged.merge_commits(our_commit, their_commit).conflicts?
end
def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset) def find_commits_by_message_by_shelling_out(query, ref, path, limit, offset)
ref ||= root_ref ref ||= root_ref
......
...@@ -53,7 +53,10 @@ class User < ActiveRecord::Base ...@@ -53,7 +53,10 @@ class User < ActiveRecord::Base
serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize
devise :lockable, :recoverable, :rememberable, :trackable, devise :lockable, :recoverable, :rememberable, :trackable,
:validatable, :omniauthable, :confirmable, :registerable :validatable, :omniauthable, :confirmable, :registerable
BLOCKED_MESSAGE = "Your account has been blocked. Please contact your GitLab " \
"administrator if you think this is an error.".freeze
# Override Devise::Models::Trackable#update_tracked_fields! # Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour # to limit database writes to at most once every hour
...@@ -217,8 +220,7 @@ class User < ActiveRecord::Base ...@@ -217,8 +220,7 @@ class User < ActiveRecord::Base
end end
def inactive_message def inactive_message
"Your account has been blocked. Please contact your GitLab " \ BLOCKED_MESSAGE
"administrator if you think this is an error."
end end
end end
end end
......
...@@ -41,8 +41,11 @@ class SystemHooksService ...@@ -41,8 +41,11 @@ class SystemHooksService
when User when User
data.merge!(user_data(model)) data.merge!(user_data(model))
if event == :rename case event
when :rename
data[:old_username] = model.username_was data[:old_username] = model.username_was
when :failed_login
data[:state] = model.state
end end
when ProjectMember when ProjectMember
data.merge!(project_member_data(model)) data.merge!(project_member_data(model))
......
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute - issues_count = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute.count
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute - merge_requests_count = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute.count
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll .nav-sidebar-inner-scroll
...@@ -39,14 +39,14 @@ ...@@ -39,14 +39,14 @@
= sprite_icon('issues') = sprite_icon('issues')
%span.nav-item-name %span.nav-item-name
Issues Issues
%span.badge.count= number_with_delimiter(issues.count) %span.badge.count= number_with_delimiter(issues_count)
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
= link_to issues_group_path(@group) do = link_to issues_group_path(@group) do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
#{ _('Issues') } #{ _('Issues') }
%span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues.count) %span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count)
%li.divider.fly-out-top-item %li.divider.fly-out-top-item
= nav_link(path: 'groups#issues', html_options: { class: 'home' }) do = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
= link_to issues_group_path(@group), title: 'List' do = link_to issues_group_path(@group), title: 'List' do
...@@ -69,13 +69,13 @@ ...@@ -69,13 +69,13 @@
= sprite_icon('git-merge') = sprite_icon('git-merge')
%span.nav-item-name %span.nav-item-name
Merge Requests Merge Requests
%span.badge.count= number_with_delimiter(merge_requests.count) %span.badge.count= number_with_delimiter(merge_requests_count)
%ul.sidebar-sub-level-items.is-fly-out-only %ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
= link_to merge_requests_group_path(@group) do = link_to merge_requests_group_path(@group) do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
#{ _('Merge Requests') } #{ _('Merge Requests') }
%span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests.count) %span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests_count)
= nav_link(path: 'group_members#index') do = nav_link(path: 'group_members#index') do
= link_to group_group_members_path(@group) do = link_to group_group_members_path(@group) do
.nav-icon-container .nav-icon-container
......
- illustration = local_assigns.fetch(:illustration) - illustration = local_assigns.fetch(:illustration)
- illustration_size = local_assigns.fetch(:illustration_size) - illustration_size = local_assigns.fetch(:illustration_size)
- title = local_assigns.fetch(:title) - title = local_assigns.fetch(:title)
- content = local_assigns.fetch(:content) - content = local_assigns.fetch(:content, nil)
- action = local_assigns.fetch(:action, nil) - action = local_assigns.fetch(:action, nil)
.row.empty-state .row.empty-state
...@@ -11,7 +11,8 @@ ...@@ -11,7 +11,8 @@
.col-xs-12 .col-xs-12
.text-content .text-content
%h4.text-center= title %h4.text-center= title
%p= content - if content
%p= content
- if action - if action
.text-center .text-center
= action = action
...@@ -93,14 +93,13 @@ ...@@ -93,14 +93,13 @@
illustration: 'illustrations/manual_action.svg', illustration: 'illustrations/manual_action.svg',
illustration_size: 'svg-394', illustration_size: 'svg-394',
title: _('This job requires a manual action'), title: _('This job requires a manual action'),
content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.'), content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'),
action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), class: 'btn btn-primary', title: _('Trigger this manual action') ) action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') )
- else - else
= render 'empty_state', = render 'empty_state',
illustration: 'illustrations/job_not_triggered.svg', illustration: 'illustrations/job_not_triggered.svg',
illustration_size: 'svg-306', illustration_size: 'svg-306',
title: _('This job has not been triggered yet'), title: _('This job has not been triggered yet')
content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered.')
= render "sidebar" = render "sidebar"
......
...@@ -17,10 +17,7 @@ class RepositoryForkWorker ...@@ -17,10 +17,7 @@ class RepositoryForkWorker
project.repository_storage_path, project.disk_path) project.repository_storage_path, project.disk_path)
raise "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result raise "Unable to fork project #{project_id} for repository #{source_disk_path} -> #{project.disk_path}" unless result
project.repository.after_import project.after_import
raise "Project #{project_id} had an invalid repository after fork" unless project.valid_repo?
project.import_finish
end end
private private
......
---
title: Makes forking protect default branch on completion
merge_request:
author:
type: fixed
---
title: 'Closes #38540 - Remove .ssh/environment file that now breaks the gitlab:check
rake task'
merge_request:
author:
type: fixed
---
title: Fix double query execution on groups page
merge_request: 16314
author:
type: performance
---
title: "[API] Fix creating issue when assignee_id is empty"
merge_request:
author:
type: fixed
---
title: Fixing rack request mime type when using rack attack
merge_request: 16427
author:
type: fixed
---
title: Log and send a system hook if a blocked user attempts to login
merge_request:
author:
type: added
...@@ -2,4 +2,8 @@ Rails.application.configure do |config| ...@@ -2,4 +2,8 @@ Rails.application.configure do |config|
Warden::Manager.after_set_user do |user, auth, opts| Warden::Manager.after_set_user do |user, auth, opts|
Gitlab::Auth::UniqueIpsLimiter.limit_user!(user) Gitlab::Auth::UniqueIpsLimiter.limit_user!(user)
end end
Warden::Manager.before_failure do |env, opts|
Gitlab::Auth::BlockedUserTracker.log_if_user_blocked(env)
end
end end
# Technical Articles # Technical articles list (deprecated)
[Technical Articles](../development/writing_documentation.md#technical-articles) are [Technical articles](../development/writing_documentation.md#technical-articles) are
topic-related documentation, written with an user-friendly approach and language, aiming topic-related documentation, written with an user-friendly approach and language, aiming
to provide the community with guidance on specific processes to achieve certain objectives. to provide the community with guidance on specific processes to achieve certain objectives.
They are written by members of the GitLab Team and by The list of technical articles was [deprecated](https://gitlab.com/gitlab-org/gitlab-ce/issues/41138) in favor of having them linked from their topic-related documentation:
[Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/).
Part of the articles listed below link to the [GitLab Blog](https://about.gitlab.com/blog/), - [Git](../topics/git/index.md)
where they were originally published. - [GitLab administrator](../administration/index.md)
- [GitLab CI/CD](../ci/README.md)
## GitLab Pages - [GitLab Pages](../user/project/pages/index.md)
- [Install GitLab](../install/README.md)
Learn how to deploy a static website with [GitLab Pages](../user/project/pages/index.md#getting-started):
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
| **Series: GitLab Pages from A to Z:** |
| [- Part 1: Static sites and GitLab Pages domains](../user/project/pages/getting_started_part_one.md)| User guide | 2017-02-22 |
| [- Part 2: Quick start guide - Setting up GitLab Pages](../user/project/pages/getting_started_part_two.md)| User guide | 2017-02-22 |
| [- Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](../user/project/pages/getting_started_part_three.md)| User guide | 2017-02-22 |
| [- Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](../user/project/pages/getting_started_part_four.md)| User guide | 2017-02-22 |
| [Setting up GitLab Pages with CloudFlare Certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Tutorial | 2017-02-07 |
| [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/) | Tutorial | 2016-12-07 |
| [Publish Code Coverage Report with GitLab Pages](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/) | Tutorial | 2016-11-03 |
| [GitLab CI: Deployment & Environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) | Tutorial | 2016-08-26 |
| [Posting to your GitLab Pages blog from iOS](https://about.gitlab.com/2016/08/19/posting-to-your-gitlab-pages-blog-from-ios/) | Tutorial | 2016-08-19 |
| **Series: Static Site Generator:** |
| [- Part 1: Dynamic vs Static Websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) | Tutorial | 2016-06-03 |
| [- Part 2: Modern Static Site Generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) | Tutorial | 2016-06-10 |
| [- Part 3: Build any SSG site with GitLab Pages](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/) | Tutorial | 2016-06-17 |
| [Securing your GitLab Pages with TLS and Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) | Tutorial | 2016-04-11 |
| [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) | Tutorial | 2016-04-07 |
## Install and maintain GitLab
[Admin](../README.md#administrator-documentation), [install](../install/README.md),
upgrade, integrate, migrate to GitLab:
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
| [Video Tutorial: Idea to Production on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/) | Tutorial | 2017-01-23 |
| [How to Setup a GitLab Instance on Microsoft Azure](https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/) | Tutorial | 2016-07-13 |
| [Getting started with GitLab and DigitalOcean](https://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/) | Tutorial | 2016-04-27 |
## Software development
Explore the best of GitLab's software development's capabilities:
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
| [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) | Concepts | 2017-07-13 |
| [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/)| Concepts | 2017-06-29 |
| [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/) | Concepts | 2017-05-22 |
| [Demo: Auto-Deploy from GitLab to an OpenShift Container Cluster](https://about.gitlab.com/2017/05/16/devops-containers-gitlab-openshift/) | Technical overview | 2017-05-16 |
| [Demo: GitLab Service Desk](https://about.gitlab.com/2017/05/09/demo-service-desk/) | Feature highlight | 2017-05-09 |
| [Demo: Mapping Work Versus Time, With Burndown Charts](https://about.gitlab.com/2017/04/25/mapping-work-to-do-versus-time-with-burndown-charts/) | Feature highlight | 2017-04-25 |
| [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/) | Feature highlight | 2017-04-18 |
| [Demo: Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/) | Feature highlight | 2017-03-17 |
| [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/) | Technical overview | 2016-11-14 |
| [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/) | Technical overview | 2016-10-25 |
| [Trends in Version Control Land: Microservices](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/) | Concepts | 2016-08-16 |
| [Continuous Integration, Delivery, and Deployment with GitLab](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) | Concepts | 2016-08-05 |
| [Trends in Version Control Land: Innersourcing](https://about.gitlab.com/2016/07/07/trends-version-control-innersourcing/) | Concepts | 2016-07-07 |
| [Tutorial: It's all connected in GitLab](https://about.gitlab.com/2016/03/08/gitlab-tutorial-its-all-connected/) | Technical overview | 2016-03-08 |
## Technologies
| Article title | Category | Publishing date |
| :------------ | :------: | --------------: |
| [Why we are not leaving the cloud](https://about.gitlab.com/2017/03/02/why-we-are-not-leaving-the-cloud/) | Concepts | 2017-03-02 |
| [Why We Chose Vue.js](https://about.gitlab.com/2016/10/20/why-we-chose-vue/) | Concepts | 2016-10-20 |
| [Markdown Kramdown Tips & Tricks](https://about.gitlab.com/2016/07/19/markdown-kramdown-tips-and-tricks/) | Technical overview | 2016-07-19 |
...@@ -28,6 +28,7 @@ you don't need to set up anything to start to use them with GitLab CI/CD. ...@@ -28,6 +28,7 @@ you don't need to set up anything to start to use them with GitLab CI/CD.
- Article (2016-08-05): [Continuous Integration, Delivery, and Deployment with GitLab - Intro to CI/CD](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/) - Article (2016-08-05): [Continuous Integration, Delivery, and Deployment with GitLab - Intro to CI/CD](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)
- Article (2015-12-14): [Getting started with GitLab and GitLab CI - Intro to CI](https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/) - Article (2015-12-14): [Getting started with GitLab and GitLab CI - Intro to CI](https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/)
- Article (2017-07-13): [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/) - Article (2017-07-13): [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/)
- Article (2017-05-22): [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/)
- **Videos:** - **Videos:**
- Demo (Streamed live on Jul 17, 2017): [GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195) - Demo (Streamed live on Jul 17, 2017): [GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195)
- Demo (March, 2017): [How to get started using CI/CD with GitLab](https://about.gitlab.com/2017/03/13/ci-cd-demo/) - Demo (March, 2017): [How to get started using CI/CD with GitLab](https://about.gitlab.com/2017/03/13/ci-cd-demo/)
...@@ -93,17 +94,6 @@ Leverage the power of Docker to run your CI pipelines. ...@@ -93,17 +94,6 @@ Leverage the power of Docker to run your CI pipelines.
See the documentation on [GitLab Pages](../user/project/pages/index.md). See the documentation on [GitLab Pages](../user/project/pages/index.md).
## Special configuration (GitLab admin)
As a GitLab administrator, you can change the default behavior of GitLab CI/CD in
your whole GitLab instance as well as in each project.
- **Project specific:**
- [Pipelines settings](../user/project/pipelines/settings.md)
- [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
- **Affecting the whole GitLab instance:**
- [Continuous Integration admin settings](../user/admin_area/settings/continuous_integration.md)
## Examples ## Examples
Check the [GitLab CI/CD examples](examples/README.md) for a collection of tutorials and guides on setting up your CI/CD pipeline for various programming languages, frameworks, Check the [GitLab CI/CD examples](examples/README.md) for a collection of tutorials and guides on setting up your CI/CD pipeline for various programming languages, frameworks,
...@@ -115,6 +105,18 @@ and operating systems. ...@@ -115,6 +105,18 @@ and operating systems.
- Article (2016-05-05): [Getting Started with GitLab and Shippable Continuous Integration](https://about.gitlab.com/2016/05/05/getting-started-gitlab-and-shippable/) - Article (2016-05-05): [Getting Started with GitLab and Shippable Continuous Integration](https://about.gitlab.com/2016/05/05/getting-started-gitlab-and-shippable/)
- Article (2016-04-19): [GitLab Partners with DigitalOcean to make Continuous Integration faster, safer, and more affordable](https://about.gitlab.com/2016/04/19/gitlab-partners-with-digitalocean-to-make-continuous-integration-faster-safer-and-more-affordable/) - Article (2016-04-19): [GitLab Partners with DigitalOcean to make Continuous Integration faster, safer, and more affordable](https://about.gitlab.com/2016/04/19/gitlab-partners-with-digitalocean-to-make-continuous-integration-faster-safer-and-more-affordable/)
## Special configuration (GitLab admin)
As a GitLab administrator, you can change the default behavior of GitLab CI/CD in
your whole GitLab instance as well as in each project.
- [Continuous Integration admin settings](../administration/index.md#continuous-integration-settings)
- **Project specific:**
- [Pipelines settings](../user/project/pipelines/settings.md)
- [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
- **Affecting the whole GitLab instance:**
- [Continuous Integration admin settings](../user/admin_area/settings/continuous_integration.md)
## Breaking changes ## Breaking changes
- [CI variables renaming for GitLab 9.0](variables/README.md#9-0-renaming) Read about the - [CI variables renaming for GitLab 9.0](variables/README.md#9-0-renaming) Read about the
......
...@@ -37,6 +37,8 @@ during the deployment. ...@@ -37,6 +37,8 @@ during the deployment.
We made a [simple guide](quick_start_guide.md) to using Auto Deploy with GitLab.com. We made a [simple guide](quick_start_guide.md) to using Auto Deploy with GitLab.com.
For a demonstration of GitLab Auto Deploy, read the blog post [Auto Deploy from GitLab to an OpenShift Container Cluster](https://about.gitlab.com/2017/05/16/devops-containers-gitlab-openshift/)
## Supported templates ## Supported templates
The list of supported auto deploy templates is available in the The list of supported auto deploy templates is available in the
......
...@@ -41,6 +41,19 @@ There's also a collection of repositories with [example projects](https://gitlab ...@@ -41,6 +41,19 @@ There's also a collection of repositories with [example projects](https://gitlab
[Analyze code quality with the Code Climate CLI](code_climate.md). [Analyze code quality with the Code Climate CLI](code_climate.md).
### Static Application Security Testing (SAST)
- **(EEU)** [Scan your code for vulnerabilities](https://docs.gitlab.com/ee/ci/examples/sast.html)
- [Scan your Docker images for vulnerabilities](sast_docker.md)
### Dynamic Application Security Testing (DAST)
Scan your app for vulnerabilities with GitLab [Dynamic Application Security Testing (DAST)](dast.md).
### Browser Performance Testing with Sitespeed.io
Analyze your [browser performance with Sitespeed.io](browser_performance.md).
### GitLab CI/CD for Review Apps ### GitLab CI/CD for Review Apps
- [Example project](https://gitlab.com/gitlab-examples/review-apps-nginx/) that shows how to use GitLab CI/CD for [Review Apps](../review_apps/index.html). - [Example project](https://gitlab.com/gitlab-examples/review-apps-nginx/) that shows how to use GitLab CI/CD for [Review Apps](../review_apps/index.html).
......
# Dynamic Application Security Testing with GitLab CI/CD
This example shows how to run
[Dynamic Application Security Testing (DAST)](https://en.wikipedia.org/wiki/Dynamic_program_analysis)
on your project's source code by using GitLab CI/CD.
DAST is using the popular open source tool
[OWASP ZAProxy](https://github.com/zaproxy/zaproxy) to perform an analysis.
All you need is a GitLab Runner with the Docker executor (the shared Runners on
GitLab.com will work fine). You can then add a new job to `.gitlab-ci.yml`,
called `dast`:
```yaml
dast:
image: owasp/zap2docker-stable
script:
- mkdir /zap/wrk/
- /zap/zap-baseline.py -J gl-dast-report.json -t https://example.com || true
- cp /zap/wrk/gl-dast-report.json .
artifacts:
paths: [gl-dast-report.json]
```
The above example will create a `dast` job in your CI pipeline and will allow
you to download and analyze the report artifact in JSON format.
TIP: **Tip:**
Starting with [GitLab Enterprise Edition Ultimate][ee] 10.4, this information will
be automatically extracted and shown right in the merge request widget. To do
so, the CI job must be named `dast` and the artifact path must be
`gl-dast-report.json`.
[Learn more on dynamic application security testing results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
[ee]: https://about.gitlab.com/gitlab-ee/
# Static Application Security Testing for Docker containers with GitLab CI/CD
You can check your Docker images (or more precisely the containers) for known
vulnerabilities by using [Clair](https://github.com/coreos/clair) and
[clair-scanner](https://github.com/arminc/clair-scanner), two open source tools
for Vulnerability Static Analysis for containers.
All you need is a GitLab Runner with the Docker executor (the shared Runners on
GitLab.com will work fine). You can then add a new job to `.gitlab-ci.yml`,
called `sast:container`:
```yaml
sast:container:
image: docker:latest
variables:
DOCKER_DRIVER: overlay2
## Define two new variables based on GitLab's CI/CD predefined variables
## https://docs.gitlab.com/ee/ci/variables/#predefined-variables-environment-variables
CI_APPLICATION_REPOSITORY: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
CI_APPLICATION_TAG: $CI_COMMIT_SHA
allow_failure: true
services:
- docker:dind
script:
- docker run -d --name db arminc/clair-db:latest
- docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.1
- apk add -U wget ca-certificates
- docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
- wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
- mv clair-scanner_linux_amd64 clair-scanner
- chmod +x clair-scanner
- touch clair-whitelist.yml
- ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
artifacts:
paths: [gl-sast-container-report.json]
```
The above example will create a `sast:container` job in your CI/CD pipeline, pull
the image from the [Container Registry](../../user/project/container_registry.md)
(whose name is defined from the two `CI_APPLICATION_` variables) and scan it
for possible vulnerabilities. The report will be saved as an artifact that you
can later download and analyze.
If you want to whitelist some specific vulnerabilities, you can do so by defining
them in a [YAML file](https://github.com/arminc/clair-scanner/blob/master/README.md#example-whitelist-yaml-file),
in our case its named `clair-whitelist.yml`.
TIP: **Tip:**
Starting with [GitLab Enterprise Edition Ultimate][ee] 10.4, this information will
be automatically extracted and shown right in the merge request widget. To do
so, the CI/CD job must be named `sast:container` and the artifact path must be
`gl-sast-container-report.json`.
[Learn more on application security testing results shown in merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/sast_docker.html).
[ee]: https://about.gitlab.com/gitlab-ee/
...@@ -93,7 +93,7 @@ be an array or a multi-line string. ...@@ -93,7 +93,7 @@ be an array or a multi-line string.
> Introduced in GitLab 8.7 and requires Gitlab Runner v1.2 > Introduced in GitLab 8.7 and requires Gitlab Runner v1.2
`after_script` is used to define the command that will be run after for all `after_script` is used to define the command that will be run after for all
jobs. This has to be an array or a multi-line string. jobs, including failed ones. This has to be an array or a multi-line string.
> **Note:** > **Note:**
The `before_script` and the main `script` are concatenated and run in a single context/container. The `before_script` and the main `script` are concatenated and run in a single context/container.
......
...@@ -87,9 +87,9 @@ still having access the class's implementation with `super`. ...@@ -87,9 +87,9 @@ still having access the class's implementation with `super`.
There are a few gotchas with it: There are a few gotchas with it:
- you should always add a `raise NotImplementedError unless defined?(super)` - you should always [`extend ::Gitlab::Utils::Override`] and use `override` to
guard clause in the "overrider" method to ensure that if the method gets guard the "overrider" method to ensure that if the method gets renamed in
renamed in CE, the EE override won't be silently forgotten. CE, the EE override won't be silently forgotten.
- when the "overrider" would add a line in the middle of the CE - when the "overrider" would add a line in the middle of the CE
implementation, you should refactor the CE method and split it in implementation, you should refactor the CE method and split it in
smaller methods. Or create a "hook" method that is empty in CE, smaller methods. Or create a "hook" method that is empty in CE,
...@@ -134,6 +134,9 @@ There are a few gotchas with it: ...@@ -134,6 +134,9 @@ There are a few gotchas with it:
guards: guards:
``` ruby ``` ruby
module EE::Base module EE::Base
extend ::Gitlab::Utils::Override
override :do_something
def do_something def do_something
# Follow the above pattern to call super and extend it # Follow the above pattern to call super and extend it
end end
...@@ -174,10 +177,11 @@ implementation: ...@@ -174,10 +177,11 @@ implementation:
```ruby ```ruby
module EE module EE
class ApplicationController module ApplicationController
def after_sign_out_path_for(resource) extend ::Gitlab::Utils::Override
raise NotImplementedError unless defined?(super)
override :after_sign_out_path_for
def after_sign_out_path_for(resource)
if Gitlab::Geo.secondary? if Gitlab::Geo.secondary?
Gitlab::Geo.primary_node.oauth_logout_url(@geo_logout_state) Gitlab::Geo.primary_node.oauth_logout_url(@geo_logout_state)
else else
...@@ -188,6 +192,8 @@ module EE ...@@ -188,6 +192,8 @@ module EE
end end
``` ```
[`extend ::Gitlab::Utils::Override`]: utilities.md#override
#### Use self-descriptive wrapper methods #### Use self-descriptive wrapper methods
When it's not possible/logical to modify the implementation of a When it's not possible/logical to modify the implementation of a
...@@ -208,8 +214,8 @@ end ...@@ -208,8 +214,8 @@ end
In EE, the implementation `ee/app/models/ee/users.rb` would be: In EE, the implementation `ee/app/models/ee/users.rb` would be:
```ruby ```ruby
override :full_private_access?
def full_private_access? def full_private_access?
raise NotImplementedError unless defined?(super)
super || auditor? super || auditor?
end end
``` ```
......
...@@ -45,6 +45,51 @@ We developed a number of utilities to ease development. ...@@ -45,6 +45,51 @@ We developed a number of utilities to ease development.
[:hello, "world", :this, :crushes, "an entire", "hash"] [:hello, "world", :this, :crushes, "an entire", "hash"]
``` ```
## [`Override`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/utils/override.rb)
* This utility could help us check if a particular method would override
another method or not. It has the same idea of Java's `@Override` annotation
or Scala's `override` keyword. However we only do this check when
`ENV['STATIC_VERIFICATION']` is set to avoid production runtime overhead.
This is useful to check:
* If we have typos in overriding methods.
* If we renamed the overridden methods, making original overriding methods
overrides nothing.
Here's a simple example:
``` ruby
class Base
def execute
end
end
class Derived < Base
extend ::Gitlab::Utils::Override
override :execute # Override check happens here
def execute
end
end
```
This also works on modules:
``` ruby
module Extension
extend ::Gitlab::Utils::Override
override :execute # Modules do not check this immediately
def execute
end
end
class Derived < Base
prepend Extension # Override check happens here, not in the module
end
```
## [`StrongMemoize`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/utils/strong_memoize.rb) ## [`StrongMemoize`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/utils/strong_memoize.rb)
* Memoize the value even if it is `nil` or `false`. * Memoize the value even if it is `nil` or `false`.
......
...@@ -25,15 +25,19 @@ the hardware requirements. ...@@ -25,15 +25,19 @@ the hardware requirements.
## Install GitLab on cloud providers ## Install GitLab on cloud providers
- [Installing in Kubernetes](kubernetes/index.md) - Install GitLab into a Kubernetes - [Installing in Kubernetes](kubernetes/index.md): Install GitLab into a Kubernetes
Cluster using our official Helm Chart Repository. Cluster using our official Helm Chart Repository.
- [Install GitLab on OpenShift](openshift_and_gitlab/index.md) - [Install GitLab on OpenShift](openshift_and_gitlab/index.md)
- [Install GitLab on DC/OS](https://mesosphere.com/blog/gitlab-dcos/) via [GitLab-Mesosphere integration](https://about.gitlab.com/2016/09/16/announcing-gitlab-and-mesosphere/) - [Install GitLab on DC/OS](https://mesosphere.com/blog/gitlab-dcos/) via [GitLab-Mesosphere integration](https://about.gitlab.com/2016/09/16/announcing-gitlab-and-mesosphere/)
- [Install GitLab on Azure](azure/index.md) - [Install GitLab on Azure](azure/index.md)
- [Install GitLab on Google Cloud Platform](google_cloud_platform/index.md) - [Install GitLab on Google Cloud Platform](google_cloud_platform/index.md)
- [Install GitLab on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/): video tutorial on
the full process of installing GitLab on Google Container Engine (GKE), pushing an application to GitLab, building the app with GitLab CI/CD, and deploying to production.
- [Install on AWS](https://about.gitlab.com/aws/) - [Install on AWS](https://about.gitlab.com/aws/)
- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) - - _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) -
Quickly test any version of GitLab on DigitalOcean using Docker Machine. Quickly test any version of GitLab on DigitalOcean using Docker Machine.
- [Getting started with GitLab and DigitalOcean](ttps://about.gitlab.com/2016/04/27/getting-started-with-gitlab-and-digitalocean/): requirements, installation process, updates.
- [Demo: Cloud Native Development with GitLab](https://about.gitlab.com/2017/04/18/cloud-native-demo/): video demonstration on how to install GitLab on Kubernetes, build a project, create Review Apps, store Docker images in Container Registry, deploy to production on Kubernetes, and monitor with Prometheus.
## Database ## Database
......
...@@ -15,6 +15,8 @@ In this tutorial, we will see how to deploy GitLab in OpenShift using GitLab's ...@@ -15,6 +15,8 @@ In this tutorial, we will see how to deploy GitLab in OpenShift using GitLab's
official Docker image while getting familiar with the web interface and CLI official Docker image while getting familiar with the web interface and CLI
tools that will help us achieve our goal. tools that will help us achieve our goal.
For a video demonstration on installing GitLab on Openshift, check the article [In 13 minutes from Kubernetes to a complete application development tool](https://about.gitlab.com/2016/11/14/idea-to-production/).
--- ---
## Prerequisites ## Prerequisites
......
...@@ -11,6 +11,7 @@ Your GitLab instance can perform HTTP POST requests on the following events: ...@@ -11,6 +11,7 @@ Your GitLab instance can perform HTTP POST requests on the following events:
- `user_remove_from_team` - `user_remove_from_team`
- `user_create` - `user_create`
- `user_destroy` - `user_destroy`
- `user_failed_login`
- `user_rename` - `user_rename`
- `key_create` - `key_create`
- `key_destroy` - `key_destroy`
...@@ -22,6 +23,8 @@ Your GitLab instance can perform HTTP POST requests on the following events: ...@@ -22,6 +23,8 @@ Your GitLab instance can perform HTTP POST requests on the following events:
The triggers for most of these are self-explanatory, but `project_update` and `project_rename` deserve some clarification: `project_update` is fired any time an attribute of a project is changed (name, description, tags, etc.) *unless* the `path` attribute is also changed. In that case, a `project_rename` is triggered instead (so that, for instance, if all you care about is the repo URL, you can just listen for `project_rename`). The triggers for most of these are self-explanatory, but `project_update` and `project_rename` deserve some clarification: `project_update` is fired any time an attribute of a project is changed (name, description, tags, etc.) *unless* the `path` attribute is also changed. In that case, a `project_rename` is triggered instead (so that, for instance, if all you care about is the repo URL, you can just listen for `project_rename`).
`user_failed_login` is sent whenever a **blocked** user attempts to login and denied access.
System hooks can be used, e.g. for logging or changing information in a LDAP server. System hooks can be used, e.g. for logging or changing information in a LDAP server.
> **Note:** > **Note:**
...@@ -196,6 +199,23 @@ Please refer to `group_rename` and `user_rename` for that case. ...@@ -196,6 +199,23 @@ Please refer to `group_rename` and `user_rename` for that case.
} }
``` ```
**User failed login:**
```json
{
"event_name": "user_failed_login",
"created_at": "2017-10-03T06:08:48Z",
"updated_at": "2018-01-15T04:52:06Z",
"name": "John Smith",
"email": "user4@example.com",
"user_id": 26,
"username": "user4",
"state": "blocked"
}
```
If the user is blocked via LDAP, `state` will be `ldap_blocked`.
**User renamed:** **User renamed:**
```json ```json
......
...@@ -20,6 +20,8 @@ project in an easy and automatic way: ...@@ -20,6 +20,8 @@ project in an easy and automatic way:
1. [Auto Test](#auto-test) 1. [Auto Test](#auto-test)
1. [Auto Code Quality](#auto-code-quality) 1. [Auto Code Quality](#auto-code-quality)
1. [Auto SAST (Static Application Security Testing)](#auto-sast) 1. [Auto SAST (Static Application Security Testing)](#auto-sast)
1. [Auto SAST for Docker images](#auto-sast-for-docker-images)
1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast)
1. [Auto Browser Performance Testing](#auto-browser-performance-testing) 1. [Auto Browser Performance Testing](#auto-browser-performance-testing)
1. [Auto Review Apps](#auto-review-apps) 1. [Auto Review Apps](#auto-review-apps)
1. [Auto Deploy](#auto-deploy) 1. [Auto Deploy](#auto-deploy)
...@@ -37,6 +39,8 @@ knowledge of the following: ...@@ -37,6 +39,8 @@ knowledge of the following:
Auto DevOps provides great defaults for all the stages; you can, however, Auto DevOps provides great defaults for all the stages; you can, however,
[customize](#customizing) almost everything to your needs. [customize](#customizing) almost everything to your needs.
For an overview on the creation of Auto DevOps, read the blog post [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/).
## Prerequisites ## Prerequisites
TIP: **Tip:** TIP: **Tip:**
...@@ -193,8 +197,10 @@ Auto Code Quality uses the open source ...@@ -193,8 +197,10 @@ Auto Code Quality uses the open source
[`codeclimate` image](https://hub.docker.com/r/codeclimate/codeclimate/) to run [`codeclimate` image](https://hub.docker.com/r/codeclimate/codeclimate/) to run
static analysis and other code checks on the current code. The report is static analysis and other code checks on the current code. The report is
created, and is uploaded as an artifact which you can later download and check created, and is uploaded as an artifact which you can later download and check
out. In GitLab Enterprise Edition Starter, differences between the source and out.
target branches are
In GitLab Enterprise Edition Starter, differences between the source and
target branches are also
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html). [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
### Auto SAST ### Auto SAST
...@@ -207,7 +213,34 @@ analysis on the current code and checks for potential security issues. Once the ...@@ -207,7 +213,34 @@ analysis on the current code and checks for potential security issues. Once the
report is created, it's uploaded as an artifact which you can later download and report is created, it's uploaded as an artifact which you can later download and
check out. check out.
Any security warnings are also [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast.html). In GitLab Enterprise Edition Ultimate, any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast.html).
### Auto SAST for Docker images
> Introduced in GitLab 10.4.
Vulnerability Static Analysis for containers uses
[Clair](https://github.com/coreos/clair) to run static analysis on a
Docker image and checks for potential security issues. Once the report is
created, it's uploaded as an artifact which you can later download and
check out.
In GitLab Enterprise Edition Ultimate, any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast_docker.html).
### Auto DAST
> Introduced in [GitLab Enterprise Edition Ultimate][ee] 10.4.
Dynamic Application Security Testing (DAST) uses the
popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy)
to perform an analysis on the current code and checks for potential security
issues. Once the report is created, it's uploaded as an artifact which you can
later download and check out.
In GitLab Enterprise Edition Ultimate, any security warnings are also
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
### Auto Browser Performance Testing ### Auto Browser Performance Testing
......
...@@ -44,7 +44,7 @@ We've gathered some resources to help you to get the best from Git with GitLab. ...@@ -44,7 +44,7 @@ We've gathered some resources to help you to get the best from Git with GitLab.
## Troubleshooting Git ## Troubleshooting Git
- [Numerous _undo_ possibilities in Git](../../articles/numerous_undo_possibilities_in_git/index.md) - [Numerous _undo_ possibilities in Git](numerous_undo_possibilities_in_git/index.md)
- Learn a few [Git troubleshooting](troubleshooting_git.md) techniques to help you out. - Learn a few [Git troubleshooting](troubleshooting_git.md) techniques to help you out.
## Branching strategies ## Branching strategies
......
This diff is collapsed.
...@@ -23,9 +23,20 @@ all the way through, from within the same platform. ...@@ -23,9 +23,20 @@ all the way through, from within the same platform.
Please check this page for an overview on [GitLab's features](https://about.gitlab.com/features/). Please check this page for an overview on [GitLab's features](https://about.gitlab.com/features/).
### Concepts
For an overview on concepts involved when developing code on GitLab,
read the articles on:
- [Mastering Code Review With GitLab](https://about.gitlab.com/2017/03/17/demo-mastering-code-review-with-gitlab/).
- [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/#gitlab-workflow-use-case-scenario).
- [Tutorial: It's all connected in GitLab](https://about.gitlab.com/2016/03/08/gitlab-tutorial-its-all-connected/): an overview on code collaboration with GitLab.
- [Trends in Version Control Land: Microservices](https://about.gitlab.com/2016/08/16/trends-in-version-control-land-microservices/).
- [Trends in Version Control Land: Innersourcing](https://about.gitlab.com/2016/07/07/trends-version-control-innersourcing/).
## Use cases ## Use cases
GitLab is a git-based platforms that integrates a great number of essential tools for software development and deployment, and project management: GitLab is a Git-based platform that integrates a great number of essential tools for software development and deployment, and project management:
- Code hosting in repositories with version control - Code hosting in repositories with version control
- Track proposals for new implementations, bug reports, and feedback with a - Track proposals for new implementations, bug reports, and feedback with a
...@@ -58,12 +69,6 @@ and [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board. ...@@ -58,12 +69,6 @@ and [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.
You can also [integrate](project/integrations/project_services.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, HipChat, Trello, Slack, Bamboo CI, JIRA, and a lot more. You can also [integrate](project/integrations/project_services.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, HipChat, Trello, Slack, Bamboo CI, JIRA, and a lot more.
### Articles
For a complete workflow use case please check [GitLab Workflow, an Overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/#gitlab-workflow-use-case-scenario).
For more use cases please check our [Technical Articles](../articles/index.md).
## Projects ## Projects
In GitLab, you can create [projects](project/index.md) for numerous reasons, such as, host In GitLab, you can create [projects](project/index.md) for numerous reasons, such as, host
......
...@@ -17,11 +17,11 @@ module API ...@@ -17,11 +17,11 @@ module API
end end
def storage_health def storage_health
@failing_storage_health ||= Gitlab::Git::Storage::Health.for_all_storages @storage_health ||= Gitlab::Git::Storage::Health.for_all_storages
end end
end end
desc 'Get all failing git storages' do desc 'Get all git storages' do
detail 'This feature was introduced in GitLab 9.5' detail 'This feature was introduced in GitLab 9.5'
success Entities::RepositoryStorageHealth success Entities::RepositoryStorageHealth
end end
......
...@@ -3,8 +3,10 @@ module API ...@@ -3,8 +3,10 @@ module API
module CommonHelpers module CommonHelpers
def convert_parameters_from_legacy_format(params) def convert_parameters_from_legacy_format(params)
params.tap do |params| params.tap do |params|
if params[:assignee_id].present? assignee_id = params.delete(:assignee_id)
params[:assignee_ids] = [params.delete(:assignee_id)]
if assignee_id.present?
params[:assignee_ids] = [assignee_id]
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Auth
class BlockedUserTracker
ACTIVE_RECORD_REQUEST_PARAMS = 'action_dispatch.request.request_parameters'
def self.log_if_user_blocked(env)
message = env.dig('warden.options', :message)
# Devise calls User#active_for_authentication? on the User model and then
# throws an exception to Warden with User#inactive_message:
# https://github.com/plataformatec/devise/blob/v4.2.1/lib/devise/hooks/activatable.rb#L8
#
# Since Warden doesn't pass the user record to the failure handler, we
# need to do a database lookup with the username. We can limit the
# lookups to happen when the user was blocked by checking the inactive
# message passed along by Warden.
return unless message == User::BLOCKED_MESSAGE
login = env.dig(ACTIVE_RECORD_REQUEST_PARAMS, 'user', 'login')
return unless login.present?
user = User.by_login(login)
return unless user&.blocked?
Gitlab::AppLogger.info("Failed login for blocked user: user=#{user.username} ip=#{env['REMOTE_ADDR']}")
SystemHooksService.new.execute_hooks_for(user, :failed_login)
true
rescue TypeError
end
end
end
end
...@@ -96,9 +96,7 @@ module Gitlab ...@@ -96,9 +96,7 @@ module Gitlab
end end
def ensure_action_dispatch_request(request) def ensure_action_dispatch_request(request)
return request if request.is_a?(ActionDispatch::Request) ActionDispatch::Request.new(request.env.dup)
ActionDispatch::Request.new(request.env)
end end
def current_request def current_request
......
...@@ -15,7 +15,7 @@ module Gitlab ...@@ -15,7 +15,7 @@ module Gitlab
@conflicts ||= begin @conflicts ||= begin
@target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled| @target_repository.gitaly_migrate(:conflicts_list_conflict_files) do |is_enabled|
if is_enabled if is_enabled
gitaly_conflicts_client(@target_repository).list_conflict_files gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
else else
rugged_list_conflict_files rugged_list_conflict_files
end end
......
...@@ -44,29 +44,13 @@ module Gitlab ...@@ -44,29 +44,13 @@ module Gitlab
# Import project via git clone --bare # Import project via git clone --bare
# URL must be publicly cloneable # URL must be publicly cloneable
def import_project(source, timeout) def import_project(source, timeout)
# Skip import if repo already exists Gitlab::GitalyClient.migrate(:import_repository) do |is_enabled|
return false if File.exist?(repository_absolute_path) if is_enabled
gitaly_import_repository(source)
masked_source = mask_password_in_url(source) else
git_import_repository(source, timeout)
logger.info "Importing project from <#{masked_source}> to <#{repository_absolute_path}>." end
cmd = %W(git clone --bare -- #{source} #{repository_absolute_path})
success = run_with_timeout(cmd, timeout, nil)
unless success
logger.error("Importing project from <#{masked_source}> to <#{repository_absolute_path}> failed.")
FileUtils.rm_rf(repository_absolute_path)
return false
end end
Gitlab::Git::Repository.create_hooks(repository_absolute_path, global_hooks_path)
# The project was imported successfully.
# Remove the origin URL since it may contain password.
remove_origin_in_repo
true
end end
def fork_repository(new_shard_path, new_repository_relative_path) def fork_repository(new_shard_path, new_repository_relative_path)
...@@ -231,6 +215,42 @@ module Gitlab ...@@ -231,6 +215,42 @@ module Gitlab
raise(ShardNameNotFoundError, "no shard found for path '#{shard_path}'") raise(ShardNameNotFoundError, "no shard found for path '#{shard_path}'")
end end
def git_import_repository(source, timeout)
# Skip import if repo already exists
return false if File.exist?(repository_absolute_path)
masked_source = mask_password_in_url(source)
logger.info "Importing project from <#{masked_source}> to <#{repository_absolute_path}>."
cmd = %W(git clone --bare -- #{source} #{repository_absolute_path})
success = run_with_timeout(cmd, timeout, nil)
unless success
logger.error("Importing project from <#{masked_source}> to <#{repository_absolute_path}> failed.")
FileUtils.rm_rf(repository_absolute_path)
return false
end
Gitlab::Git::Repository.create_hooks(repository_absolute_path, global_hooks_path)
# The project was imported successfully.
# Remove the origin URL since it may contain password.
remove_origin_in_repo
true
end
def gitaly_import_repository(source)
raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil)
Gitlab::GitalyClient::RepositoryService.new(raw_repository).import_repository(source)
true
rescue GRPC::BadStatus => e
@output << e.message
false
end
def git_fork_repository(new_shard_path, new_repository_relative_path) def git_fork_repository(new_shard_path, new_repository_relative_path)
from_path = repository_absolute_path from_path = repository_absolute_path
to_path = File.join(new_shard_path, new_repository_relative_path) to_path = File.join(new_shard_path, new_repository_relative_path)
......
...@@ -177,7 +177,7 @@ module Gitlab ...@@ -177,7 +177,7 @@ module Gitlab
response = GitalyClient.call(@repository.storage, :commit_service, :list_commits_by_oid, request, timeout: GitalyClient.medium_timeout) response = GitalyClient.call(@repository.storage, :commit_service, :list_commits_by_oid, request, timeout: GitalyClient.medium_timeout)
consume_commits_response(response) consume_commits_response(response)
rescue GRPC::Unknown # If no repository is found, happens mainly during testing rescue GRPC::NotFound # If no repository is found, happens mainly during testing
[] []
end end
......
module Gitlab
module GitalyClient
class ConflictFilesStitcher
include Enumerable
def initialize(rpc_response)
@rpc_response = rpc_response
end
def each
current_file = nil
@rpc_response.each do |msg|
msg.files.each do |gitaly_file|
if gitaly_file.header
yield current_file if current_file
current_file = file_from_gitaly_header(gitaly_file.header)
else
current_file.content << gitaly_file.content
end
end
end
yield current_file if current_file
end
private
def file_from_gitaly_header(header)
Gitlab::Git::Conflict::File.new(
Gitlab::GitalyClient::Util.git_repository(header.repository),
header.commit_oid,
conflict_from_gitaly_file_header(header),
''
)
end
def conflict_from_gitaly_file_header(header)
{
ours: { path: header.our_path, mode: header.our_mode },
theirs: { path: header.their_path }
}
end
end
end
end
...@@ -20,7 +20,11 @@ module Gitlab ...@@ -20,7 +20,11 @@ module Gitlab
) )
response = GitalyClient.call(@repository.storage, :conflicts_service, :list_conflict_files, request) response = GitalyClient.call(@repository.storage, :conflicts_service, :list_conflict_files, request)
files_from_response(response).to_a GitalyClient::ConflictFilesStitcher.new(response)
end
def conflicts?
list_conflict_files.any?
end end
def resolve_conflicts(target_repository, resolution, source_branch, target_branch) def resolve_conflicts(target_repository, resolution, source_branch, target_branch)
...@@ -58,38 +62,6 @@ module Gitlab ...@@ -58,38 +62,6 @@ module Gitlab
user: Gitlab::Git::User.from_gitlab(resolution.user).to_gitaly user: Gitlab::Git::User.from_gitlab(resolution.user).to_gitaly
) )
end end
def files_from_response(response)
files = []
response.each do |msg|
msg.files.each do |gitaly_file|
if gitaly_file.header
files << file_from_gitaly_header(gitaly_file.header)
else
files.last.content << gitaly_file.content
end
end
end
files
end
def file_from_gitaly_header(header)
Gitlab::Git::Conflict::File.new(
Gitlab::GitalyClient::Util.git_repository(header.repository),
header.commit_oid,
conflict_from_gitaly_file_header(header),
''
)
end
def conflict_from_gitaly_file_header(header)
{
ours: { path: header.our_path, mode: header.our_mode },
theirs: { path: header.their_path }
}
end
end end
end end
end end
...@@ -100,6 +100,21 @@ module Gitlab ...@@ -100,6 +100,21 @@ module Gitlab
) )
end end
def import_repository(source)
request = Gitaly::CreateRepositoryFromURLRequest.new(
repository: @gitaly_repo,
url: source
)
GitalyClient.call(
@storage,
:repository_service,
:create_repository_from_url,
request,
timeout: GitalyClient.default_timeout
)
end
def rebase_in_progress?(rebase_id) def rebase_in_progress?(rebase_id)
request = Gitaly::IsRebaseInProgressRequest.new( request = Gitaly::IsRebaseInProgressRequest.new(
repository: @gitaly_repo, repository: @gitaly_repo,
......
...@@ -36,7 +36,11 @@ module Gitlab ...@@ -36,7 +36,11 @@ module Gitlab
def complete_command(namespace_name) def complete_command(namespace_name)
return unless chart return unless chart
"helm install #{chart} --name #{name} --namespace #{namespace_name} >/dev/null" if chart_values_file
"helm install #{chart} --name #{name} --namespace #{namespace_name} -f /data/helm/#{name}/config/values.yaml >/dev/null"
else
"helm install #{chart} --name #{name} --namespace #{namespace_name} >/dev/null"
end
end end
def install_dps_command def install_dps_command
......
...@@ -10,9 +10,10 @@ module Gitlab ...@@ -10,9 +10,10 @@ module Gitlab
def generate def generate
spec = { containers: [container_specification], restartPolicy: 'Never' } spec = { containers: [container_specification], restartPolicy: 'Never' }
if command.chart_values_file if command.chart_values_file
generate_config_map create_config_map
spec['volumes'] = volumes_specification spec[:volumes] = volumes_specification
end end
::Kubeclient::Resource.new(metadata: metadata, spec: spec) ::Kubeclient::Resource.new(metadata: metadata, spec: spec)
...@@ -35,19 +36,39 @@ module Gitlab ...@@ -35,19 +36,39 @@ module Gitlab
end end
def labels def labels
{ 'gitlab.org/action': 'install', 'gitlab.org/application': command.name } {
'gitlab.org/action': 'install',
'gitlab.org/application': command.name
}
end end
def metadata def metadata
{ name: command.pod_name, namespace: namespace_name, labels: labels } {
name: command.pod_name,
namespace: namespace_name,
labels: labels
}
end end
def volume_mounts_specification def volume_mounts_specification
[{ name: 'config-volume', mountPath: '/etc/config' }] [
{
name: 'configuration-volume',
mountPath: "/data/helm/#{command.name}/config"
}
]
end end
def volumes_specification def volumes_specification
[{ name: 'config-volume', configMap: { name: 'values-config' } }] [
{
name: 'configuration-volume',
configMap: {
name: 'values-content-configuration',
items: [{ key: 'values', path: 'values.yaml' }]
}
}
]
end end
def generate_pod_env(command) def generate_pod_env(command)
...@@ -58,10 +79,10 @@ module Gitlab ...@@ -58,10 +79,10 @@ module Gitlab
}.map { |key, value| { name: key, value: value } } }.map { |key, value| { name: key, value: value } }
end end
def generate_config_map def create_config_map
resource = ::Kubeclient::Resource.new resource = ::Kubeclient::Resource.new
resource.metadata = { name: 'values-config', namespace: namespace_name } resource.metadata = { name: 'values-content-configuration', namespace: namespace_name, labels: { name: 'values-content-configuration' } }
resource.data = YAML.load_file(command.chart_values_file) resource.data = { values: File.read(command.chart_values_file) }
kubeclient.create_config_map(resource) kubeclient.create_config_map(resource)
end end
end end
......
module Gitlab
module Utils
module Override
class Extension
def self.verify_class!(klass, method_name)
instance_method_defined?(klass, method_name) ||
raise(
NotImplementedError.new(
"#{klass}\##{method_name} doesn't exist!"))
end
def self.instance_method_defined?(klass, name, include_super: true)
klass.instance_methods(include_super).include?(name) ||
klass.private_instance_methods(include_super).include?(name)
end
attr_reader :subject
def initialize(subject)
@subject = subject
end
def add_method_name(method_name)
method_names << method_name
end
def add_class(klass)
classes << klass
end
def verify!
classes.each do |klass|
index = klass.ancestors.index(subject)
parents = klass.ancestors.drop(index + 1)
method_names.each do |method_name|
parents.any? do |parent|
self.class.instance_method_defined?(
parent, method_name, include_super: false)
end ||
raise(
NotImplementedError.new(
"#{klass}\##{method_name} doesn't exist!"))
end
end
end
private
def method_names
@method_names ||= []
end
def classes
@classes ||= []
end
end
# Instead of writing patterns like this:
#
# def f
# raise NotImplementedError unless defined?(super)
#
# true
# end
#
# We could write it like:
#
# extend ::Gitlab::Utils::Override
#
# override :f
# def f
# true
# end
#
# This would make sure we're overriding something. See:
# https://gitlab.com/gitlab-org/gitlab-ee/issues/1819
def override(method_name)
return unless ENV['STATIC_VERIFICATION']
if is_a?(Class)
Extension.verify_class!(self, method_name)
else # We delay the check for modules
Override.extensions[self] ||= Extension.new(self)
Override.extensions[self].add_method_name(method_name)
end
end
def included(base = nil)
return super if base.nil? # Rails concern, ignoring it
super
if base.is_a?(Class) # We could check for Class in `override`
# This could be `nil` if `override` was never called
Override.extensions[self]&.add_class(base)
end
end
alias_method :prepended, :included
def self.extensions
@extensions ||= {}
end
def self.verify!
extensions.values.each(&:verify!)
end
end
end
end
...@@ -7,4 +7,9 @@ namespace :dev do ...@@ -7,4 +7,9 @@ namespace :dev do
Rake::Task["gitlab:setup"].invoke Rake::Task["gitlab:setup"].invoke
Rake::Task["gitlab:shell:setup"].invoke Rake::Task["gitlab:shell:setup"].invoke
end end
desc "GitLab | Eager load application"
task load: :environment do
Rails.application.eager_load!
end
end end
...@@ -54,16 +54,6 @@ namespace :gitlab do ...@@ -54,16 +54,6 @@ namespace :gitlab do
# (Re)create hooks # (Re)create hooks
Rake::Task['gitlab:shell:create_hooks'].invoke Rake::Task['gitlab:shell:create_hooks'].invoke
# Required for debian packaging with PKGR: Setup .ssh/environment with
# the current PATH, so that the correct ruby version gets loaded
# Requires to set "PermitUserEnvironment yes" in sshd config (should not
# be an issue since it is more than likely that there are no "normal"
# user accounts on a gitlab server). The alternative is for the admin to
# install a ruby (1.9.3+) in the global path.
File.open(File.join(user_home, ".ssh", "environment"), "w+") do |f|
f.puts "PATH=#{ENV['PATH']}"
end
Gitlab::Shell.ensure_secret_token! Gitlab::Shell.ensure_secret_token!
end end
......
unless Rails.env.production? unless Rails.env.production?
namespace :lint do namespace :lint do
task :static_verification_env do
ENV['STATIC_VERIFICATION'] = 'true'
end
desc "GitLab | lint | Static verification"
task static_verification: %w[
lint:static_verification_env
dev:load
] do
Gitlab::Utils::Override.verify!
end
desc "GitLab | lint | Lint JavaScript files using ESLint" desc "GitLab | lint | Lint JavaScript files using ESLint"
task :javascript do task :javascript do
Rake::Task['eslint'].invoke Rake::Task['eslint'].invoke
......
...@@ -10,9 +10,10 @@ tasks = [ ...@@ -10,9 +10,10 @@ tasks = [
%w[bundle exec license_finder], %w[bundle exec license_finder],
%w[yarn run eslint], %w[yarn run eslint],
%w[bundle exec rubocop --parallel], %w[bundle exec rubocop --parallel],
%w[scripts/lint-conflicts.sh],
%w[bundle exec rake gettext:lint], %w[bundle exec rake gettext:lint],
%w[scripts/lint-changelog-yaml] %w[bundle exec rake lint:static_verification],
%w[scripts/lint-changelog-yaml],
%w[scripts/lint-conflicts.sh]
] ]
failed_tasks = tasks.reduce({}) do |failures, task| failed_tasks = tasks.reduce({}) do |failures, task|
......
...@@ -380,9 +380,18 @@ feature 'Jobs' do ...@@ -380,9 +380,18 @@ feature 'Jobs' do
it 'shows manual action empty state' do it 'shows manual action empty state' do
expect(page).to have_content('This job requires a manual action') expect(page).to have_content('This job requires a manual action')
expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.') expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments')
expect(page).to have_link('Trigger this manual action') expect(page).to have_link('Trigger this manual action')
end end
it 'plays manual action', :js do
click_link 'Trigger this manual action'
wait_for_requests
expect(page).to have_content('This job has not been triggered')
expect(page).to have_content('This job is stuck, because the project doesn\'t have any runners online assigned to it.')
expect(page).to have_content('pending')
end
end end
context 'Non triggered job' do context 'Non triggered job' do
...@@ -392,9 +401,8 @@ feature 'Jobs' do ...@@ -392,9 +401,8 @@ feature 'Jobs' do
visit project_job_path(project, job) visit project_job_path(project, job)
end end
it 'shows manual action empty state' do it 'shows empty state' do
expect(page).to have_content('This job has not been triggered yet') expect(page).to have_content('This job has not been triggered yet')
expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered.')
end end
end end
end end
......
import Vue from 'vue';
import stackedProgressBarComponent from '~/vue_shared/components/stacked_progress_bar.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
const createComponent = (config) => {
const Component = Vue.extend(stackedProgressBarComponent);
const defaultConfig = Object.assign({}, {
successLabel: 'Synced',
failureLabel: 'Failed',
neutralLabel: 'Out of sync',
successCount: 10,
failureCount: 5,
totalCount: 20,
}, config);
return mountComponent(Component, defaultConfig);
};
describe('StackedProgressBarComponent', () => {
let vm;
beforeEach(() => {
vm = createComponent();
});
afterEach(() => {
vm.$destroy();
});
describe('computed', () => {
describe('neutralCount', () => {
it('returns neutralCount based on totalCount, successCount and failureCount', () => {
expect(vm.neutralCount).toBe(5); // 20 - 10 - 5
});
});
});
describe('methods', () => {
describe('getPercent', () => {
it('returns percentage from provided count based on `totalCount`', () => {
expect(vm.getPercent(10)).toBe(50);
});
});
describe('barStyle', () => {
it('returns style string based on percentage provided', () => {
expect(vm.barStyle(50)).toBe('width: 50%;');
});
});
describe('getTooltip', () => {
it('returns label string based on label and count provided', () => {
expect(vm.getTooltip('Synced', 10)).toBe('Synced: 10');
});
});
});
describe('template', () => {
it('renders container element', () => {
expect(vm.$el.classList.contains('stacked-progress-bar')).toBeTruthy();
});
it('renders empty state when count is unavailable', () => {
const vmX = createComponent({ totalCount: 0, successCount: 0, failureCount: 0 });
expect(vmX.$el.querySelectorAll('.status-unavailable').length).not.toBe(0);
vmX.$destroy();
});
it('renders bar elements when count is available', () => {
expect(vm.$el.querySelectorAll('.status-green').length).not.toBe(0);
expect(vm.$el.querySelectorAll('.status-neutral').length).not.toBe(0);
expect(vm.$el.querySelectorAll('.status-red').length).not.toBe(0);
});
});
});
require 'spec_helper'
describe Gitlab::Auth::BlockedUserTracker do
set(:user) { create(:user) }
describe '.log_if_user_blocked' do
it 'does not log if user failed to login due to undefined reason' do
expect_any_instance_of(SystemHooksService).not_to receive(:execute_hooks_for)
expect(described_class.log_if_user_blocked({})).to be_nil
end
it 'gracefully handles malformed environment variables' do
env = { 'warden.options' => 'test' }
expect(described_class.log_if_user_blocked(env)).to be_nil
end
context 'failed login due to blocked user' do
let(:env) do
{
'warden.options' => { message: User::BLOCKED_MESSAGE },
described_class::ACTIVE_RECORD_REQUEST_PARAMS => { 'user' => { 'login' => user.username } }
}
end
subject { described_class.log_if_user_blocked(env) }
before do
expect_any_instance_of(SystemHooksService).to receive(:execute_hooks_for).with(user, :failed_login)
end
it 'logs a blocked user' do
user.block!
expect(subject).to be_truthy
end
it 'logs a blocked user by e-mail' do
user.block!
env[described_class::ACTIVE_RECORD_REQUEST_PARAMS]['user']['login'] = user.email
expect(subject).to be_truthy
end
it 'logs a LDAP blocked user' do
user.ldap_block!
expect(subject).to be_truthy
end
end
end
end
...@@ -76,6 +76,16 @@ describe Gitlab::Auth::UserAuthFinders do ...@@ -76,6 +76,16 @@ describe Gitlab::Auth::UserAuthFinders do
expect(find_user_from_rss_token).to be_nil expect(find_user_from_rss_token).to be_nil
end end
end end
context 'when the request format is empty' do
it 'the method call does not modify the original value' do
env['action_dispatch.request.formats'] = nil
find_user_from_rss_token
expect(env['action_dispatch.request.formats']).to be_nil
end
end
end end
describe '#find_user_from_access_token' do describe '#find_user_from_access_token' do
......
...@@ -158,39 +158,55 @@ describe Gitlab::Git::GitlabProjects do ...@@ -158,39 +158,55 @@ describe Gitlab::Git::GitlabProjects do
subject { gl_projects.import_project(import_url, timeout) } subject { gl_projects.import_project(import_url, timeout) }
context 'success import' do shared_examples 'importing repository' do
it 'imports a repo' do context 'success import' do
expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy it 'imports a repo' do
expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>." is_expected.to be_truthy
expect(logger).to receive(:info).with(message)
is_expected.to be_truthy expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_truthy
end
end
expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_truthy context 'already exists' do
it "doesn't import" do
FileUtils.mkdir_p(tmp_repo_path)
is_expected.to be_falsy
end
end end
end end
context 'already exists' do context 'when Gitaly import_repository feature is enabled' do
it "doesn't import" do it_behaves_like 'importing repository'
FileUtils.mkdir_p(tmp_repo_path) end
context 'when Gitaly import_repository feature is disabled', :disable_gitaly do
describe 'logging' do
it 'imports a repo' do
message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>."
expect(logger).to receive(:info).with(message)
is_expected.to be_falsy subject
end
end end
end
context 'timeout' do context 'timeout' do
it 'does not import a repo' do it 'does not import a repo' do
stub_spawn_timeout(cmd, timeout, nil) stub_spawn_timeout(cmd, timeout, nil)
message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed." message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed."
expect(logger).to receive(:error).with(message) expect(logger).to receive(:error).with(message)
is_expected.to be_falsy is_expected.to be_falsy
expect(gl_projects.output).to eq("Timed out\n") expect(gl_projects.output).to eq("Timed out\n")
expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
end
end end
it_behaves_like 'importing repository'
end end
end end
......
require 'spec_helper'
describe Gitlab::GitalyClient::ConflictFilesStitcher do
describe 'enumeration' do
it 'combines segregated ConflictFile messages together' do
target_project = create(:project, :repository)
target_repository = target_project.repository.raw
target_gitaly_repository = target_repository.gitaly_repository
our_path_1 = 'our/path/1'
their_path_1 = 'their/path/1'
our_mode_1 = 0744
commit_oid_1 = 'f00'
content_1 = 'content of the first file'
our_path_2 = 'our/path/2'
their_path_2 = 'their/path/2'
our_mode_2 = 0600
commit_oid_2 = 'ba7'
content_2 = 'content of the second file'
header_1 = double(repository: target_gitaly_repository, commit_oid: commit_oid_1,
our_path: our_path_1, their_path: their_path_1, our_mode: our_mode_1)
header_2 = double(repository: target_gitaly_repository, commit_oid: commit_oid_2,
our_path: our_path_2, their_path: their_path_2, our_mode: our_mode_2)
messages = [
double(files: [double(header: header_1), double(header: nil, content: content_1[0..5])]),
double(files: [double(header: nil, content: content_1[6..-1])]),
double(files: [double(header: header_2)]),
double(files: [double(header: nil, content: content_2[0..5]), double(header: nil, content: content_2[6..10])]),
double(files: [double(header: nil, content: content_2[11..-1])])
]
conflict_files = described_class.new(messages).to_a
expect(conflict_files.size).to be(2)
expect(conflict_files[0].content).to eq(content_1)
expect(conflict_files[0].their_path).to eq(their_path_1)
expect(conflict_files[0].our_path).to eq(our_path_1)
expect(conflict_files[0].our_mode).to be(our_mode_1)
expect(conflict_files[0].repository).to eq(target_repository)
expect(conflict_files[0].commit_oid).to eq(commit_oid_1)
expect(conflict_files[1].content).to eq(content_2)
expect(conflict_files[1].their_path).to eq(their_path_2)
expect(conflict_files[1].our_path).to eq(our_path_2)
expect(conflict_files[1].our_mode).to be(our_mode_2)
expect(conflict_files[1].repository).to eq(target_repository)
expect(conflict_files[1].commit_oid).to eq(commit_oid_2)
end
end
end
...@@ -19,41 +19,12 @@ describe Gitlab::GitalyClient::ConflictsService do ...@@ -19,41 +19,12 @@ describe Gitlab::GitalyClient::ConflictsService do
their_commit_oid: their_commit_oid their_commit_oid: their_commit_oid
) )
end end
let(:our_path) { 'our/path' }
let(:their_path) { 'their/path' }
let(:our_mode) { 0744 }
let(:header) do
double(repository: target_gitaly_repository, commit_oid: our_commit_oid,
our_path: our_path, our_mode: 0744, their_path: their_path)
end
let(:response) do
[
double(files: [double(header: header), double(content: 'foo', header: nil)]),
double(files: [double(content: 'bar', header: nil)])
]
end
let(:file) { subject[0] }
subject { client.list_conflict_files }
it 'sends an RPC request' do it 'sends an RPC request' do
expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files) expect_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
.with(request, kind_of(Hash)).and_return([]) .with(request, kind_of(Hash)).and_return([].to_enum)
subject
end
it 'forms a Gitlab::Git::ConflictFile collection from the response' do
allow_any_instance_of(Gitaly::ConflictsService::Stub).to receive(:list_conflict_files)
.with(request, kind_of(Hash)).and_return(response)
expect(subject.size).to be(1) client.list_conflict_files
expect(file.content).to eq('foobar')
expect(file.their_path).to eq(their_path)
expect(file.our_path).to eq(our_path)
expect(file.our_mode).to be(our_mode)
expect(file.repository).to eq(target_repository)
expect(file.commit_oid).to eq(our_commit_oid)
end end
end end
......
...@@ -100,6 +100,25 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do ...@@ -100,6 +100,25 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
is_expected.to eq(command) is_expected.to eq(command)
end end
end end
context 'when chart values file is present' do
let(:install_command) { described_class.new(prometheus.name, chart: prometheus.chart, chart_values_file: prometheus.chart_values_file) }
let(:command) do
<<~MSG.chomp
set -eo pipefail
apk add -U ca-certificates openssl >/dev/null
wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
helm init --client-only >/dev/null
helm install #{prometheus.chart} --name #{prometheus.name} --namespace #{namespace.name} -f /data/helm/#{prometheus.name}/config/values.yaml >/dev/null
MSG
end
it 'should return appropriate command' do
is_expected.to eq(command)
end
end
end end
describe "#pod_name" do describe "#pod_name" do
......
...@@ -52,18 +52,20 @@ describe Gitlab::Kubernetes::Helm::Pod do ...@@ -52,18 +52,20 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'should include volumes for the container' do it 'should include volumes for the container' do
container = subject.generate.spec.containers.first container = subject.generate.spec.containers.first
expect(container.volumeMounts.first['name']).to eq('config-volume') expect(container.volumeMounts.first['name']).to eq('configuration-volume')
expect(container.volumeMounts.first['mountPath']).to eq('/etc/config') expect(container.volumeMounts.first['mountPath']).to eq("/data/helm/#{app.name}/config")
end end
it 'should include a volume inside the specification' do it 'should include a volume inside the specification' do
spec = subject.generate.spec spec = subject.generate.spec
expect(spec.volumes.first['name']).to eq('config-volume') expect(spec.volumes.first['name']).to eq('configuration-volume')
end end
it 'should mount configMap specification in the volume' do it 'should mount configMap specification in the volume' do
spec = subject.generate.spec spec = subject.generate.spec
expect(spec.volumes.first.configMap['name']).to eq('values-config') expect(spec.volumes.first.configMap['name']).to eq('values-content-configuration')
expect(spec.volumes.first.configMap['items'].first['key']).to eq('values')
expect(spec.volumes.first.configMap['items'].first['path']).to eq('values.yaml')
end end
end end
......
require 'spec_helper'
describe Gitlab::Utils::Override do
let(:base) { Struct.new(:good) }
let(:derived) { Class.new(base).tap { |m| m.extend described_class } }
let(:extension) { Module.new.tap { |m| m.extend described_class } }
let(:prepending_class) { base.tap { |m| m.prepend extension } }
let(:including_class) { base.tap { |m| m.include extension } }
let(:klass) { subject }
def good(mod)
mod.module_eval do
override :good
def good
super.succ
end
end
mod
end
def bad(mod)
mod.module_eval do
override :bad
def bad
true
end
end
mod
end
shared_examples 'checking as intended' do
it 'checks ok for overriding method' do
good(subject)
result = klass.new(0).good
expect(result).to eq(1)
described_class.verify!
end
it 'raises NotImplementedError when it is not overriding anything' do
expect do
bad(subject)
klass.new(0).bad
described_class.verify!
end.to raise_error(NotImplementedError)
end
end
shared_examples 'nothing happened' do
it 'does not complain when it is overriding something' do
good(subject)
result = klass.new(0).good
expect(result).to eq(1)
described_class.verify!
end
it 'does not complain when it is not overriding anything' do
bad(subject)
result = klass.new(0).bad
expect(result).to eq(true)
described_class.verify!
end
end
before do
# Make sure we're not touching the internal cache
allow(described_class).to receive(:extensions).and_return({})
end
describe '#override' do
context 'when STATIC_VERIFICATION is set' do
before do
stub_env('STATIC_VERIFICATION', 'true')
end
context 'when subject is a class' do
subject { derived }
it_behaves_like 'checking as intended'
end
context 'when subject is a module, and class is prepending it' do
subject { extension }
let(:klass) { prepending_class }
it_behaves_like 'checking as intended'
end
context 'when subject is a module, and class is including it' do
subject { extension }
let(:klass) { including_class }
it 'raises NotImplementedError because it is not overriding it' do
expect do
good(subject)
klass.new(0).good
described_class.verify!
end.to raise_error(NotImplementedError)
end
it 'raises NotImplementedError when it is not overriding anything' do
expect do
bad(subject)
klass.new(0).bad
described_class.verify!
end.to raise_error(NotImplementedError)
end
end
end
end
context 'when STATIC_VERIFICATION is not set' do
before do
stub_env('STATIC_VERIFICATION', nil)
end
context 'when subject is a class' do
subject { derived }
it_behaves_like 'nothing happened'
end
context 'when subject is a module, and class is prepending it' do
subject { extension }
let(:klass) { prepending_class }
it_behaves_like 'nothing happened'
end
context 'when subject is a module, and class is including it' do
subject { extension }
let(:klass) { including_class }
it 'does not complain when it is overriding something' do
good(subject)
result = klass.new(0).good
expect(result).to eq(0)
described_class.verify!
end
it 'does not complain when it is not overriding anything' do
bad(subject)
result = klass.new(0).bad
expect(result).to eq(true)
described_class.verify!
end
end
end
end
...@@ -358,28 +358,38 @@ describe Repository do ...@@ -358,28 +358,38 @@ describe Repository do
end end
describe '#can_be_merged?' do describe '#can_be_merged?' do
context 'mergeable branches' do shared_examples 'can be merged' do
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') } context 'mergeable branches' do
subject { repository.can_be_merged?('0b4bc9a49b562e85de7cc9e834518ea6828729b9', 'master') }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
end end
context 'non-mergeable branches' do context 'non-mergeable branches' do
subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') } subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') }
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end end
context 'non merged branch' do context 'non merged branch' do
subject { repository.merged_to_root_ref?('fix') } subject { repository.merged_to_root_ref?('fix') }
it { is_expected.to be_falsey } it { is_expected.to be_falsey }
end
context 'non existent branch' do
subject { repository.merged_to_root_ref?('non_existent_branch') }
it { is_expected.to be_nil }
end
end end
context 'non existent branch' do context 'when Gitaly can_be_merged feature is enabled' do
subject { repository.merged_to_root_ref?('non_existent_branch') } it_behaves_like 'can be merged'
end
it { is_expected.to be_nil } context 'when Gitaly can_be_merged feature is disabled', :disable_gitaly do
it_behaves_like 'can be merged'
end end
end end
......
...@@ -847,6 +847,15 @@ describe API::Issues, :mailer do ...@@ -847,6 +847,15 @@ describe API::Issues, :mailer do
expect(json_response['assignee']['name']).to eq(user2.name) expect(json_response['assignee']['name']).to eq(user2.name)
expect(json_response['assignees'].first['name']).to eq(user2.name) expect(json_response['assignees'].first['name']).to eq(user2.name)
end end
it 'creates a new project issue when assignee_id is empty' do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', assignee_id: ''
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('new issue')
expect(json_response['assignee']).to be_nil
end
end end
context 'single assignee restrictions' do context 'single assignee restrictions' do
......
...@@ -105,12 +105,25 @@ describe SystemHooksService do ...@@ -105,12 +105,25 @@ describe SystemHooksService do
expect(data[:old_username]).to eq(user.username_was) expect(data[:old_username]).to eq(user.username_was)
end end
end end
context 'user_failed_login' do
it 'contains state of user' do
user.ldap_block!
data = event_data(user, :failed_login)
expect(data).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username, :state)
expect(data[:username]).to eq(user.username)
expect(data[:state]).to eq('ldap_blocked')
end
end
end end
context 'event names' do context 'event names' do
it { expect(event_name(user, :create)).to eq "user_create" } it { expect(event_name(user, :create)).to eq "user_create" }
it { expect(event_name(user, :destroy)).to eq "user_destroy" } it { expect(event_name(user, :destroy)).to eq "user_destroy" }
it { expect(event_name(user, :rename)).to eq 'user_rename' } it { expect(event_name(user, :rename)).to eq 'user_rename' }
it { expect(event_name(user, :failed_login)).to eq 'user_failed_login' }
it { expect(event_name(project, :create)).to eq "project_create" } it { expect(event_name(project, :create)).to eq "project_create" }
it { expect(event_name(project, :destroy)).to eq "project_destroy" } it { expect(event_name(project, :destroy)).to eq "project_destroy" }
it { expect(event_name(project, :rename)).to eq "project_rename" } it { expect(event_name(project, :rename)).to eq "project_rename" }
......
...@@ -97,10 +97,6 @@ RSpec.configure do |config| ...@@ -97,10 +97,6 @@ RSpec.configure do |config|
TestEnv.init TestEnv.init
end end
config.after(:suite) do
TestEnv.cleanup
end
config.before(:example) do config.before(:example) do
# Skip pre-receive hook check so we can use the web editor and merge. # Skip pre-receive hook check so we can use the web editor and merge.
allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
......
...@@ -90,10 +90,6 @@ module TestEnv ...@@ -90,10 +90,6 @@ module TestEnv
setup_forked_repo setup_forked_repo
end end
def cleanup
stop_gitaly
end
def disable_mailer def disable_mailer
allow_any_instance_of(NotificationService).to receive(:mailer) allow_any_instance_of(NotificationService).to receive(:mailer)
.and_return(double.as_null_object) .and_return(double.as_null_object)
...@@ -163,6 +159,8 @@ module TestEnv ...@@ -163,6 +159,8 @@ module TestEnv
spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s spawn_script = Rails.root.join('scripts/gitaly-test-spawn').to_s
@gitaly_pid = Bundler.with_original_env { IO.popen([spawn_script], &:read).to_i } @gitaly_pid = Bundler.with_original_env { IO.popen([spawn_script], &:read).to_i }
Kernel.at_exit { stop_gitaly }
wait_gitaly wait_gitaly
end end
...@@ -309,7 +307,7 @@ module TestEnv ...@@ -309,7 +307,7 @@ module TestEnv
# Before we used Git clone's --mirror option, bare repos could end up # Before we used Git clone's --mirror option, bare repos could end up
# with missing refs, clearing them and retrying should fix the issue. # with missing refs, clearing them and retrying should fix the issue.
cleanup && clean_gitlab_test_path && init unless reset.call clean_gitlab_test_path && init unless reset.call
end end
end end
......
...@@ -47,6 +47,14 @@ describe RepositoryForkWorker do ...@@ -47,6 +47,14 @@ describe RepositoryForkWorker do
perform! perform!
end end
it 'protects the default branch' do
expect_fork_repository.and_return(true)
perform!
expect(fork_project.protected_branches.first.name).to eq(fork_project.default_branch)
end
it 'flushes various caches' do it 'flushes various caches' do
expect_fork_repository.and_return(true) expect_fork_repository.and_return(true)
......
alertmanager: | alertmanager:
enabled: false enabled: false
kubeStateMetrics: | kubeStateMetrics:
enabled: 'false' enabled: false
nodeExporter: | nodeExporter:
enabled: 'false' enabled: false
pushgateway: | pushgateway:
enabled: 'false' enabled: false
serverFiles: | serverFiles:
alerts: '' alerts: ""
rules: '' rules: ""
prometheus.yml: |- prometheus.yml: |-
rule_files: | rule_files:
- /etc/config/rules - /etc/config/rules
- /etc/config/alerts - /etc/config/alerts
scrape_configs: |
scrape_configs:
- job_name: prometheus - job_name: prometheus
static_configs: | static_configs:
- targets: - targets:
- localhost:9090 - localhost:9090
- job_name: 'kubernetes-apiservers' - job_name: 'kubernetes-apiservers'
kubernetes_sd_configs: |
kubernetes_sd_configs:
- role: endpoints - role: endpoints
scheme: https scheme: https
tls_config: tls_config:
...@@ -37,14 +40,17 @@ serverFiles: | ...@@ -37,14 +40,17 @@ serverFiles: |
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep action: keep
regex: default;kubernetes;https regex: default;kubernetes;https
- job_name: 'kubernetes-nodes' - job_name: 'kubernetes-nodes'
scheme: https scheme: https
tls_config: tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
kubernetes_sd_configs: kubernetes_sd_configs:
- role: node - role: node
relabel_configs: relabel_configs:
- action: labelmap - action: labelmap
regex: __meta_kubernetes_node_label_(.+) regex: __meta_kubernetes_node_label_(.+)
...@@ -54,14 +60,15 @@ serverFiles: | ...@@ -54,14 +60,15 @@ serverFiles: |
regex: (.+) regex: (.+)
target_label: __metrics_path__ target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics replacement: /api/v1/nodes/${1}/proxy/metrics
- job_name: 'kubernetes-service-endpoints' - job_name: 'kubernetes-service-endpoints'
kubernetes_sd_configs: kubernetes_sd_configs:
- role: endpoints - role: endpoints
relabel_configs: |
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep action: keep
regex: 'true' regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace action: replace
target_label: __scheme__ target_label: __scheme__
...@@ -83,24 +90,30 @@ serverFiles: | ...@@ -83,24 +90,30 @@ serverFiles: |
- source_labels: [__meta_kubernetes_service_name] - source_labels: [__meta_kubernetes_service_name]
action: replace action: replace
target_label: kubernetes_name target_label: kubernetes_name
- job_name: 'prometheus-pushgateway' - job_name: 'prometheus-pushgateway'
honor_labels: true honor_labels: true
kubernetes_sd_configs: |
kubernetes_sd_configs:
- role: service - role: service
relabel_configs: |
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
action: keep action: keep
regex: pushgateway regex: pushgateway
- job_name: 'kubernetes-services' - job_name: 'kubernetes-services'
metrics_path: /probe metrics_path: /probe
params: | params:
module: [http_2xx] module: [http_2xx]
kubernetes_sd_configs: |
kubernetes_sd_configs:
- role: service - role: service
relabel_configs: |
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe] - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
action: keep action: keep
regex: 'true' regex: true
- source_labels: [__address__] - source_labels: [__address__]
target_label: __param_target target_label: __param_target
- target_label: __address__ - target_label: __address__
...@@ -113,17 +126,25 @@ serverFiles: | ...@@ -113,17 +126,25 @@ serverFiles: |
target_label: kubernetes_namespace target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name] - source_labels: [__meta_kubernetes_service_name]
target_label: kubernetes_name target_label: kubernetes_name
- job_name: 'kubernetes-pods' - job_name: 'kubernetes-pods'
kubernetes_sd_configs: kubernetes_sd_configs:
- role: pod - role: pod
relabel_configs: relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape] - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep action: keep
regex: 'true' regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path] - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace action: replace
target_label: __metrics_path__ target_label: __metrics_path__
regex: (.+) regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: (.+):(?:\d+);(\d+)
replacement: ${1}:${2}
target_label: __address__
- action: labelmap - action: labelmap
regex: __meta_kubernetes_pod_label_(.+) regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace] - source_labels: [__meta_kubernetes_namespace]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment