Commit e262d9f2 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2017-12-04

# Conflicts:
#	app/services/ci/create_pipeline_service.rb
#	lib/gitlab/shell.rb
#	spec/javascripts/notes/components/issue_comment_form_spec.js
#	spec/services/projects/count_service_spec.rb

[ci skip]
parents ffe6e3f6 8f9622bb
source 'https://rubygems.org'
gem 'rails', '4.2.8'
gem 'rails', '4.2.10'
gem 'rails-deprecated_sanitizer', '~> 1.0.3'
# Responders respond_to and respond_with
......
......@@ -4,38 +4,38 @@ GEM
RedCloth (4.3.2)
abstract_type (0.0.7)
ace-rails-ap (4.1.2)
actionmailer (4.2.8)
actionpack (= 4.2.8)
actionview (= 4.2.8)
activejob (= 4.2.8)
actionmailer (4.2.10)
actionpack (= 4.2.10)
actionview (= 4.2.10)
activejob (= 4.2.10)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 1.0, >= 1.0.5)
actionpack (4.2.8)
actionview (= 4.2.8)
activesupport (= 4.2.8)
actionpack (4.2.10)
actionview (= 4.2.10)
activesupport (= 4.2.10)
rack (~> 1.6)
rack-test (~> 0.6.2)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (4.2.8)
activesupport (= 4.2.8)
actionview (4.2.10)
activesupport (= 4.2.10)
builder (~> 3.1)
erubis (~> 2.7.0)
rails-dom-testing (~> 1.0, >= 1.0.5)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (4.2.8)
activesupport (= 4.2.8)
activejob (4.2.10)
activesupport (= 4.2.10)
globalid (>= 0.3.0)
activemodel (4.2.8)
activesupport (= 4.2.8)
activemodel (4.2.10)
activesupport (= 4.2.10)
builder (~> 3.1)
activerecord (4.2.8)
activemodel (= 4.2.8)
activesupport (= 4.2.8)
activerecord (4.2.10)
activemodel (= 4.2.10)
activesupport (= 4.2.10)
arel (~> 6.0)
activerecord_sane_schema_dumper (0.2)
rails (>= 4, < 5)
activesupport (4.2.8)
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
......@@ -325,8 +325,8 @@ GEM
omniauth (~> 1.3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
rubyntlm (~> 0.5)
globalid (0.3.7)
activesupport (>= 4.1.0)
globalid (0.4.1)
activesupport (>= 4.2.0)
gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.2.7)
......@@ -427,7 +427,8 @@ GEM
json (~> 1.8)
multi_xml (>= 0.5.2)
httpclient (2.8.2)
i18n (0.8.6)
i18n (0.9.1)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
influxdb (0.2.3)
cause
......@@ -502,8 +503,8 @@ GEM
railties (>= 4, < 5.2)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.6)
mime-types (>= 1.16, < 4)
mail (2.7.0)
mini_mime (>= 0.1.1)
mail_room (0.9.1)
memoist (0.16.0)
memoizable (0.4.2)
......@@ -602,8 +603,8 @@ GEM
parallel (1.12.0)
paranoia (2.3.1)
activerecord (>= 4.0, < 5.2)
parser (2.4.0.0)
ast (~> 2.2)
parser (2.4.0.2)
ast (~> 2.3)
parslet (1.5.0)
blankslate (~> 2.0)
path_expander (1.0.1)
......@@ -685,16 +686,16 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.2.8)
actionmailer (= 4.2.8)
actionpack (= 4.2.8)
actionview (= 4.2.8)
activejob (= 4.2.8)
activemodel (= 4.2.8)
activerecord (= 4.2.8)
activesupport (= 4.2.8)
rails (4.2.10)
actionmailer (= 4.2.10)
actionpack (= 4.2.10)
actionview (= 4.2.10)
activejob (= 4.2.10)
activemodel (= 4.2.10)
activerecord (= 4.2.10)
activesupport (= 4.2.10)
bundler (>= 1.3.0, < 2.0)
railties (= 4.2.8)
railties (= 4.2.10)
sprockets-rails
rails-deprecated_sanitizer (1.0.3)
activesupport (>= 4.2.0.alpha)
......@@ -707,15 +708,15 @@ GEM
rails-i18n (4.0.9)
i18n (~> 0.7)
railties (~> 4.0)
railties (4.2.8)
actionpack (= 4.2.8)
activesupport (= 4.2.8)
railties (4.2.10)
actionpack (= 4.2.10)
activesupport (= 4.2.10)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
raindrops (0.18.0)
rake (12.1.0)
rake (12.3.0)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
rbnacl (4.0.2)
......@@ -902,7 +903,7 @@ GEM
sprockets (3.7.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.0)
sprockets-rails (3.2.1)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
......@@ -941,7 +942,7 @@ GEM
truncato (0.7.10)
htmlentities (~> 4.3.1)
nokogiri (~> 1.8.0, >= 1.7.0)
tzinfo (1.2.3)
tzinfo (1.2.4)
thread_safe (~> 0.1)
u2f (0.2.1)
uber (0.1.0)
......@@ -1156,7 +1157,7 @@ DEPENDENCIES
rack-cors (~> 0.4.0)
rack-oauth2 (~> 1.2.1)
rack-proxy (~> 0.6.0)
rails (= 4.2.8)
rails (= 4.2.10)
rails-deprecated_sanitizer (~> 1.0.3)
rails-i18n (~> 4.0.9)
rainbow (~> 2.2)
......
<script>
import tooltip from '../../vue_shared/directives/tooltip';
import identicon from '../../vue_shared/components/identicon.vue';
import eventHub from '../event_hub';
......@@ -8,6 +9,9 @@ import itemStats from './item_stats.vue';
import itemActions from './item_actions.vue';
export default {
directives: {
tooltip,
},
components: {
identicon,
itemCaret,
......@@ -112,10 +116,16 @@ export default {
</a>
</div>
<div
class="title">
class="title namespace-title">
<a
v-tooltip
:href="group.relativePath"
class="no-expand">{{group.fullName}}</a>
:title="group.fullName"
class="no-expand"
data-placement="top"
>
{{group.name}}
</a>
<span
v-if="group.permission"
class="access-type"
......
......@@ -4,7 +4,7 @@ import Poll from '../../lib/utils/poll';
import * as types from './mutation_types';
import * as utils from './utils';
import * as constants from '../constants';
import service from '../services/issue_notes_service';
import service from '../services/notes_service';
import loadAwardsHandler from '../../awards_handler';
import sidebarTimeTrackingEventHub from '../../sidebar/event_hub';
import { isInViewport, scrollToElement } from '../../lib/utils/common_utils';
......
......@@ -39,7 +39,6 @@
color: $brand-info;
}
.underlined-link { text-decoration: underline; }
.hint { font-style: italic; color: $hint-color; }
.light { color: $common-gray; }
......
......@@ -14,6 +14,5 @@
&:hover {
background-color: $user-mention-bg-hover;
text-decoration: none;
}
}
......@@ -455,6 +455,12 @@ ul.indent-list {
}
}
.namespace-title {
.tooltip-inner {
max-width: 350px;
}
}
ul.group-list-tree {
li.group-row {
&.has-description {
......
......@@ -134,19 +134,22 @@
}
.select2-search {
padding: 15px 15px 5px;
padding: $grid-size;
.select2-drop-auto-width & {
padding: 15px 15px 5px;
padding: $grid-size;
}
input {
padding: 2px 25px 2px 5px;
padding: $grid-size;
background: $white-light image-url('select2.png');
background-clip: content-box;
background-origin: content-box;
background-repeat: no-repeat;
background-position: right 0 bottom 6px;
background-position: right 0 bottom 0 !important;
border: 1px solid $input-border;
border-radius: $border-radius-default;
line-height: 16px;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
&:focus {
......@@ -156,11 +159,16 @@
&.select2-active {
background-color: $white-light;
background-image: image-url('select2-spinner.gif') !important;
background-origin: content-box;
background-repeat: no-repeat;
background-position: right 5px center !important;
background-position: right 6px center !important;
background-size: 16px 16px !important;
}
}
+ .select2-results {
padding-top: 0;
}
}
.select2-results {
......
......@@ -20,6 +20,11 @@
.ref-name {
font-size: 12px;
&:hover {
text-decoration: underline;
color: $gl-text-color;
}
}
}
......
......@@ -110,6 +110,10 @@
padding: 6px 10px;
border-radius: $label-border-radius;
}
&:hover .color-label {
text-decoration: underline;
}
}
&.has-labels {
......@@ -174,6 +178,14 @@
color: $gray-darkest;
}
}
&.assignee {
.author_link:hover {
.author {
text-decoration: underline;
}
}
}
}
.block-first {
......@@ -469,7 +481,6 @@
a:not(.btn-retry) {
&:hover {
color: $md-link-color;
text-decoration: none;
.avatar {
border-color: rgba($avatar-border, .2);
......
......@@ -208,7 +208,6 @@ ul.notes {
a {
color: $gl-link-color;
text-decoration: none;
}
p {
......@@ -395,6 +394,10 @@ ul.notes {
&:focus,
&:hover {
text-decoration: none;
.note-header-author-name {
text-decoration: underline;
}
}
}
......@@ -461,6 +464,10 @@ ul.notes {
.system-note-message {
white-space: normal;
}
a:hover {
text-decoration: underline;
}
}
/**
......
......@@ -73,7 +73,7 @@
.profile-link-holder {
display: inline;
a {
a:not(.text-link) {
text-decoration: none;
}
}
......
......@@ -818,6 +818,7 @@ a.deploy-project-label {
&:hover,
&:focus {
color: $gl-text-color;
text-decoration: underline;
}
}
}
......
......@@ -124,7 +124,11 @@
&:hover,
&.active {
color: $black;
text-decoration: none;
span {
text-decoration: underline;
}
}
}
......
......@@ -26,7 +26,7 @@ class UsersFinder
end
def execute
users = User.all
users = User.all.order_id_desc
users = by_username(users)
users = by_search(users)
users = by_blocked(users)
......
......@@ -63,7 +63,7 @@ module CommitsHelper
# Returns a link formatted as a commit branch link
def commit_branch_link(url, text)
link_to(url, class: 'label label-gray ref-name branch-link') do
icon('code-fork') + " #{text}"
icon('code-fork', class: 'append-right-5') + "#{text}"
end
end
......@@ -77,7 +77,7 @@ module CommitsHelper
# Returns a link formatted as a commit tag link
def commit_tag_link(url, text)
link_to(url, class: 'label label-gray ref-name') do
icon('tag') + " #{text}"
icon('tag', class: 'append-right-5') + "#{text}"
end
end
......
......@@ -113,7 +113,13 @@ module MarkupHelper
text = wiki_page.content
return '' unless text.present?
context = { pipeline: :wiki, project: @project, project_wiki: @project_wiki, page_slug: wiki_page.slug }
context = {
pipeline: :wiki,
project: @project,
project_wiki: @project_wiki,
page_slug: wiki_page.slug,
issuable_state_filter_enabled: true
}
html =
case wiki_page.format
......
......@@ -52,7 +52,6 @@ module Ci
validates :status, presence: { unless: :importing? }
validate :valid_commit_sha, unless: :importing?
after_initialize :set_config_source, if: :new_record?
after_create :keep_around_commits, unless: :importing?
enum source: {
......
......@@ -239,8 +239,8 @@ class Project < ActiveRecord::Base
validates :creator, presence: true, on: :create
validates :description, length: { maximum: 2000 }, allow_blank: true
validates :ci_config_path,
format: { without: /\.{2}/,
message: 'cannot include directory traversal.' },
format: { without: /(\.{2}|\A\/)/,
message: 'cannot include leading slash or directory traversal.' },
length: { maximum: 255 },
allow_blank: true
validates :name,
......@@ -606,7 +606,7 @@ class Project < ActiveRecord::Base
def ci_config_path=(value)
# Strip all leading slashes so that //foo -> foo
super(value&.sub(%r{\A/+}, '')&.delete("\0"))
super(value&.delete("\0"))
end
def import_url=(value)
......
......@@ -263,7 +263,7 @@ class Repository
end
def diverging_commit_counts(branch)
root_ref_hash = raw_repository.rev_parse_target(root_ref).oid
root_ref_hash = raw_repository.commit(root_ref).id
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes
......
......@@ -506,7 +506,11 @@ class User < ActiveRecord::Base
end
def two_factor_u2f_enabled?
u2f_registrations.exists?
if u2f_registrations.loaded?
u2f_registrations.any?
else
u2f_registrations.exists?
end
end
def namespace_uniq
......
......@@ -2,7 +2,8 @@ module Ci
class CreatePipelineService < BaseService
attr_reader :pipeline
SEQUENCE = [Gitlab::Ci::Pipeline::Chain::Validate::Abilities,
SEQUENCE = [Gitlab::Ci::Pipeline::Chain::Build,
Gitlab::Ci::Pipeline::Chain::Validate::Abilities,
Gitlab::Ci::Pipeline::Chain::Validate::Repository,
Gitlab::Ci::Pipeline::Chain::Validate::Config,
Gitlab::Ci::Pipeline::Chain::Skip,
......@@ -10,6 +11,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Create,
EE::Gitlab::Ci::Pipeline::Chain::Limit::Activity].freeze
<<<<<<< HEAD
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, mirror_update: false, &block)
@pipeline = Ci::Pipeline.new(
source: source,
......@@ -26,6 +28,19 @@ module Ci
# VALIDATE mirror_update!
command = OpenStruct.new(ignore_skip_ci: ignore_skip_ci,
=======
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, &block)
@pipeline = Ci::Pipeline.new
command = OpenStruct.new(source: source,
origin_ref: params[:ref],
checkout_sha: params[:checkout_sha],
after_sha: params[:after],
before_sha: params[:before],
trigger_request: trigger_request,
schedule: schedule,
ignore_skip_ci: ignore_skip_ci,
>>>>>>> upstream/master
save_incompleted: save_on_errors,
allow_mirror_update: mirror_update,
seeds_block: block,
......@@ -49,14 +64,6 @@ module Ci
private
def commit
@commit ||= project.commit(origin_sha || origin_ref)
end
def sha
commit.try(:id)
end
def update_merge_requests_head_pipeline
return unless pipeline.latest?
......@@ -80,26 +87,6 @@ module Ci
.created_or_pending
end
def before_sha
params[:checkout_sha] || params[:before] || Gitlab::Git::BLANK_SHA
end
def origin_sha
params[:checkout_sha] || params[:after]
end
def origin_ref
params[:ref]
end
def tag_exists?
project.repository.tag_exists?(ref)
end
def ref
@ref ||= Gitlab::Git.ref_name(origin_ref)
end
def pipeline_created_counter
@pipeline_created_counter ||= Gitlab::Metrics
.counter(:pipelines_created_total, "Counter of pipelines created")
......
......@@ -98,6 +98,12 @@ module NotificationRecipientService
self << [target.participants(user), :participating]
end
def add_mentions(user, target:)
return unless target.respond_to?(:mentioned_users)
self << [target.mentioned_users(user), :mention]
end
# Get project/group users with CUSTOM notification level
def add_custom_notifications
user_ids = []
......@@ -227,6 +233,11 @@ module NotificationRecipientService
add_subscribed_users
if [:new_issue, :new_merge_request].include?(custom_action)
# These will all be participants as well, but adding with the :mention
# type ensures that users with the mention notification level will
# receive them, too.
add_mentions(current_user, target: target)
add_labels_subscribers
end
end
......@@ -263,7 +274,7 @@ module NotificationRecipientService
def build!
# Add all users participating in the thread (author, assignee, comment authors)
add_participants(note.author)
self << [note.mentioned_users, :mention]
add_mentions(note.author, target: note)
unless note.for_personal_snippet?
# Merge project watchers
......
......@@ -282,7 +282,7 @@
= render 'projects/settings/ee/nav'
- else
= nav_link(path: %w[members#show]) do
= nav_link(controller: :project_members) do
= link_to project_settings_members_path(@project), title: 'Members', class: 'shortcuts-tree' do
.nav-icon-container
= sprite_icon('users')
......
......@@ -19,5 +19,5 @@
distributed with computer software, forming part of its documentation.
%p
We recommend you to
= link_to "add a README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link'
= link_to "add a README", add_special_file_path(@project, file_name: 'README.md')
file to the repository and GitLab will render it here instead of this message.
......@@ -3,6 +3,6 @@
%tr.tree-item{ 'data-link' => path_to_directory }
%td.tree-item-file-name
= tree_icon('folder', '755', directory.name)
= link_to path_to_directory do
%span.str-truncated= directory.name
= link_to path_to_directory, class: 'str-truncated' do
%span= directory.name
%td
......@@ -6,12 +6,12 @@
%td.tree-item-file-name
= tree_icon('file', blob.mode, blob.name)
- if external_link
= link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip',
= link_to path_to_file, class: 'tree-item-file-external-link js-artifact-tree-tooltip str-truncated',
target: '_blank', rel: 'noopener noreferrer', title: _('Opens in a new window') do
%span.str-truncated>= blob.name
%span>= blob.name
= icon('external-link', class: 'js-artifact-tree-external-icon')
- else
= link_to path_to_file do
%span.str-truncated= blob.name
= link_to path_to_file, class: 'str-truncated' do
%span= blob.name
%td
= number_to_human_size(blob.size, precision: 2)
......@@ -8,8 +8,7 @@
%li{ class: "js-branch-#{branch.name}" }
%div
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated ref-name' do
= icon('code-fork')
= branch.name
= icon('code-fork', class: 'append-right-5') + "#{branch.name}"
&nbsp;
- if branch.name == @repository.root_ref
%span.label.label-primary default
......
......@@ -48,6 +48,6 @@
- if commit.status(ref)
= render_commit_status(commit, ref: ref)
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent"
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent btn-link"
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit)
......@@ -14,12 +14,12 @@
%p
Otherwise you can start with adding a
= succeed ',' do
= link_to "README", add_special_file_path(@project, file_name: 'README.md'), class: 'underlined-link'
= link_to "README", add_special_file_path(@project, file_name: 'README.md')
a
= succeed ',' do
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE'), class: 'underlined-link'
= link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE')
or a
= link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore'), class: 'underlined-link'
= link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore')
to this project.
%p
You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected.
......
......@@ -16,7 +16,7 @@
%li prevent pushes from everybody except Masters
%li prevent <strong>anyone</strong> from force pushing to the branch
%li prevent <strong>anyone</strong> from deleting the branch
%p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}.
%p Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches")} and #{link_to "project permissions", help_page_path("user/permissions")}.
- if can? current_user, :admin_project, @project
= content_for :create_protected_branch
......
......@@ -16,7 +16,7 @@
%li Prevent <strong>anyone</strong> from updating the tag
%li Prevent <strong>anyone</strong> from deleting the tag
%p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags"), class: "underlined-link"}.
%p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags")}.
- if can? current_user, :admin_project, @project
= yield :create_protected_tag
......
......@@ -2,8 +2,8 @@
%td.tree-item-file-name
= tree_icon(type, blob_item.mode, blob_item.name)
- file_name = blob_item.name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), title: file_name do
%span.str-truncated= file_name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
%span= file_name
%td.hidden-xs.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'
......@@ -2,8 +2,8 @@
%td.tree-item-file-name
= tree_icon(type, tree_item.mode, tree_item.name)
- path = flatten_tree(@path, tree_item)
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), title: path do
%span.str-truncated= path
= link_to project_tree_path(@project, tree_join(@id || @commit.id, path)), class: 'str-truncated', title: path do
%span= path
%td.hidden-xs.tree-commit
%td.tree-time-ago.text-right
= render 'projects/tree/spinner'
......@@ -6,9 +6,8 @@
- git_access_url = project_wikis_git_access_path(@project)
= link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do
= succeed '&nbsp;' do
= icon('cloud-download')
= _("Clone repository")
= icon('cloud-download', class: 'append-right-5')
%span= _("Clone repository")
.blocks-container
.block.block-first
......
......@@ -38,9 +38,9 @@
= link_to 'Delete', destroy_label_path(label), title: 'Delete', method: :delete, data: {confirm: 'Remove this label? Are you sure?'}
.pull-right.hidden-xs.hidden-sm.hidden-md
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action') do
= link_to_label(label, subject: subject, type: :merge_request, css_class: 'btn btn-transparent btn-action btn-link') do
view merge requests
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action') do
= link_to_label(label, subject: subject, css_class: 'btn btn-transparent btn-action btn-link') do
view open issues
- if current_user
......
......@@ -31,8 +31,7 @@
.note-header
.note-header-info
%a{ href: user_path(note.author) }
%span.note-header-author-name
= sanitize(note.author.name)
%span.note-header-author-name= sanitize(note.author.name)
%span.note-headline-light
= note.author.to_reference
%span.note-headline-light
......
......@@ -51,7 +51,7 @@
.cover-desc
- unless @user.public_email.blank?
.profile-link-holder.middle-dot-divider
= link_to @user.public_email, "mailto:#{@user.public_email}"
= link_to @user.public_email, "mailto:#{@user.public_email}", class: 'text-link'
- unless @user.skype.blank?
.profile-link-holder.middle-dot-divider
= link_to "skype:#{@user.skype}", title: "Skype" do
......@@ -66,7 +66,7 @@
= icon('twitter-square')
- unless @user.website_url.blank?
.profile-link-holder.middle-dot-divider
= link_to @user.short_website_url, @user.full_website_url
= link_to @user.short_website_url, @user.full_website_url, class: 'text-link'
- unless @user.location.blank?
.profile-link-holder.middle-dot-divider
= icon('map-marker')
......
......@@ -32,16 +32,14 @@ module RepositoryCheck
end
def git_fsck(repository)
path = repository.path_to_repo
cmd = %W(nice git --git-dir=#{path} fsck)
output, status = Gitlab::Popen.popen(cmd)
return false unless repository.exists?
if status.zero?
true
else
Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}")
false
end
repository.raw_repository.fsck
true
rescue Gitlab::Git::Repository::GitError => e
Gitlab::RepositoryCheckLogger.error(e.message)
false
end
def has_pushes?(project)
......
---
title: Fix sending notification emails to users with the mention level set who were
mentioned in an issue or merge request description
merge_request:
author:
type: fixed
---
title: show status of gitlab reference links in wiki
merge_request: 15694
author: haseebeqx
type: added
---
title: Show only group name by default and put full namespace in tooltip in Groups
tree
merge_request: 15650
author:
type: changed
---
title: Fix typo in docs about Elasticsearch
merge_request: 15699
author: Takuya Noguchi
type: other
---
title: Prefer ci_config_path validation for leading slashes instead of sanitizing
the input
merge_request: 15672
author: Christiaan Van den Poel
type: other
---
title: Keep track of all circuitbreaker keys in a set
merge_request: 15613
author:
type: performance
---
title: Added default order to UsersFinder
merge_request: 15679
author:
type: fixed
---
title: "Gracefully handle case when repository's root ref does not exist"
merge_request:
author:
type: fixed
# WARNING changes in this file must be manually propagated to gitaly-ruby.
#
# https://gitlab.com/gitlab-org/gitaly/blob/master/ruby/lib/gitlab/gollum.rb
module Gollum
GIT_ADAPTER = "rugged".freeze
end
......
......@@ -80,7 +80,7 @@ changes.
The first example focuses on _how_ we fixed something, not on _what_ it fixes.
The rewritten version clearly describes the _end benefit_ to the user (fewer 500
errors), and _when_ (searching commits with ElasticSearch).
errors), and _when_ (searching commits with Elasticsearch).
Use your best judgement and try to put yourself in the mindset of someone
reading the compiled changelog. Does this entry add value? Does it offer context
......
......@@ -170,12 +170,6 @@ You can combine one or more of the following:
= link_to 'Help page', help_page_path('user/permissions'), class: 'btn btn-info'
```
1. **Underlining a link.**
```haml
= link_to 'Help page', help_page_path('user/permissions'), class: 'underlined-link'
```
1. **Using links inline of some text.**
```haml
......
......@@ -175,7 +175,7 @@ A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of
### EC2 Instance
### ElasticSearch
### Elasticsearch
Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data.
......
......@@ -41,7 +41,7 @@ Line-breaks, or softreturns, are rendered if you end a line with two or more spa
Sugar is sweet
Roses are red
Roses are red
Violets are blue
Sugar is sweet
......@@ -370,14 +370,17 @@ This also works for the asciidoctor `:stem: latexmath`. For details see the [asc
### Mermaid
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15107) in
GitLab 10.3.
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#mermaid
It is possible to generate diagrams and flowcharts from text using [Mermaid][mermaid].
It is possible to generate diagrams and flowcharts from text using [Mermaid][mermaid].
In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block.
In order to generate a diagram or flowchart, you should write your text inside the `mermaid` block.
Example:
Example:
```mermaid
graph TD;
......@@ -385,7 +388,7 @@ Example:
A-->C;
B-->D;
C-->D;
```
```
Becomes:
......@@ -395,7 +398,7 @@ graph TD;
A-->C;
B-->D;
C-->D;
```
```
For details see the [Mermaid official page][mermaid].
......@@ -697,7 +700,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa
This line is also a separate paragraph, but...
This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
This line is also a separate paragraph, and...
This line is also a separate paragraph, and...
This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
spaces.
......@@ -710,7 +713,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa
This line is also a separate paragraph, but...
This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
This line is also a separate paragraph, and...
This line is also a separate paragraph, and...
This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
spaces.
......
# Redmine Service
To enable the Redmine integration in a project, navigate to the
1. To enable the Redmine integration in a project, navigate to the
[Integrations page](project_services.md#accessing-the-project-services), click
the **Redmine** service, and fill in the required details on the page as described
in the table below.
| Field | Description |
| ----- | ----------- |
| `description` | A name for the issue tracker (to differentiate between instances, for example) |
| `project_url` | The URL to the project in Redmine which is being linked to this GitLab project |
| `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project |
| Field | Description |
| ----- | ----------- |
| `description` | A name for the issue tracker (to differentiate between instances, for example) |
| `project_url` | The URL to the project in Redmine which is being linked to this GitLab project |
| `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project |
Once you have configured and enabled Redmine:
Once you have configured and enabled Redmine:
- the **Issues** link on the GitLab project pages takes you to the appropriate
Redmine issue index
- clicking **New issue** on the project dashboard creates a new Redmine issue
- the **Issues** link on the GitLab project pages takes you to the appropriate
Redmine issue index
- clicking **New issue** on the project dashboard creates a new Redmine issue
As an example, below is a configuration for a project named gitlab-ci.
As an example, below is a configuration for a project named gitlab-ci.
![Redmine configuration](img/redmine_configuration.png)
![Redmine configuration](img/redmine_configuration.png)
2. To disable the internal issue tracking system in a project, navigate to the General page, expand [Permissions](../settings/index.md#sharing-and-permissions), and slide the Issues switch invalid.
![Issue configuration](img/issue_configuration.png)
## Referencing issues in Redmine
......
......@@ -3,21 +3,41 @@ require 'capybara-screenshot/spinach'
# Give CI some extra time
timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30
Capybara.javascript_driver = :chrome
Capybara.register_driver :chrome do |app|
extra_args = []
extra_args << 'headless' unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
'args' => %w[no-sandbox disable-gpu --window-size=1240,1400] + extra_args
# This enables access to logs with `page.driver.manage.get_log(:browser)`
loggingPrefs: {
browser: "ALL",
client: "ALL",
driver: "ALL",
server: "ALL"
}
)
Capybara::Selenium::Driver
.new(app, browser: :chrome, desired_capabilities: capabilities)
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("window-size=1240,1400")
# Chrome won't work properly in a Docker container in sandbox mode
options.add_argument("no-sandbox")
# Run headless by default unless CHROME_HEADLESS specified
unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i
options.add_argument("headless")
# Chrome documentation says this flag is needed for now
# https://developers.google.com/web/updates/2017/04/headless-chrome#cli
options.add_argument("disable-gpu")
end
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: capabilities,
options: options
)
end
Capybara.javascript_driver = :chrome
Capybara.default_max_wait_time = timeout
Capybara.ignore_hidden_elements = false
......
......@@ -2,6 +2,8 @@ module API
module Helpers
module Pagination
def paginate(relation)
relation = add_default_order(relation)
relation.page(params[:page]).per(params[:per_page]).tap do |data|
add_pagination_headers(data)
end
......@@ -45,6 +47,14 @@ module API
# Ensure there is in total at least 1 page
[paginated_data.total_pages, 1].max
end
def add_default_order(relation)
if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty?
relation = relation.order(:id)
end
relation
end
end
end
end
......@@ -82,6 +82,8 @@ module API
forbidden!("Not authorized to access /api/v4/users") unless authorized
entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
present paginate(users), with: entity
end
......
module Gitlab
module Ci
module Pipeline
module Chain
class Build < Chain::Base
include Chain::Helpers
def perform!
@pipeline.assign_attributes(
source: @command.source,
project: @project,
ref: ref,
sha: sha,
before_sha: before_sha,
tag: tag_exists?,
trigger_requests: Array(@command.trigger_request),
user: @current_user,
pipeline_schedule: @command.schedule,
protected: protected_ref?
)
@pipeline.set_config_source
end
def break?
false
end
private
def ref
@ref ||= Gitlab::Git.ref_name(origin_ref)
end
def sha
@project.commit(origin_sha || origin_ref).try(:id)
end
def origin_ref
@command.origin_ref
end
def origin_sha
@command.checkout_sha || @command.after_sha
end
def before_sha
@command.checkout_sha || @command.before_sha || Gitlab::Git::BLANK_SHA
end
def protected_ref?
@project.protected_for?(ref)
end
end
end
end
end
end
......@@ -1119,9 +1119,11 @@ module Gitlab
end
# Refactoring aid; allows us to copy code from app/models/repository.rb
def run_git(args, env: {})
def run_git(args, env: {}, nice: false)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
circuit_breaker.perform do
popen([Gitlab.config.git.bin_path, *args], path, env)
popen(cmd, path, env)
end
end
......@@ -1201,6 +1203,12 @@ module Gitlab
end
end
def fsck
output, status = run_git(%W[--git-dir=#{path} fsck], nice: true)
raise GitError.new("Could not fsck repository:\n#{output}") unless status.zero?
end
def gitaly_repository
Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository)
end
......@@ -1267,7 +1275,11 @@ module Gitlab
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/695
def git_merged_branch_names(branch_names = [])
root_sha = find_branch(root_ref).target
return [] unless root_ref
root_sha = find_branch(root_ref)&.target
return [] unless root_sha
git_arguments =
%W[branch --merged #{root_sha}
......
......@@ -15,6 +15,7 @@ module Gitlab
Failing = Class.new(Inaccessible)
REDIS_KEY_PREFIX = 'storage_accessible:'.freeze
REDIS_KNOWN_KEYS = "#{REDIS_KEY_PREFIX}known_keys_set".freeze
def self.redis
Gitlab::Redis::SharedState
......
......@@ -13,10 +13,8 @@ module Gitlab
delegate :last_failure, :failure_count, to: :failure_info
def self.reset_all!
pattern = "#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}*"
Gitlab::Git::Storage.redis.with do |redis|
all_storage_keys = redis.scan_each(match: pattern).to_a
all_storage_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
redis.del(*all_storage_keys) unless all_storage_keys.empty?
end
......@@ -135,23 +133,29 @@ module Gitlab
redis.hset(cache_key, :last_failure, last_failure.to_i)
redis.hincrby(cache_key, :failure_count, 1)
redis.expire(cache_key, failure_reset_time)
maintain_known_keys(redis)
end
end
end
def track_storage_accessible
return if no_failures?
@failure_info = FailureInfo.new(nil, 0)
Gitlab::Git::Storage.redis.with do |redis|
redis.pipelined do
redis.hset(cache_key, :last_failure, nil)
redis.hset(cache_key, :failure_count, 0)
maintain_known_keys(redis)
end
end
end
def maintain_known_keys(redis)
expire_time = Time.now.to_i + failure_reset_time
redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, expire_time, cache_key)
redis.zremrangebyscore(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, '-inf', Time.now.to_i)
end
def get_failure_info
last_failure, failure_count = Gitlab::Git::Storage.redis.with do |redis|
redis.hmget(cache_key, :last_failure, :failure_count)
......
......@@ -4,8 +4,8 @@ module Gitlab
class Health
attr_reader :storage_name, :info
def self.pattern_for_storage(storage_name)
"#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage_name}:*"
def self.prefix_for_storage(storage_name)
"#{Gitlab::Git::Storage::REDIS_KEY_PREFIX}#{storage_name}:"
end
def self.for_all_storages
......@@ -25,26 +25,15 @@ module Gitlab
private_class_method def self.all_keys_for_storages(storage_names, redis)
keys_per_storage = {}
all_keys = redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
redis.pipelined do
storage_names.each do |storage_name|
pattern = pattern_for_storage(storage_name)
matched_keys = redis.scan_each(match: pattern)
storage_names.each do |storage_name|
prefix = prefix_for_storage(storage_name)
keys_per_storage[storage_name] = matched_keys
end
keys_per_storage[storage_name] = all_keys.select { |key| key.starts_with?(prefix) }
end
# We need to make sure each lazy-loaded `Enumerator` for matched keys
# is loaded into an array.
#
# Otherwise it would be loaded in the second `Redis#pipelined` block
# within `.load_for_keys`. In this pipelined call, the active
# Redis-client changes again, so the values would not be available
# until the end of that pipelined-block.
keys_per_storage.each do |storage_name, key_future|
keys_per_storage[storage_name] = key_future.to_a
end
keys_per_storage
end
private_class_method def self.load_for_keys(keys_per_storage, redis)
......
......@@ -18,7 +18,7 @@ module Gitlab
def initialize(kubeclient)
@kubeclient = kubeclient
@namespace = Namespace.new(NAMESPACE, kubeclient)
@namespace = Gitlab::Kubernetes::Namespace.new(NAMESPACE, kubeclient)
end
def install(command)
......
......@@ -144,6 +144,7 @@ module Gitlab
storage, "#{path}.git", "#{new_path}.git"])
end
<<<<<<< HEAD
# Move repository storage
#
# current_storage - project's current storage path
......@@ -158,6 +159,8 @@ module Gitlab
current_storage, "#{path}.git", new_storage])
end
=======
>>>>>>> upstream/master
# Fork repository to new path
# forked_from_storage - forked-from project's storage path
# forked_from_disk_path - project disk path
......
......@@ -30,12 +30,16 @@ module Omnibus
private
def ee?
File.exist?('CHANGELOG-EE.md')
end
def env_params
{
"ref" => ENV["OMNIBUS_BRANCH"] || "master",
"variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
"variables[ALTERNATIVE_SOURCES]" => true,
"variables[ee]" => ENV["EE_PACKAGE"] || "false"
"variables[ee]" => ee? ? 'true' : 'false'
}
end
......
......@@ -14,7 +14,7 @@ describe 'Discussion Lock', :js do
project.add_developer(user)
end
context 'when the discussion is unlocked' do
context 'when the discussion is unlocked' do
it 'the user can lock the issue' do
visit project_issue_path(project, issue)
......
......@@ -205,7 +205,7 @@ describe MarkupHelper do
it "uses Wiki pipeline for markdown files" do
allow(@wiki).to receive(:format).and_return(:markdown)
expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page")
expect(helper).to receive(:markdown_unsafe).with('wiki content', pipeline: :wiki, project: project, project_wiki: @wiki, page_slug: "nested/page", issuable_state_filter_enabled: true)
helper.render_wiki_content(@wiki)
end
......
/* eslint-disable space-before-function-paren, no-var */
import '~/behaviors/requires_input';
(function() {
describe('requiresInput', function() {
preloadFixtures('branches/new_branch.html.raw');
beforeEach(function() {
loadFixtures('branches/new_branch.html.raw');
this.submitButton = $('button[type="submit"]');
});
it('disables submit when any field is required', function() {
$('.js-requires-input').requiresInput();
return expect(this.submitButton).toBeDisabled();
});
it('enables submit when no field is required', function() {
$('*[required=required]').removeAttr('required');
$('.js-requires-input').requiresInput();
return expect(this.submitButton).not.toBeDisabled();
});
it('enables submit when all required fields are pre-filled', function() {
$('*[required=required]').remove();
$('.js-requires-input').requiresInput();
return expect($('.submit')).not.toBeDisabled();
});
it('enables submit when all required fields receive input', function() {
$('.js-requires-input').requiresInput();
$('#required1').val('input1').change();
expect(this.submitButton).toBeDisabled();
$('#optional1').val('input1').change();
expect(this.submitButton).toBeDisabled();
$('#required2').val('input2').change();
$('#required3').val('input3').change();
$('#required4').val('input4').change();
$('#required5').val('1').change();
return expect($('.submit')).not.toBeDisabled();
});
describe('requiresInput', () => {
let submitButton;
preloadFixtures('branches/new_branch.html.raw');
beforeEach(() => {
loadFixtures('branches/new_branch.html.raw');
submitButton = $('button[type="submit"]');
});
it('disables submit when any field is required', () => {
$('.js-requires-input').requiresInput();
expect(submitButton).toBeDisabled();
});
it('enables submit when no field is required', () => {
$('*[required=required]').removeAttr('required');
$('.js-requires-input').requiresInput();
expect(submitButton).not.toBeDisabled();
});
it('enables submit when all required fields are pre-filled', () => {
$('*[required=required]').remove();
$('.js-requires-input').requiresInput();
expect($('.submit')).not.toBeDisabled();
});
it('enables submit when all required fields receive input', () => {
$('.js-requires-input').requiresInput();
$('#required1').val('input1').change();
expect(submitButton).toBeDisabled();
$('#optional1').val('input1').change();
expect(submitButton).toBeDisabled();
$('#required2').val('input2').change();
$('#required3').val('input3').change();
$('#required4').val('input4').change();
$('#required5').val('1').change();
expect($('.submit')).not.toBeDisabled();
});
}).call(window);
});
......@@ -2,7 +2,11 @@ import Vue from 'vue';
import Autosize from 'autosize';
import store from '~/notes/stores';
import issueCommentForm from '~/notes/components/issue_comment_form.vue';
<<<<<<< HEAD
import { loggedOutIssueData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data';
=======
import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data';
>>>>>>> upstream/master
import { keyboardDownEvent } from '../../issue_show/helpers';
describe('issue_comment_form component', () => {
......@@ -190,7 +194,11 @@ describe('issue_comment_form component', () => {
describe('user is not logged in', () => {
beforeEach(() => {
store.dispatch('setUserData', null);
<<<<<<< HEAD
store.dispatch('setNoteableData', loggedOutIssueData);
=======
store.dispatch('setNoteableData', loggedOutnoteableData);
>>>>>>> upstream/master
store.dispatch('setNotesData', notesDataMock);
vm = mountComponent();
......
import Vue from 'vue';
import issueNotesApp from '~/notes/components/issue_notes_app.vue';
import service from '~/notes/services/issue_notes_service';
import service from '~/notes/services/notes_service';
import * as mockData from '../mock_data';
describe('issue_note_app', () => {
......
......@@ -271,7 +271,7 @@ export const discussionMock = {
individual_note: false,
};
export const loggedOutIssueData = {
export const loggedOutnoteableData = {
"id": 98,
"iid": 26,
"author_id": 1,
......
......@@ -92,6 +92,27 @@ describe API::Helpers::Pagination do
subject.paginate(resource)
end
end
context 'if order' do
it 'is not present it adds default order(:id) if no order is present' do
resource.order_values = []
paginated_relation = subject.paginate(resource)
expect(resource.order_values).to be_empty
expect(paginated_relation.order_values).to be_present
expect(paginated_relation.order_values.first).to be_ascending
expect(paginated_relation.order_values.first.expr.name).to eq :id
end
it 'is present it does not add anything' do
paginated_relation = subject.paginate(resource.order(created_at: :desc))
expect(paginated_relation.order_values).to be_present
expect(paginated_relation.order_values.first).to be_descending
expect(paginated_relation.order_values.first.expr.name).to eq :created_at
end
end
end
context 'when resource empty' do
......
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Build do
set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
let(:pipeline) { Ci::Pipeline.new }
let(:command) do
double('command', source: :push,
origin_ref: 'master',
checkout_sha: project.commit.id,
after_sha: nil,
before_sha: nil,
trigger_request: nil,
schedule: nil,
project: project,
current_user: user)
end
let(:step) { described_class.new(pipeline, command) }
before do
stub_repository_ci_yaml_file(sha: anything)
step.perform!
end
it 'never breaks the chain' do
expect(step.break?).to be false
end
it 'fills pipeline object with data' do
expect(pipeline.sha).not_to be_empty
expect(pipeline.sha).to eq project.commit.id
expect(pipeline.ref).to eq 'master'
expect(pipeline.user).to eq user
expect(pipeline.project).to eq project
end
it 'sets a valid config source' do
expect(pipeline.repository_source?).to be true
end
it 'returns a valid pipeline' do
expect(pipeline).to be_valid
end
it 'does not persist a pipeline' do
expect(pipeline).not_to be_persisted
end
end
......@@ -1210,6 +1210,16 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
context 'when no root ref is available' do
it 'returns empty list' do
project = create(:project, :empty_repo)
names = project.repository.merged_branch_names(%w[feature])
expect(names).to be_empty
end
end
context 'when no branch names are specified' do
before do
repository.create_branch('identical', 'master')
......
......@@ -27,6 +27,7 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
def set_in_redis(name, value)
Gitlab::Git::Storage.redis.with do |redis|
redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key)
redis.hmset(cache_key, name, value)
end.first
end
......@@ -181,6 +182,24 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
expect(circuit_breaker.last_failure).to be_nil
end
it 'maintains known storage keys' do
Timecop.freeze do
# Insert an old key to expire
old_entry = Time.now.to_i - 3.days.to_i
Gitlab::Git::Storage.redis.with do |redis|
redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, old_entry, 'to_be_removed')
end
circuit_breaker.perform { '' }
known_keys = Gitlab::Git::Storage.redis.with do |redis|
redis.zrange(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, -1)
end
expect(known_keys).to contain_exactly(cache_key)
end
end
it 'only performs the accessibility check once' do
expect(Gitlab::Git::Storage::ForkedStorageCheck)
.to receive(:storage_available?).once.and_call_original
......
......@@ -6,6 +6,7 @@ describe Gitlab::Git::Storage::Health, clean_gitlab_redis_shared_state: true, br
def set_in_redis(cache_key, value)
Gitlab::Git::Storage.redis.with do |redis|
redis.zadd(Gitlab::Git::Storage::REDIS_KNOWN_KEYS, 0, cache_key)
redis.hmset(cache_key, :failure_count, value)
end.first
end
......
......@@ -717,7 +717,7 @@ describe Notify do
it 'has the correct subject and body' do
aggregate_failures do
is_expected.to have_subject("Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})")
is_expected.to have_subject("Re: #{project.name} | #{commit.title} (#{commit.short_id})")
is_expected.to have_body_text(commit.short_id)
end
end
......@@ -827,7 +827,7 @@ describe Notify do
it_behaves_like 'a user cannot unsubscribe through footer link'
it 'has the correct subject' do
is_expected.to have_subject "Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})"
is_expected.to have_subject "Re: #{project.name} | #{commit.title} (#{commit.short_id})"
end
it 'contains a link to the commit' do
......
......@@ -872,62 +872,59 @@ describe Ci::Pipeline, :mailer do
end
describe '#set_config_source' do
context 'on object initialisation' do
context 'when pipelines does not contain needed data' do
let(:pipeline) do
Ci::Pipeline.new
end
context 'when pipelines does not contain needed data' do
it 'defines source to be unknown' do
pipeline.set_config_source
it 'defines source to be unknown' do
expect(pipeline).to be_unknown_source
end
expect(pipeline).to be_unknown_source
end
end
context 'when pipeline contains all needed data' do
let(:pipeline) do
Ci::Pipeline.new(
project: project,
sha: '1234',
ref: 'master',
source: :push)
context 'when pipeline contains all needed data' do
let(:pipeline) do
create(:ci_pipeline, project: project,
sha: '1234',
ref: 'master',
source: :push)
end
context 'when the repository has a config file' do
before do
allow(project.repository).to receive(:gitlab_ci_yml_for)
.and_return('config')
end
context 'when the repository has a config file' do
before do
allow(project.repository).to receive(:gitlab_ci_yml_for)
.and_return('config')
end
it 'defines source to be from repository' do
pipeline.set_config_source
it 'defines source to be from repository' do
expect(pipeline).to be_repository_source
end
expect(pipeline).to be_repository_source
end
context 'when loading an object' do
let(:new_pipeline) { Ci::Pipeline.find(pipeline.id) }
context 'when loading an object' do
let(:new_pipeline) { Ci::Pipeline.find(pipeline.id) }
it 'does not redefine the source' do
# force to overwrite the source
pipeline.unknown_source!
it 'does not redefine the source' do
# force to overwrite the source
pipeline.unknown_source!
expect(new_pipeline).to be_unknown_source
end
expect(new_pipeline).to be_unknown_source
end
end
end
context 'when the repository does not have a config file' do
let(:implied_yml) { Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content }
context 'when the repository does not have a config file' do
let(:implied_yml) { Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps').content }
context 'auto devops enabled' do
before do
stub_application_setting(auto_devops_enabled: true)
allow(project).to receive(:ci_config_path) { 'custom' }
end
context 'auto devops enabled' do
before do
stub_application_setting(auto_devops_enabled: true)
allow(project).to receive(:ci_config_path) { 'custom' }
end
it 'defines source to be auto devops' do
subject
it 'defines source to be auto devops' do
pipeline.set_config_source
expect(pipeline).to be_auto_devops_source
end
expect(pipeline).to be_auto_devops_source
end
end
end
......
......@@ -142,6 +142,7 @@ describe Project do
it { is_expected.to validate_length_of(:ci_config_path).is_at_most(255) }
it { is_expected.to allow_value('').for(:ci_config_path) }
it { is_expected.not_to allow_value('test/../foo').for(:ci_config_path) }
it { is_expected.not_to allow_value('/test/foo').for(:ci_config_path) }
it { is_expected.to validate_presence_of(:creator) }
......@@ -1713,8 +1714,8 @@ describe Project do
expect(project.ci_config_path).to eq('foo/.gitlab_ci.yml')
end
it 'sets a string but removes all leading slashes and null characters' do
project.update!(ci_config_path: "///f\0oo/\0/.gitlab_ci.yml")
it 'sets a string but removes all null characters' do
project.update!(ci_config_path: "f\0oo/\0/.gitlab_ci.yml")
expect(project.ci_config_path).to eq('foo//.gitlab_ci.yml')
end
......
......@@ -77,4 +77,10 @@ describe BaseCountService, :use_clean_rails_memory_store_caching do
expect { service.cache_key }.to raise_error(NotImplementedError)
end
end
describe '#cache_options' do
it 'returns the default in options' do
expect(service.cache_options).to eq({ raw: false })
end
end
end
......@@ -8,7 +8,7 @@ describe Ci::CreatePipelineService do
let(:ref_name) { 'refs/heads/master' }
before do
stub_ci_pipeline_to_return_yaml_file
stub_repository_ci_yaml_file(sha: anything)
end
describe '#execute' do
......@@ -44,6 +44,7 @@ describe Ci::CreatePipelineService do
expect(pipeline).to eq(project.pipelines.last)
expect(pipeline).to have_attributes(user: user)
expect(pipeline).to have_attributes(status: 'pending')
expect(pipeline.repository_source?).to be true
expect(pipeline.builds.first).to be_kind_of(Ci::Build)
end
......
......@@ -22,6 +22,8 @@ describe Clusters::Applications::ScheduleInstallationService do
let(:service) { described_class.new(project, nil, cluster: cluster, application_class: application_class) }
it 'creates a new application' do
allow(ClusterInstallAppWorker).to receive(:perform_async)
expect { service.execute }.to change { application_class.count }.by(1)
end
......
......@@ -12,6 +12,8 @@ describe NotificationService, :mailer do
shared_examples 'notifications for new mentions' do
def send_notifications(*new_mentions)
mentionable.description = new_mentions.map(&:to_reference).join(' ')
notification.send(notification_method, mentionable, new_mentions, @u_disabled)
end
......@@ -20,13 +22,13 @@ describe NotificationService, :mailer do
should_not_email_anyone
end
it 'emails new mentions with a watch level higher than participant' do
send_notifications(@u_watcher, @u_participant_mentioned, @u_custom_global)
should_only_email(@u_watcher, @u_participant_mentioned, @u_custom_global)
it 'emails new mentions with a watch level higher than mention' do
send_notifications(@u_watcher, @u_participant_mentioned, @u_custom_global, @u_mentioned)
should_only_email(@u_watcher, @u_participant_mentioned, @u_custom_global, @u_mentioned)
end
it 'does not email new mentions with a watch level equal to or less than participant' do
send_notifications(@u_participating, @u_mentioned)
it 'does not email new mentions with a watch level equal to or less than mention' do
send_notifications(@u_disabled)
should_not_email_anyone
end
end
......@@ -509,6 +511,14 @@ describe NotificationService, :mailer do
should_not_email(issue.assignees.first)
end
it "emails any mentioned users with the mention level" do
issue.description = @u_mentioned.to_reference
notification.new_issue(issue, @u_disabled)
should_email(@u_mentioned)
end
it "emails the author if they've opted into notifications about their activity" do
issue.author.notified_of_own_activity = true
......@@ -900,6 +910,14 @@ describe NotificationService, :mailer do
should_not_email(@u_lazy_participant)
end
it "emails any mentioned users with the mention level" do
merge_request.description = @u_mentioned.to_reference
notification.new_merge_request(merge_request, @u_disabled)
should_email(@u_mentioned)
end
it "emails the author if they've opted into notifications about their activity" do
merge_request.author.notified_of_own_activity = true
......
......@@ -6,7 +6,10 @@ describe Projects::CountService do
describe '.query' do
it 'raises NotImplementedError' do
<<<<<<< HEAD
expect { service.relation_for_count }.to raise_error(NotImplementedError)
=======
>>>>>>> upstream/master
expect { described_class.query(project.id) }.to raise_error(NotImplementedError)
end
end
......
......@@ -7,21 +7,41 @@ require 'selenium-webdriver'
# Give CI some extra time
timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 60 : 30
Capybara.javascript_driver = :chrome
Capybara.register_driver :chrome do |app|
extra_args = []
extra_args << 'headless' unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: {
'args' => %w[no-sandbox disable-gpu --window-size=1240,1400] + extra_args
# This enables access to logs with `page.driver.manage.get_log(:browser)`
loggingPrefs: {
browser: "ALL",
client: "ALL",
driver: "ALL",
server: "ALL"
}
)
Capybara::Selenium::Driver
.new(app, browser: :chrome, desired_capabilities: capabilities)
options = Selenium::WebDriver::Chrome::Options.new
options.add_argument("window-size=1240,1400")
# Chrome won't work properly in a Docker container in sandbox mode
options.add_argument("no-sandbox")
# Run headless by default unless CHROME_HEADLESS specified
unless ENV['CHROME_HEADLESS'] =~ /^(false|no|0)$/i
options.add_argument("headless")
# Chrome documentation says this flag is needed for now
# https://developers.google.com/web/updates/2017/04/headless-chrome#cli
options.add_argument("disable-gpu")
end
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: capabilities,
options: options
)
end
Capybara.javascript_driver = :chrome
Capybara.default_max_wait_time = timeout
Capybara.ignore_hidden_elements = true
......
......@@ -21,6 +21,12 @@ module StubGitlabCalls
allow_any_instance_of(Ci::Pipeline).to receive(:ci_yaml_file) { ci_yaml }
end
def stub_repository_ci_yaml_file(sha:, path: '.gitlab-ci.yml')
allow_any_instance_of(Repository)
.to receive(:gitlab_ci_yml_for).with(sha, path)
.and_return(gitlab_ci_yaml)
end
def stub_ci_builds_disabled
allow_any_instance_of(Project).to receive(:builds_enabled?).and_return(false)
end
......
......@@ -66,19 +66,21 @@ describe PostReceive do
end
context "gitlab-ci.yml" do
let(:changes) { "123456 789012 refs/heads/feature\n654321 210987 refs/tags/tag" }
subject { described_class.new.perform(gl_repository, key_id, base64_changes) }
context "creates a Ci::Pipeline for every change" do
before do
stub_ci_pipeline_to_return_yaml_file
# TODO, don't stub private methods
#
allow_any_instance_of(Ci::CreatePipelineService)
.to receive(:commit).and_return(OpenStruct.new(id: '123456'))
allow_any_instance_of(Project)
.to receive(:commit)
.and_return(project.commit)
allow_any_instance_of(Repository)
.to receive(:branch_exists?).and_return(true)
.to receive(:branch_exists?)
.and_return(true)
end
it { expect { subject }.to change { Ci::Pipeline.count }.by(2) }
......
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