Commit 35acd04d authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into rubocop/gb/enabled-dot-styleposition-cop-eewq

* master: (26 commits)
  Document the manual steps needed to use repmgr
  Update CHANGELOG.md for 9.2.7
  Update CHANGELOG-EE.md for 9.2.7-ee
  Change GitLab version for object storage
  Speed up counting approvers when some are specified
  Fix bad connector - verify if both new and resolved issues are present before adding the connector.
  Re-enable autocomplete for milestones, tags, releases, and wiki
  Clarify that only when using CI_JOB_TOKEN multi-project pipelines exist
  Add link to issue for MySQL/subgroups drop support
  Fix duplicate testing of vm.triggered.
  Fix avatar images in pipeline and approval emails
  Fix some failing specs on master
  Change a few missing `login_as` to `gitlab_sign_in`
  Shush eslint.
  Add specs for coercing triggeredBy into a collection.
  Fix up specs that depend on triggered_by.
  Fix up propsData refs to triggered_by.
  Shush eslint.
  Update expected format for linked_pipelines_mack_data.
  Coerce triggered_by response into collection.
  ...

Conflicts:
	spec/helpers/application_helper_spec.rb
parents 31c844da ee4b5fc6
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 9.2.7 (2017-06-21)
- Geo: fixed Dynamic Backoff strategy that was not being used by workers. !2128
- fix Rebase being disabled for unapproved MRs.
## 9.2.6 (2017-06-16) ## 9.2.6 (2017-06-16)
- Geo: backported fix from 9.3 for big repository sync issues. !2000 - Geo: backported fix from 9.3 for big repository sync issues. !2000
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 9.2.7 (2017-06-21)
- Reinstate is_admin flag in users api when authenticated user is an admin. !12211 (rickettm)
## 9.2.6 (2017-06-16) ## 9.2.6 (2017-06-16)
- Fix the last coverage in trace log should be extracted. !11128 (dosuken123) - Fix the last coverage in trace log should be extracted. !11128 (dosuken123)
......
...@@ -194,7 +194,7 @@ import AuditLogs from './audit_logs'; ...@@ -194,7 +194,7 @@ import AuditLogs from './audit_logs';
case 'groups:milestones:update': case 'groups:milestones:update':
new ZenMode(); new ZenMode();
new gl.DueDateSelectors(); new gl.DueDateSelectors();
new gl.GLForm($('.milestone-form')); new gl.GLForm($('.milestone-form'), true);
break; break;
case 'projects:compare:show': case 'projects:compare:show':
new gl.Diff(); new gl.Diff();
...@@ -206,7 +206,7 @@ import AuditLogs from './audit_logs'; ...@@ -206,7 +206,7 @@ import AuditLogs from './audit_logs';
case 'projects:issues:new': case 'projects:issues:new':
case 'projects:issues:edit': case 'projects:issues:edit':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new gl.GLForm($('.issue-form')); new gl.GLForm($('.issue-form'), true);
new IssuableForm($('.issue-form')); new IssuableForm($('.issue-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
...@@ -218,7 +218,7 @@ import AuditLogs from './audit_logs'; ...@@ -218,7 +218,7 @@ import AuditLogs from './audit_logs';
case 'projects:merge_requests:edit': case 'projects:merge_requests:edit':
new gl.Diff(); new gl.Diff();
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
new gl.GLForm($('.merge-request-form')); new gl.GLForm($('.merge-request-form'), true);
new IssuableForm($('.merge-request-form')); new IssuableForm($('.merge-request-form'));
new LabelsSelect(); new LabelsSelect();
new MilestoneSelect(); new MilestoneSelect();
...@@ -227,7 +227,7 @@ import AuditLogs from './audit_logs'; ...@@ -227,7 +227,7 @@ import AuditLogs from './audit_logs';
break; break;
case 'projects:tags:new': case 'projects:tags:new':
new ZenMode(); new ZenMode();
new gl.GLForm($('.tag-form')); new gl.GLForm($('.tag-form'), true);
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs); new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
break; break;
case 'projects:snippets:new': case 'projects:snippets:new':
...@@ -240,11 +240,11 @@ import AuditLogs from './audit_logs'; ...@@ -240,11 +240,11 @@ import AuditLogs from './audit_logs';
case 'snippets:edit': case 'snippets:edit':
case 'snippets:create': case 'snippets:create':
case 'snippets:update': case 'snippets:update':
new gl.GLForm($('.snippet-form')); new gl.GLForm($('.snippet-form'), false);
break; break;
case 'projects:releases:edit': case 'projects:releases:edit':
new ZenMode(); new ZenMode();
new gl.GLForm($('.release-form')); new gl.GLForm($('.release-form'), true);
break; break;
case 'projects:merge_requests:show': case 'projects:merge_requests:show':
new gl.Diff(); new gl.Diff();
...@@ -510,7 +510,7 @@ import AuditLogs from './audit_logs'; ...@@ -510,7 +510,7 @@ import AuditLogs from './audit_logs';
new gl.Wikis(); new gl.Wikis();
shortcut_handler = new ShortcutsWiki(); shortcut_handler = new ShortcutsWiki();
new ZenMode(); new ZenMode();
new gl.GLForm($('.wiki-form')); new gl.GLForm($('.wiki-form'), true);
break; break;
case 'snippets': case 'snippets':
shortcut_handler = new ShortcutsNavigation(); shortcut_handler = new ShortcutsNavigation();
......
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
return this.pipeline.triggered || []; return this.pipeline.triggered || [];
}, },
triggeredBy() { triggeredBy() {
return this.pipeline.triggeredBy || []; const response = this.pipeline.triggered_by;
return response ? [response] : [];
}, },
hasTriggered() { hasTriggered() {
return !!this.triggered.length; return !!this.triggered.length;
......
...@@ -38,7 +38,8 @@ export default { ...@@ -38,7 +38,8 @@ export default {
return this.mr.pipeline.triggered || []; return this.mr.pipeline.triggered || [];
}, },
triggeredBy() { triggeredBy() {
return this.mr.pipeline.triggered_by || []; const response = this.mr.pipeline.triggered_by;
return response ? [response] : [];
}, },
}, },
template: ` template: `
......
...@@ -50,27 +50,40 @@ export default { ...@@ -50,27 +50,40 @@ export default {
codeText() { codeText() {
const { newIssues, resolvedIssues } = this.mr.codeclimateMetrics; const { newIssues, resolvedIssues } = this.mr.codeclimateMetrics;
let newIssuesText = ''; let newIssuesText;
let resolvedIssuesText = ''; let resolvedIssuesText;
let text = ''; let text = [];
if (this.hasNoneIssues) { if (this.hasNoneIssues) {
text = 'No changes to code quality so far.'; text.push('No changes to code quality so far.');
} else if (this.hasIssues) { } else if (this.hasIssues) {
if (newIssues.length) { if (newIssues.length) {
newIssuesText = `degraded on ${newIssues.length} ${this.pointsText(newIssues)}`; newIssuesText = ` degraded on ${newIssues.length} ${this.pointsText(newIssues)}`;
} }
if (resolvedIssues.length) { if (resolvedIssues.length) {
resolvedIssuesText = `improved on ${resolvedIssues.length} ${this.pointsText(resolvedIssues)}`; resolvedIssuesText = ` improved on ${resolvedIssues.length} ${this.pointsText(resolvedIssues)}`;
} }
const connector = this.hasIssues ? 'and' : ''; const connector = (newIssues.length > 0 && resolvedIssues.length > 0) ? ' and' : null;
text = `Code quality ${resolvedIssuesText} ${connector} ${newIssuesText}.`; text = ['Code quality'];
if (resolvedIssuesText) {
text.push(resolvedIssuesText);
} }
return text; if (connector) {
text.push(connector);
}
if (newIssuesText) {
text.push(newIssuesText);
}
text.push('.');
}
return text.join('');
}, },
}, },
......
...@@ -68,7 +68,7 @@ module ApplicationHelper ...@@ -68,7 +68,7 @@ module ApplicationHelper
end end
end end
def avatar_icon(user_or_email = nil, size = nil, scale = 2) def avatar_icon(user_or_email = nil, size = nil, scale = 2, only_path: true)
user = user =
if user_or_email.is_a?(User) if user_or_email.is_a?(User)
user_or_email user_or_email
...@@ -77,7 +77,7 @@ module ApplicationHelper ...@@ -77,7 +77,7 @@ module ApplicationHelper
end end
if user if user
user.avatar_url(size: size) || default_avatar user.avatar_url(size: size, only_path: only_path) || default_avatar
else else
gravatar_icon(user_or_email, size, scale) gravatar_icon(user_or_email, size, scale)
end end
......
...@@ -36,23 +36,28 @@ module Approvable ...@@ -36,23 +36,28 @@ module Approvable
# #
def number_of_potential_approvers def number_of_potential_approvers
has_access = ['access_level > ?', Member::REPORTER] has_access = ['access_level > ?', Member::REPORTER]
users_with_access = { id: project.project_authorizations.where(has_access).select(:user_id) }
all_approvers = all_approvers_including_groups all_approvers = all_approvers_including_groups
wheres = [ users_relation = User.active.where.not(id: approvals.select(:user_id))
"id IN (#{project.project_authorizations.where(has_access).select(:user_id).to_sql})" users_relation = users_relation.where.not(id: author.id) if author
]
# This is an optimisation for large instances. Instead of getting the
# count of all users who meet the conditions in a single query, which
# produces a slow query plan, we get the union of all users with access
# and all users in the approvers list, and count them.
if all_approvers.any? if all_approvers.any?
wheres << "id IN (#{all_approvers.map(&:id).join(', ')})" specific_approvers = { id: all_approvers.map(&:id) }
end
users = User
.active
.where("(#{wheres.join(' OR ')}) AND id NOT IN (#{approvals.select(:user_id).to_sql})")
users = users.where.not(id: author.id) if author union = Gitlab::SQL::Union.new([
users_relation.where(users_with_access).select(:id),
users_relation.where(specific_approvers).select(:id)
])
users.count User.from("(#{union.to_sql}) subquery").count
else
users_relation.where(users_with_access).count
end
end end
# Users in the list of approvers who have not already approved this MR. # Users in the list of approvers who have not already approved this MR.
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
%span{ style: "font-weight: bold;color:#333333;" } Merge request %span{ style: "font-weight: bold;color:#333333;" } Merge request
%a{ href: merge_request_url(@merge_request), style: "font-weight: bold;color:#3777b0;text-decoration:none" }= @merge_request.to_reference %a{ href: merge_request_url(@merge_request), style: "font-weight: bold;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
%span was approved by %span was approved by
%img.avatar{ height: "24", src: avatar_icon(@approved_by, 24), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(@approved_by, 24, only_path: false), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar" }/
%a.muted{ href: user_url(@approved_by), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(@approved_by), style: "color:#333333;text-decoration:none;" }
= @approved_by.name = @approved_by.name
%tr.spacer %tr.spacer
...@@ -117,7 +117,7 @@ ...@@ -117,7 +117,7 @@
%tbody %tbody
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon(@merge_request.author, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(@merge_request.author, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name = @merge_request.author.name
...@@ -130,7 +130,7 @@ ...@@ -130,7 +130,7 @@
%tbody %tbody
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon(@merge_request.assignee, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(@merge_request.assignee, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
%a.muted{ href: user_url(@merge_request.assignee), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(@merge_request.assignee), style: "color:#333333;text-decoration:none;" }
= @merge_request.assignee.name = @merge_request.assignee.name
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
%tbody %tbody
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.author - if commit.author
%a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
%tbody %tbody
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.committer - if commit.committer
%a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
triggered by triggered by
- if @pipeline.user - if @pipeline.user
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
%img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
%a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
= @pipeline.user.name = @pipeline.user.name
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
%tbody %tbody
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.author - if commit.author
%a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
%tbody %tbody
%tr %tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
%img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.committer - if commit.committer
%a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
...@@ -100,7 +100,7 @@ ...@@ -100,7 +100,7 @@
triggered by triggered by
- if @pipeline.user - if @pipeline.user
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
%img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/ %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" } %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
%a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" } %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
= @pipeline.user.name = @pipeline.user.name
......
---
title: fix Rebase being disabled for unapproved MRs
merge_request:
author:
---
title: 'Geo: fixed Dynamic Backoff strategy that was not being used by workers'
merge_request: 2128
author:
---
title: Speed up checking for approvers when approvers are specified on the MR
merge_request:
author:
...@@ -140,7 +140,7 @@ production: &base ...@@ -140,7 +140,7 @@ production: &base
# path: shared/artifacts # path: shared/artifacts
# object_store: # object_store:
# enabled: false # enabled: false
# remote_directory: artifacts # remote_directory: artifacts # The bucket name
# connection: # connection:
# provider: AWS # Only AWS supported at the moment # provider: AWS # Only AWS supported at the moment
# aws_access_key_id: AWS_ACCESS_KEY_ID # aws_access_key_id: AWS_ACCESS_KEY_ID
......
This diff is collapsed.
...@@ -22,6 +22,9 @@ If you use a cloud-managed service, or provide your own PostgreSQL: ...@@ -22,6 +22,9 @@ If you use a cloud-managed service, or provide your own PostgreSQL:
## Configure using Omnibus ## Configure using Omnibus
**Note**: We're working on a new version that will help automate the setup of a PostgreSQL cluster.
You can use the [alpha version of the document](alpha_database.md) to try it out now.
1. Download/install GitLab Omnibus using **steps 1 and 2** from 1. Download/install GitLab Omnibus using **steps 1 and 2** from
[GitLab downloads](https://about.gitlab.com/downloads). Do not complete other [GitLab downloads](https://about.gitlab.com/downloads). Do not complete other
steps on the download page. steps on the download page.
......
...@@ -46,7 +46,10 @@ To disable artifacts site-wide, follow the steps below. ...@@ -46,7 +46,10 @@ To disable artifacts site-wide, follow the steps below.
After a successful job, GitLab Runner uploads an archive containing the job After a successful job, GitLab Runner uploads an archive containing the job
artifacts to GitLab. artifacts to GitLab.
To change the location where the artifacts are stored, follow the steps below. ### Using local storage
To change the location where the artifacts are stored locally, follow the steps
below.
--- ---
...@@ -82,41 +85,86 @@ _The artifacts are stored by default in ...@@ -82,41 +85,86 @@ _The artifacts are stored by default in
1. Save the file and [restart GitLab][] for the changes to take effect. 1. Save the file and [restart GitLab][] for the changes to take effect.
### Using object storage
>**Notes:**
- [Introduced][ee-1762] in [GitLab Enterprise Edition Premium][eep] 9.4.
- By enabling this feature the artifacts will **not** be [browsable] anymore
through the web interface. This limitation will be removed in one of the
upcoming releases.
If you don't want to use the local disk where GitLab is installed to store the
artifacts, you can use an object storage like AWS S3 instead.
This configuration relies on valid AWS credentials to be configured already.
**In Omnibus installations:**
_The artifacts are stored by default in
`/var/opt/gitlab/gitlab-rails/shared/artifacts`._
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines by replacing with
the values you want:
```ruby
gitlab_rails['artifacts_enabled'] = true
gitlab_rails['artifacts']['object_store_enabled'] = false
gitlab_rails['artifacts']['object_store_directory'] = "artifacts"
gitlab_rails['artifacts']['object_store_connection'] = {
'provider' => 'AWS',
'region' => 'eu-central-1',
'aws_access_key_id' => 'AWS_ACCESS_KEY_ID',
'aws_secret_access_key' => 'AWS_SECRET_ACCESS_KEY'
}
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
1. Migrate any existing local artifacts to the object storage:
```bash
gitlab-rake gitlab:artifacts:migrate
```
Currently this has to be executed manually and it will allow you to
migrate the existing artifacts to the object storage, but all new
artifacts will still be stored on the local disk. In the future
you will be given an option to define a default storage artifacts for all
new files.
--- ---
**Using Object Store** **In installations from source:**
The previously mentioned methods use the local disk to store artifacts. However, _The artifacts are stored by default in
there is the option to use object stores like AWS' S3. To do this, set the `/home/git/gitlab/shared/artifacts`._
`object_store` in your `gitlab.yml`. This relies on valid AWS
credentials to be configured already. 1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
```yaml ```yaml
artifacts: artifacts:
enabled: true enabled: true
path: /mnt/storage/artifacts
object_store: object_store:
enabled: true enabled: true
remote_directory: my-bucket-name remote_directory: "artifacts" # The bucket name
connection: connection:
provider: AWS provider: AWS # Only AWS supported at the moment
aws_access_key_id: S3_KEY_ID aws_access_key_id: AWS_ACESS_KEY_ID
aws_secret_key_id: S3_SECRET_KEY_ID aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: eu-central-1 region: eu-central-1
``` ```
This will allow you to migrate existing artifacts to object store, 1. Save the file and [restart GitLab][] for the changes to take effect.
but all new artifacts will still be stored on the local disk. 1. Migrate any existing local artifacts to the object storage:
In the future you will be given an option to define a default storage artifacts
for all new files. Currently the artifacts migration has to be executed manually:
```bash ```bash
gitlab-rake gitlab:artifacts:migrate sudo -u git -H bundle exec rake gitlab:artifacts:migrate RAILS_ENV=production
``` ```
Please note, that enabling this feature Currently this has to be executed manually and it will allow you to
will have the effect that artifacts are _not_ browsable anymore through the web migrate the existing artifacts to the object storage, but all new
interface. This limitation will be removed in one of the upcoming releases. artifacts will still be stored on the local disk. In the future
you will be given an option to define a default storage artifacts for all
new files.
## Expiring artifacts ## Expiring artifacts
...@@ -181,6 +229,9 @@ When clicking on a specific file, [GitLab Workhorse] extracts it ...@@ -181,6 +229,9 @@ When clicking on a specific file, [GitLab Workhorse] extracts it
from the archive and the download begins. This implementation saves space, from the archive and the download begins. This implementation saves space,
memory and disk I/O. memory and disk I/O.
[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab" [reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md "How to restart GitLab" [restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository" [gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
[ee-1762]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762
[browsable]: ../user/project/pipelines/job_artifacts.md#browsing-job-artifacts
...@@ -186,6 +186,23 @@ by name. The order of severity is: ...@@ -186,6 +186,23 @@ by name. The order of severity is:
![Pipeline mini graph sorting](img/pipelines_mini_graph_sorting.png) ![Pipeline mini graph sorting](img/pipelines_mini_graph_sorting.png)
### Multi-project pipelines graphs
> [Introduced][ee-2121] in [GitLab Enterprise Edition Premium][eep] 9.3.
Using the [`CI_JOB_TOKEN` when triggering pipelines][triggers], GitLab
recognizes the source of the job token, and thus internally ties these pipelines
together which makes it easy to start visualizing their relationships.
Those relationships are displayed in the pipeline graph by showing inbound and
outbound connections for upstream and downstream pipeline dependencies.
![Multi-projects pipelines graphs](img/multi_project_pipelines_graph.png)
This is useful for larger projects, especially those adopting a micro-services
architecture, that often have a set of interdependent components which form the
complete product.
## How the pipeline duration is calculated ## How the pipeline duration is calculated
Total running time for a given pipeline would exclude retries and pending Total running time for a given pipeline would exclude retries and pending
...@@ -229,9 +246,11 @@ respective link in the [Pipelines settings] page. ...@@ -229,9 +246,11 @@ respective link in the [Pipelines settings] page.
[stages]: yaml/README.md#stages [stages]: yaml/README.md#stages
[runners]: runners/README.html [runners]: runners/README.html
[pipelines settings]: ../user/project/pipelines/settings.md [pipelines settings]: ../user/project/pipelines/settings.md
[triggers]: triggers/README.md [triggers]: triggers/README.md#ci-job-token
[ce-5742]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5742 [ce-5742]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5742
[ce-6242]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6242 [ce-6242]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6242
[ce-7931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7931 [ce-7931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7931
[ce-9760]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9760 [ce-9760]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9760
[regexp]: https://gitlab.com/gitlab-org/gitlab-ce/blob/2f3dc314f42dbd79813e6251792853bc231e69dd/app/models/commit_status.rb#L99 [regexp]: https://gitlab.com/gitlab-org/gitlab-ce/blob/2f3dc314f42dbd79813e6251792853bc231e69dd/app/models/commit_status.rb#L99
[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
[ee-2121]: https://gitlab.com/gitlab-org/gitlab-ee/issues/2121
...@@ -26,13 +26,13 @@ which is used to authenticate with the [GitLab Container Registry][registry]. ...@@ -26,13 +26,13 @@ which is used to authenticate with the [GitLab Container Registry][registry].
This way of triggering can only be used when invoked inside `.gitlab-ci.yml`, This way of triggering can only be used when invoked inside `.gitlab-ci.yml`,
and it creates a dependent pipeline relation visible on the and it creates a dependent pipeline relation visible on the
[pipeline graph](../pipelines.md#pipelines-graphs). For example: [pipeline graph](../pipelines.md#multi-project-pipelines-graphs). For example:
```yaml ```yaml
build_docs: build_docs:
stage: deploy stage: deploy
script: script:
- "curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline" - curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=master https://gitlab.example.com/api/v4/projects/9/trigger/pipeline
only: only:
- tags - tags
``` ```
......
...@@ -86,56 +86,31 @@ if your available memory changes. ...@@ -86,56 +86,31 @@ if your available memory changes.
Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those. Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those.
## GitLab Runner
We strongly advise against installing GitLab Runner on the same machine you plan
to install GitLab on. Depending on how you decide to configure GitLab Runner and
what tools you use to exercise your application in the CI environment, GitLab
Runner can consume significant amount of available memory.
Memory consumption calculations, that are available above, will not be valid if
you decide to run GitLab Runner and the GitLab Rails application on the same
machine.
It is also not safe to install everything on a single machine, because of the
[security reasons] - especially when you plan to use shell executor with GitLab
Runner.
We recommend using a separate machine for each GitLab Runner, if you plan to
use the CI features.
[security reasons]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md
## Unicorn Workers
It's possible to increase the amount of unicorn workers and this will usually help to reduce the response time of the applications and increase the ability to handle parallel requests.
For most instances we recommend using: CPU cores + 1 = unicorn workers.
So for a machine with 2 cores, 3 unicorn workers is ideal.
For all machines that have 2GB and up we recommend a minimum of three unicorn workers.
If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping.
To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings).
## Database ## Database
The server running the database should have _at least_ 5-10 GB of storage
available, though the exact requirements depend on the size of the GitLab
installation (e.g. the number of users, projects, etc).
We currently support the following databases: We currently support the following databases:
- PostgreSQL - PostgreSQL
- MySQL/MariaDB - MySQL/MariaDB
We _highly_ recommend the use of PostgreSQL instead of MySQL/MariaDB as not all We **highly recommend** the use of PostgreSQL instead of MySQL/MariaDB as not all
features of GitLab may work with MySQL/MariaDB. For example, MySQL does not have features of GitLab may work with MySQL/MariaDB:
the right features to support nested groups in an efficient manner; see
<https://gitlab.com/gitlab-org/gitlab-ce/issues/30472> for more information 1. MySQL support for subgroups was [dropped with GitLab 9.3][post].
about this. GitLab Geo also does [not support MySQL](https://docs.gitlab.com/ee/gitlab-geo/database.html#mysql-replication). See [issue #30472][30472] for more information.
1. GitLab Geo does [not support MySQL](https://docs.gitlab.com/ee/gitlab-geo/database.html#mysql-replication).
1. [Zero downtime migrations][zero] do not work with MySQL
Existing users using GitLab with MySQL/MariaDB are advised to Existing users using GitLab with MySQL/MariaDB are advised to
migrate to PostgreSQL instead. [migrate to PostgreSQL](../update/mysql_to_postgresql.md) instead.
The server running the database should have _at least_ 5-10 GB of storage [30472]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472
available, though the exact requirements depend on the size of the GitLab [zero]: ../update/README.md#upgrading-without-downtime
installation (e.g. the number of users, projects, etc). [post]: https://about.gitlab.com/2017/06/22/gitlab-9-3-released/#dropping-support-for-subgroups-in-mysql
### PostgreSQL Requirements ### PostgreSQL Requirements
...@@ -154,6 +129,18 @@ CREATE EXTENSION pg_trgm; ...@@ -154,6 +129,18 @@ CREATE EXTENSION pg_trgm;
On some systems you may need to install an additional package (e.g. On some systems you may need to install an additional package (e.g.
`postgresql-contrib`) for this extension to become available. `postgresql-contrib`) for this extension to become available.
## Unicorn Workers
It's possible to increase the amount of unicorn workers and this will usually help to reduce the response time of the applications and increase the ability to handle parallel requests.
For most instances we recommend using: CPU cores + 1 = unicorn workers.
So for a machine with 2 cores, 3 unicorn workers is ideal.
For all machines that have 2GB and up we recommend a minimum of three unicorn workers.
If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping.
To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings).
## Redis and Sidekiq ## Redis and Sidekiq
Redis stores all user sessions and the background task queue. Redis stores all user sessions and the background task queue.
...@@ -172,6 +159,26 @@ default settings. ...@@ -172,6 +159,26 @@ default settings.
If you would like to disable Prometheus and it's exporters or read more information If you would like to disable Prometheus and it's exporters or read more information
about it, check the [Prometheus documentation](../administration/monitoring/prometheus/index.md). about it, check the [Prometheus documentation](../administration/monitoring/prometheus/index.md).
## GitLab Runner
We strongly advise against installing GitLab Runner on the same machine you plan
to install GitLab on. Depending on how you decide to configure GitLab Runner and
what tools you use to exercise your application in the CI environment, GitLab
Runner can consume significant amount of available memory.
Memory consumption calculations, that are available above, will not be valid if
you decide to run GitLab Runner and the GitLab Rails application on the same
machine.
It is also not safe to install everything on a single machine, because of the
[security reasons] - especially when you plan to use shell executor with GitLab
Runner.
We recommend using a separate machine for each GitLab Runner, if you plan to
use the CI features.
[security reasons]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md
## Supported web browsers ## Supported web browsers
We support the current and the previous major release of Firefox, Chrome/Chromium, Safari and Microsoft browsers (Microsoft Edge and Internet Explorer 11). We support the current and the previous major release of Firefox, Chrome/Chromium, Safari and Microsoft browsers (Microsoft Edge and Internet Explorer 11).
......
...@@ -11,22 +11,6 @@ There are currently 3 official ways to install GitLab: ...@@ -11,22 +11,6 @@ There are currently 3 official ways to install GitLab:
Based on your installation, choose a section below that fits your needs. Based on your installation, choose a section below that fits your needs.
---
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Omnibus Packages](#omnibus-packages)
- [Installation from source](#installation-from-source)
- [Installation using Docker](#installation-using-docker)
- [Upgrading between editions](#upgrading-between-editions)
- [Community to Enterprise Edition](#community-to-enterprise-edition)
- [Enterprise to Community Edition](#enterprise-to-community-edition)
- [Miscellaneous](#miscellaneous)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Omnibus Packages ## Omnibus Packages
- The [Omnibus update guide](http://docs.gitlab.com/omnibus/update/README.html) - The [Omnibus update guide](http://docs.gitlab.com/omnibus/update/README.html)
......
# Subgroups # Subgroups
> [Introduced][ce-2772] in GitLab 9.0. >**Notes:**
- [Introduced][ce-2772] in GitLab 9.0.
- Not available when using MySQL as external database (support removed in
GitLab 9.3 [due to performance reasons][issue]).
With subgroups (aka nested groups or hierarchical groups) you can have With subgroups (aka nested groups or hierarchical groups) you can have
up to 20 levels of nested groups, which among other things can help you to: up to 20 levels of nested groups, which among other things can help you to:
...@@ -173,3 +176,4 @@ Here's a list of what you can't do with subgroups: ...@@ -173,3 +176,4 @@ Here's a list of what you can't do with subgroups:
[ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772 [ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772
[permissions]: ../../permissions.md#group [permissions]: ../../permissions.md#group
[reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/path_regex.rb [reserved]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/path_regex.rb
[issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600
...@@ -60,7 +60,7 @@ describe 'Help Pages', feature: true do ...@@ -60,7 +60,7 @@ describe 'Help Pages', feature: true do
allow_any_instance_of(ApplicationSetting).to receive(:help_text) { "My Custom Text" } allow_any_instance_of(ApplicationSetting).to receive(:help_text) { "My Custom Text" }
allow_any_instance_of(ApplicationSetting).to receive(:help_page_support_url) { "http://example.com/help" } allow_any_instance_of(ApplicationSetting).to receive(:help_page_support_url) { "http://example.com/help" }
login_as :user gitlab_sign_in :user
visit help_path visit help_path
end end
......
...@@ -228,6 +228,13 @@ describe 'New/edit issue', :feature, :js do ...@@ -228,6 +228,13 @@ describe 'New/edit issue', :feature, :js do
expect(page.all('.dropdown-menu-user a.is-active')[0].first(:xpath, '..')['data-user-id']).to eq(user.id.to_s) expect(page.all('.dropdown-menu-user a.is-active')[0].first(:xpath, '..')['data-user-id']).to eq(user.id.to_s)
expect(page.all('.dropdown-menu-user a.is-active')[1].first(:xpath, '..')['data-user-id']).to eq(user2.id.to_s) expect(page.all('.dropdown-menu-user a.is-active')[1].first(:xpath, '..')['data-user-id']).to eq(user2.id.to_s)
end end
it 'description has autocomplete' do
find('#issue_description').native.send_keys('')
fill_in 'issue_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
end end
context 'edit issue' do context 'edit issue' do
...@@ -276,6 +283,13 @@ describe 'New/edit issue', :feature, :js do ...@@ -276,6 +283,13 @@ describe 'New/edit issue', :feature, :js do
end end
end end
end end
it 'description has autocomplete' do
find('#issue_description').native.send_keys('')
fill_in 'issue_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
end end
describe 'sub-group project' do describe 'sub-group project' do
......
...@@ -96,6 +96,13 @@ describe 'New/edit merge request', feature: true, js: true do ...@@ -96,6 +96,13 @@ describe 'New/edit merge request', feature: true, js: true do
.to end_with(merge_request_path(merge_request)) .to end_with(merge_request_path(merge_request))
end end
end end
it 'description has autocomplete' do
find('#merge_request_description').native.send_keys('')
fill_in 'merge_request_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
end end
context 'edit merge request' do context 'edit merge request' do
...@@ -157,6 +164,13 @@ describe 'New/edit merge request', feature: true, js: true do ...@@ -157,6 +164,13 @@ describe 'New/edit merge request', feature: true, js: true do
end end
end end
end end
it 'description has autocomplete' do
find('#merge_request_description').native.send_keys('')
fill_in 'merge_request_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
end end
end end
......
...@@ -39,7 +39,7 @@ describe 'Milestone draggable', feature: true, js: true do ...@@ -39,7 +39,7 @@ describe 'Milestone draggable', feature: true, js: true do
end end
it 'assigns issue when it has been dragged to ongoing list' do it 'assigns issue when it has been dragged to ongoing list' do
login_as(:admin) gitlab_sign_in(:admin)
create_and_drag_issue create_and_drag_issue
expect(@issue.reload.assignees).not_to be_empty expect(@issue.reload.assignees).not_to be_empty
......
require 'spec_helper'
feature 'Creating a new project milestone', :feature, :js do
let(:user) { create(:user) }
let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) }
before do
login_as(user)
visit new_namespace_project_milestone_path(project.namespace, project)
end
it 'description has autocomplete' do
find('#milestone_description').native.send_keys('')
fill_in 'milestone_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
end
...@@ -29,7 +29,7 @@ describe 'Project snippets', :js, feature: true do ...@@ -29,7 +29,7 @@ describe 'Project snippets', :js, feature: true do
context 'when submitting a note' do context 'when submitting a note' do
before do before do
login_as :admin gitlab_sign_in :admin
visit namespace_project_snippet_path(project.namespace, project, snippets[0]) visit namespace_project_snippet_path(project.namespace, project, snippets[0])
end end
......
...@@ -133,6 +133,22 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do ...@@ -133,6 +133,22 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
expect(page).to have_content('My awesome wiki!') expect(page).to have_content('My awesome wiki!')
end end
end end
scenario 'content has autocomplete', :js do
click_link 'New page'
page.within '#modal-new-wiki' do
fill_in :new_wiki_path, with: 'test-autocomplete'
click_button 'Create page'
end
page.within '.wiki-form' do
find('#wiki_content').native.send_keys('')
fill_in :wiki_content, with: '@'
end
expect(page).to have_selector('.atwho-view')
end
end end
end end
......
...@@ -5,11 +5,10 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do ...@@ -5,11 +5,10 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
background do background do
project.team << [user, :master] project.team << [user, :master]
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
gitlab_sign_in(user) gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project) visit namespace_project_wikis_path(project.namespace, project)
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
click_link 'Wiki'
end end
context 'in the user namespace' do context 'in the user namespace' do
...@@ -42,6 +41,15 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do ...@@ -42,6 +41,15 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
expect(page).to have_content('Content can\'t be blank') expect(page).to have_content('Content can\'t be blank')
expect(find('textarea#wiki_content').value).to eq '' expect(find('textarea#wiki_content').value).to eq ''
end end
scenario 'content has autocomplete', :js do
click_link 'Edit'
find('#wiki_content').native.send_keys('')
fill_in :wiki_content, with: '@'
expect(page).to have_selector('.atwho-view')
end
end end
end end
......
...@@ -7,6 +7,10 @@ feature 'Master creates tag', feature: true do ...@@ -7,6 +7,10 @@ feature 'Master creates tag', feature: true do
before do before do
project.team << [user, :master] project.team << [user, :master]
gitlab_sign_in(user) gitlab_sign_in(user)
end
context 'from tag list' do
before do
visit namespace_project_tags_path(project.namespace, project) visit namespace_project_tags_path(project.namespace, project)
end end
...@@ -64,6 +68,20 @@ feature 'Master creates tag', feature: true do ...@@ -64,6 +68,20 @@ feature 'Master creates tag', feature: true do
expect(find('.dropdown-menu')).to have_content 'empty-branch' expect(find('.dropdown-menu')).to have_content 'empty-branch'
end end
end end
end
context 'from new tag page' do
before do
visit new_namespace_project_tag_path(project.namespace, project)
end
it 'description has autocomplete', :js do
find('#release_description').native.send_keys('')
fill_in 'release_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
end
def create_tag_in_form(tag:, ref:, message: nil, desc: nil) def create_tag_in_form(tag:, ref:, message: nil, desc: nil)
click_link 'New tag' click_link 'New tag'
......
...@@ -24,6 +24,17 @@ feature 'Master updates tag', feature: true do ...@@ -24,6 +24,17 @@ feature 'Master updates tag', feature: true do
expect(page).to have_content 'v1.1.0' expect(page).to have_content 'v1.1.0'
expect(page).to have_content 'Awesome release notes' expect(page).to have_content 'Awesome release notes'
end end
scenario 'description has autocomplete', :js do
page.within(first('.content-list .controls')) do
click_link 'Edit release notes'
end
find('#release_description').native.send_keys('')
fill_in 'release_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
end end
context 'from a specific tag page' do context 'from a specific tag page' do
......
...@@ -82,6 +82,7 @@ describe ApplicationHelper do ...@@ -82,6 +82,7 @@ describe ApplicationHelper do
end end
describe 'avatar_icon' do describe 'avatar_icon' do
<<<<<<< HEAD
it 'returns an url for the avatar' do it 'returns an url for the avatar' do
user = create(:user, avatar: File.open(uploaded_image_temp_path)) user = create(:user, avatar: File.open(uploaded_image_temp_path))
...@@ -105,19 +106,73 @@ describe ApplicationHelper do ...@@ -105,19 +106,73 @@ describe ApplicationHelper do
expect(helper.avatar_icon(user.email).to_s) expect(helper.avatar_icon(user.email).to_s)
.to match("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif") .to match("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end end
=======
let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
it 'calls gravatar_icon when no User exists with the given email' do context 'using an email' do
context 'when there is a matching user' do
it 'returns a relative URL for the avatar' do
expect(helper.avatar_icon(user.email).to_s)
.to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
context 'when an asset_host is set in the config' do
let(:asset_host) { 'http://assets' }
before do
allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
end
it 'returns an absolute URL on that asset host' do
expect(helper.avatar_icon(user.email, only_path: false).to_s)
.to eq("#{asset_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
context 'when only_path is set to false' do
it 'returns an absolute URL for the avatar' do
expect(helper.avatar_icon(user.email, only_path: false).to_s)
.to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
context 'when the GitLab instance is at a relative URL' do
before do
stub_config_setting(relative_url_root: '/gitlab')
# Must be stubbed after the stub above, and separately
stub_config_setting(url: Settings.send(:build_gitlab_url))
end
it 'returns a relative URL with the correct prefix' do
expect(helper.avatar_icon(user.email).to_s)
.to eq("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
end
context 'when no user exists for the email' do
it 'calls gravatar_icon' do
expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
helper.avatar_icon('foo@example.com', 20, 2) helper.avatar_icon('foo@example.com', 20, 2)
end end
end
end
describe 'using a User' do describe 'using a user' do
it 'returns an URL for the avatar' do context 'when only_path is true' do
user = create(:user, avatar: File.open(uploaded_image_temp_path)) it 'returns a relative URL for the avatar' do
expect(helper.avatar_icon(user, only_path: true).to_s).
to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
end
expect(helper.avatar_icon(user).to_s) context 'when only_path is false' do
.to match("/uploads/system/user/avatar/#{user.id}/banana_sample.gif") it 'returns an absolute URL for the avatar' do
expect(helper.avatar_icon(user, only_path: false).to_s).
to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
end
>>>>>>> master
end end
end end
end end
......
...@@ -7,7 +7,7 @@ const GraphComponent = Vue.extend(graphComponent); ...@@ -7,7 +7,7 @@ const GraphComponent = Vue.extend(graphComponent);
const pipelineJSON = Object.assign(graphJSON, { const pipelineJSON = Object.assign(graphJSON, {
triggered: linkedPipelineJSON.triggered, triggered: linkedPipelineJSON.triggered,
triggeredBy: linkedPipelineJSON.triggered_by, triggered_by: linkedPipelineJSON.triggered_by,
}); });
const defaultPropsData = { const defaultPropsData = {
...@@ -83,6 +83,10 @@ describe('graph component', function () { ...@@ -83,6 +83,10 @@ describe('graph component', function () {
}); });
describe('linked pipelines components', function () { describe('linked pipelines components', function () {
it('should coerce triggeredBy into a collection', function () {
expect(this.component.triggeredBy.length).toBe(1);
});
it('should render an upstream pipelines column', function () { it('should render an upstream pipelines column', function () {
expect(this.component.$el.querySelector('.linked-pipelines-column')).not.toBeNull(); expect(this.component.$el.querySelector('.linked-pipelines-column')).not.toBeNull();
expect(this.component.$el.innerHTML).toContain('Upstream'); expect(this.component.$el.innerHTML).toContain('Upstream');
...@@ -97,7 +101,7 @@ describe('graph component', function () { ...@@ -97,7 +101,7 @@ describe('graph component', function () {
describe('when linked pipelines are not present', function () { describe('when linked pipelines are not present', function () {
beforeEach(function () { beforeEach(function () {
const pipeline = Object.assign(graphJSON, { triggered: [], triggeredBy: [] }); const pipeline = Object.assign(graphJSON, { triggered: [], triggered_by: [] });
this.component = new GraphComponent({ this.component = new GraphComponent({
propsData: { pipeline, isLoading: false }, propsData: { pipeline, isLoading: false },
}).$mount(); }).$mount();
......
/* eslint-disable quote-props, quotes, comma-dangle */ /* eslint-disable quote-props, quotes, comma-dangle */
export default { export default {
"triggered_by": [{ "triggered_by": {
"id": 129, "id": 129,
"active": true, "active": true,
"path": "/gitlab-org/gitlab-ce/pipelines/129", "path": "/gitlab-org/gitlab-ce/pipelines/129",
...@@ -56,7 +56,7 @@ export default { ...@@ -56,7 +56,7 @@ export default {
"cancel_path": "/gitlab-org/gitlab-ce/pipelines/129/cancel", "cancel_path": "/gitlab-org/gitlab-ce/pipelines/129/cancel",
"created_at": "2017-05-24T14:46:20.090Z", "created_at": "2017-05-24T14:46:20.090Z",
"updated_at": "2017-05-24T14:46:29.906Z" "updated_at": "2017-05-24T14:46:29.906Z"
}], },
"triggered": [{ "triggered": [{
"id": 132, "id": 132,
"active": true, "active": true,
......
...@@ -72,6 +72,34 @@ describe('Merge Request Code Quality', () => { ...@@ -72,6 +72,34 @@ describe('Merge Request Code Quality', () => {
}, 0); }, 0);
}); });
describe('text connector', () => {
it('should only render information about fixed issues', (done) => {
setTimeout(() => {
vm.mr.codeclimateMetrics.newIssues = [];
Vue.nextTick(() => {
expect(
vm.$el.querySelector('span:nth-child(2)').textContent.trim(),
).toEqual('Code quality improved on 1 point.');
done();
});
}, 0);
});
it('should only render information about added issues', (done) => {
setTimeout(() => {
vm.mr.codeclimateMetrics.resolvedIssues = [];
Vue.nextTick(() => {
expect(
vm.$el.querySelector('span:nth-child(2)').textContent.trim(),
).toEqual('Code quality degraded on 1 point.');
done();
});
}, 0);
});
});
describe('toggleCollapsed', () => { describe('toggleCollapsed', () => {
it('toggles issues', (done) => { it('toggles issues', (done) => {
setTimeout(() => { setTimeout(() => {
......
...@@ -130,6 +130,14 @@ describe('MRWidgetPipeline', () => { ...@@ -130,6 +130,14 @@ describe('MRWidgetPipeline', () => {
}); });
}); });
it('should set triggered to an empty array', () => {
expect(vm.triggered.length).toBe(0);
});
it('should set triggeredBy to an empty array', () => {
expect(vm.triggeredBy.length).toBe(0);
});
it('should not render upstream or downstream pipelines', () => { it('should not render upstream or downstream pipelines', () => {
expect(el.querySelector('.linked-pipeline-mini-list')).toBeNull(); expect(el.querySelector('.linked-pipeline-mini-list')).toBeNull();
}); });
...@@ -149,6 +157,10 @@ describe('MRWidgetPipeline', () => { ...@@ -149,6 +157,10 @@ describe('MRWidgetPipeline', () => {
}).$mount(); }).$mount();
}); });
it('should coerce triggeredBy into a collection', function () {
expect(this.vm.triggeredBy.length).toBe(1);
});
it('should render the linked pipelines mini list', function (done) { it('should render the linked pipelines mini list', function (done) {
Vue.nextTick(() => { Vue.nextTick(() => {
expect(this.vm.$el.querySelector('.linked-pipeline-mini-list.is-upstream')).not.toBeNull(); expect(this.vm.$el.querySelector('.linked-pipeline-mini-list.is-upstream')).not.toBeNull();
......
...@@ -9,7 +9,7 @@ describe('Linked pipeline mini list', () => { ...@@ -9,7 +9,7 @@ describe('Linked pipeline mini list', () => {
beforeEach(() => { beforeEach(() => {
this.component = new ListComponent({ this.component = new ListComponent({
propsData: { propsData: {
triggeredBy: mockData.triggered_by, triggeredBy: [mockData.triggered_by],
}, },
}).$mount(); }).$mount();
}); });
......
...@@ -536,10 +536,16 @@ describe MergeRequest, models: true do ...@@ -536,10 +536,16 @@ describe MergeRequest, models: true do
it "includes project members with developer access and up" do it "includes project members with developer access and up" do
expect do expect do
developer = create(:user)
project.add_guest(create(:user)) project.add_guest(create(:user))
project.add_reporter(create(:user)) project.add_reporter(create(:user))
project.add_developer(create(:user)) project.add_developer(developer)
project.add_master(create(:user)) project.add_master(create(:user))
# Add this user as both someone with access, and an explicit approver,
# to ensure they aren't double-counted.
create(:approver, user: developer, target: merge_request)
end.to change { merge_request.reload.number_of_potential_approvers }.by(2) end.to change { merge_request.reload.number_of_potential_approvers }.by(2)
end end
......
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