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.
## 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)
- Geo: backported fix from 9.3 for big repository sync issues. !2000
......
......@@ -2,6 +2,10 @@
documentation](doc/development/changelog.md) for instructions on adding your own
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)
- Fix the last coverage in trace log should be extracted. !11128 (dosuken123)
......
......@@ -194,7 +194,7 @@ import AuditLogs from './audit_logs';
case 'groups:milestones:update':
new ZenMode();
new gl.DueDateSelectors();
new gl.GLForm($('.milestone-form'));
new gl.GLForm($('.milestone-form'), true);
break;
case 'projects:compare:show':
new gl.Diff();
......@@ -206,7 +206,7 @@ import AuditLogs from './audit_logs';
case 'projects:issues:new':
case 'projects:issues:edit':
shortcut_handler = new ShortcutsNavigation();
new gl.GLForm($('.issue-form'));
new gl.GLForm($('.issue-form'), true);
new IssuableForm($('.issue-form'));
new LabelsSelect();
new MilestoneSelect();
......@@ -218,7 +218,7 @@ import AuditLogs from './audit_logs';
case 'projects:merge_requests:edit':
new gl.Diff();
shortcut_handler = new ShortcutsNavigation();
new gl.GLForm($('.merge-request-form'));
new gl.GLForm($('.merge-request-form'), true);
new IssuableForm($('.merge-request-form'));
new LabelsSelect();
new MilestoneSelect();
......@@ -227,7 +227,7 @@ import AuditLogs from './audit_logs';
break;
case 'projects:tags:new':
new ZenMode();
new gl.GLForm($('.tag-form'));
new gl.GLForm($('.tag-form'), true);
new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
break;
case 'projects:snippets:new':
......@@ -240,11 +240,11 @@ import AuditLogs from './audit_logs';
case 'snippets:edit':
case 'snippets:create':
case 'snippets:update':
new gl.GLForm($('.snippet-form'));
new gl.GLForm($('.snippet-form'), false);
break;
case 'projects:releases:edit':
new ZenMode();
new gl.GLForm($('.release-form'));
new gl.GLForm($('.release-form'), true);
break;
case 'projects:merge_requests:show':
new gl.Diff();
......@@ -510,7 +510,7 @@ import AuditLogs from './audit_logs';
new gl.Wikis();
shortcut_handler = new ShortcutsWiki();
new ZenMode();
new gl.GLForm($('.wiki-form'));
new gl.GLForm($('.wiki-form'), true);
break;
case 'snippets':
shortcut_handler = new ShortcutsNavigation();
......
......@@ -29,7 +29,8 @@
return this.pipeline.triggered || [];
},
triggeredBy() {
return this.pipeline.triggeredBy || [];
const response = this.pipeline.triggered_by;
return response ? [response] : [];
},
hasTriggered() {
return !!this.triggered.length;
......
......@@ -38,7 +38,8 @@ export default {
return this.mr.pipeline.triggered || [];
},
triggeredBy() {
return this.mr.pipeline.triggered_by || [];
const response = this.mr.pipeline.triggered_by;
return response ? [response] : [];
},
},
template: `
......
......@@ -50,27 +50,40 @@ export default {
codeText() {
const { newIssues, resolvedIssues } = this.mr.codeclimateMetrics;
let newIssuesText = '';
let resolvedIssuesText = '';
let text = '';
let newIssuesText;
let resolvedIssuesText;
let text = [];
if (this.hasNoneIssues) {
text = 'No changes to code quality so far.';
text.push('No changes to code quality so far.');
} else if (this.hasIssues) {
if (newIssues.length) {
newIssuesText = `degraded on ${newIssues.length} ${this.pointsText(newIssues)}`;
newIssuesText = ` degraded on ${newIssues.length} ${this.pointsText(newIssues)}`;
}
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
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 =
if user_or_email.is_a?(User)
user_or_email
......@@ -77,7 +77,7 @@ module ApplicationHelper
end
if user
user.avatar_url(size: size) || default_avatar
user.avatar_url(size: size, only_path: only_path) || default_avatar
else
gravatar_icon(user_or_email, size, scale)
end
......
......@@ -36,23 +36,28 @@ module Approvable
#
def number_of_potential_approvers
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
wheres = [
"id IN (#{project.project_authorizations.where(has_access).select(:user_id).to_sql})"
]
users_relation = User.active.where.not(id: approvals.select(:user_id))
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?
wheres << "id IN (#{all_approvers.map(&:id).join(', ')})"
end
users = User
.active
.where("(#{wheres.join(' OR ')}) AND id NOT IN (#{approvals.select(:user_id).to_sql})")
specific_approvers = { id: all_approvers.map(&:id) }
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
# Users in the list of approvers who have not already approved this MR.
......
......@@ -79,7 +79,7 @@
%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
%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;" }
= @approved_by.name
%tr.spacer
......@@ -117,7 +117,7 @@
%tbody
%tr
%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;" }
%a.muted{ href: user_url(@merge_request.author), style: "color:#333333;text-decoration:none;" }
= @merge_request.author.name
......@@ -130,7 +130,7 @@
%tbody
%tr
%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;" }
%a.muted{ href: user_url(@merge_request.assignee), style: "color:#333333;text-decoration:none;" }
= @merge_request.assignee.name
......
......@@ -60,7 +60,7 @@
%tbody
%tr
%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;" }
- if commit.author
%a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
......@@ -76,7 +76,7 @@
%tbody
%tr
%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;" }
- if commit.committer
%a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
......@@ -100,7 +100,7 @@
triggered by
- 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" }
%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;" }
%a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
= @pipeline.user.name
......
......@@ -60,7 +60,7 @@
%tbody
%tr
%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;" }
- if commit.author
%a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
......@@ -76,7 +76,7 @@
%tbody
%tr
%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;" }
- if commit.committer
%a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
......@@ -100,7 +100,7 @@
triggered by
- 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" }
%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;" }
%a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
= @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
# path: shared/artifacts
# object_store:
# enabled: false
# remote_directory: artifacts
# remote_directory: artifacts # The bucket name
# connection:
# provider: AWS # Only AWS supported at the moment
# aws_access_key_id: AWS_ACCESS_KEY_ID
......
# Configuring a Database for GitLab HA
**Warning**
This functionality should be considered alpha. Use with caution.
The steps listed in this document may not leave you with a configuration that matches
what the released version of the software will do.
**Warning**
You can choose to install and manage a database server (PostgreSQL/MySQL)
yourself, or you can use GitLab Omnibus packages to help. GitLab recommends
PostgreSQL. This is the database that will be installed if you use the
Omnibus package to manage your database.
## Configure your own database server
If you're hosting GitLab on a cloud provider, you can optionally use a
managed service for PostgreSQL. For example, AWS offers a managed Relational
Database Service (RDS) that runs PostgreSQL.
Alternatively, you may opt to manage your own PostgreSQL instance or cluster
separate from the GitLab Omnibus package.
If you use a cloud-managed service, or provide your own PostgreSQL instance:
1. Setup PostgreSQL according to the
[database requirements document](../../install/requirements.md#database).
1. Set up a `gitlab` username with a password of your choice. The `gitlab` user
needs privileges to create the `gitlabhq_production` database.
1. Configure the GitLab application servers with the appropriate details.
This step is covered in [Configuring GitLab for HA](gitlab.md).
## Configure using Omnibus
Following these steps should leave you with a database cluster consisting of at least 2 nodes,
using [repmgr](http://www.repmgr.org/) to handle standby synchronization, and failing over.
### On each database node
1. Download/install GitLab Omnibus using **steps 1 and 2** from
[GitLab downloads](https://about.gitlab.com/downloads). Do not complete other
steps on the download page.
1. Create a password hash for the sql user (the default username is `gitlab`)
```
$ echo -n 'PASSWORD+USERNAME' | md5sum
```
1. Create/edit `/etc/gitlab/gitlab.rb` and use the following configuration.
If there is a directive listed below that you do not see in the configuration, be sure to add it.
```ruby
# Disable all components except PostgreSQL
postgresql['enable'] = true
bootstrap['enable'] = false
nginx['enable'] = false
unicorn['enable'] = false
sidekiq['enable'] = false
redis['enable'] = false
prometheus['enable'] = false
gitaly['enable'] = false
gitlab_workhorse['enable'] = false
mailroom['enable'] = false
# PostgreSQL configuration
postgresql['md5_auth_cidr_addresses'] = ['0.0.0.0/0']
postgresql['listen_address'] = '0.0.0.0'
postgresql['sql_user_password'] = 'PASSWORD_HASH' # This is the hash generated in the previous step
postgresql['trust_auth_cidr_addresses'] = ['127.0.0.0/24']
postgresql['hot_standby'] = 'on'
postgresql['wal_level'] = 'replica'
postgresql['max_wal_senders'] = X # Should be set to at least 1 more than the number of nodes in the cluster
postgresql['shared_preload_libraries'] = 'repmgr_funcs' # If this attribute is already defined, append the new value as a comma separated list
postgresql['custom_pg_hba_entries'] = [
{
type: 'local',
database: 'replication',
user: 'gitlab_replicator',
method: 'trust',
},
{
type: 'host',
database: 'replication',
user: 'gitlab_replicator',
cidr: '127.0.0.1/32',
method: 'trust'
},
{
type: 'host',
database: 'replication',
user: 'gitlab_replicator',
cidr: 'XXX.XXX.XXX.XXX/YY', # This should be the CIDR of the network your database nodes are on
method: 'trust'
},
{
type: 'local',
database: 'repmgr',
user: 'gitlab_replicator',
method: 'trust',
},
{
type: 'host',
database: 'repmgr',
user: 'gitlab_replicator',
cidr: '127.0.0.1/32',
method: 'trust'
},
{
type: 'host',
database: 'repmgr',
user: 'gitlab_replicator',
cidr: 'XXX.XXX.XXX.XXX/YY', # This should be the CIDR of the network your database nodes are on
method: 'trust'
}
]
# Disable automatic database migrations
gitlab_rails['auto_migrate'] = false
```
1. Reconfigure GitLab for the new settings to take effect
```
# gitlab-ctl reconfigure
```
1. Create `/var/opt/gitlab/postgresql/repmgr.conf` with the following content. Use a unique integer for the value of node.
```
cluster=gitlab_cluster
node=X
node_name=HOSTNAME
conninfo='host=HOSTNAME user=gitlab_replicator dbname=repmgr'
pg_bindir='/opt/gitlab/embedded/bin'
service_start_command = '/opt/gitlab/bin/gitlab-ctl start postgresql'
service_stop_command = '/opt/gitlab/bin/gitlab-ctl stop postgresql'
service_restart_command = '/opt/gitlab/bin/gitlab-ctl restart postgresql'
promote_command = '/opt/gitlab/embedded/bin/repmgr standby promote -f /var/opt/gitlab/postgresql/repmgr.conf'
follow_command = '/opt/gitlab/embedded/bin/repmgr standby follow -f /var/opt/gitlab/postgresql/repmgr.conf'
```
### On the primary database node
1. Open a database prompt:
```
$ gitlab-psql -d template1
# Output:
psql (DB_VERSION)
Type "help" for help.
template1=#
```
1. Run the following command at the database prompt and you will be asked to
enter the new password for the PostgreSQL superuser.
```
template1=# \password
# Output:
Enter new password:
Enter it again:
```
1. Create the repmgr database:
```
template1=# ALTER USER gitlab_replicator WITH SUPERUSER;
template1=# CREATE DATABASE repmgr WITH OWNER gitlab_replicator;
```
1. Switch to the GitLab database and Enable the `pg_trgm` extension:
```
template1=# \c gitlabhq_production
gitlabhq_production=# CREATE EXTENSION pg_trgm;
# Output:
CREATE EXTENSION
```
1. Exit the database prompt by typing `\q` and Enter.
1. Register the node as the initial master node for the repmgr cluster
```
# su - gitlab-psql
$ repmgr -f /var/opt/gitlab/postgresql/repmgr.conf master register
NOTICE: master node correctly registered for cluster 'gitlab_cluster' with id X (conninfo: host=HOSTNAME user=gitlab_replicator dbname=repmgr)
```
1. Verify the cluster is initialized with one node
```
$ repmgr -f /var/opt/gitlab/postgresql/repmgr.conf cluster show
Role | Name | Upstream | Connection String
----------+-------------|----------|----------------------------------------
* master | HOSTNAME | | host=HOSTNAME user=gitlab_replicator dbname=repmgr
```
### On each standby node
1. Stop postgresql
```
# gitlab-ctl stop postgresql
```
1. Clear out the current data directory
```
# rm -rf /var/opt/gitlab/postgresql/data/*
```
1. Synchronize the data from the primary node:
```
# su - gitlab-psql
$ repmgr -h PRIMARY_HOSTNAME -U gitlab_replicator -d repmgr -D /var/opt/gitlab/postgresql/data/ -f /var/opt/gitlab/postgresql/repmgr.conf standby clone
```
1. Start the database
```
$ gitlab-ctl start postgresql
```
1. Register the node with the cluster
```
$ repmgr -f /var/opt/gitlab/postgresql/repmgr.conf standby register
NOTICE: standby node correctly registered for cluster gitlab_cluster with id X (conninfo: host=HOSTNAME user=gitlab_replicator dbname=repmgr)
```
1. Verify the node now appears in the cluster
```
$ repmgr -f /var/opt/gitlab/postgresql/repmgr.conf cluster show
Role | Name | Upstream | Connection String
----------+------------|------------|------------------------------------------------
* master | MASTER | | host=MASTER_HOSTNAME user=gitlab_replicator dbname=repmgr
standby | STANDBY | MASTER | host=STANDBY_HOSTNAME user=gitlab_replicator dbname=repmgr
```
### (Optional) Enable repmgrd
You can use repmgrd to monitor the database, and automatically failover if it detects the current master is unreachable.
Currently, there is no method of telling the application to automatically fail over to the new master, it must be done
manually. So this step is not required.
If you still want to enable this feature, do the following on each database node
1. Add the following line to `/var/opt/gitlab/postgresql/repmgr.conf`
```
failover=automatic
```
1. Create the log directory
```
install -o -d gitlab-psql /var/log/gitlab/repmgr
```
1. Start repmgrd
```
# su - gitlab-psql -c '/opt/gitlab/embedded/bin/repmgrd -f /var/opt/gitlab/postgresql/repmgr.conf --verbose -d >> /var/log/gitlab/repmgr/repmgr.log 2>&1'
```
### Operations
If your master node is experiencing an issue, you can manually failover.
1. If the master database is still running, shut it down first
```
# gitlab-ctl stop postgresql
```
1. Login to the server that should become the new master and run the following
```
# su - gitlab-psql
$ repmgr -f /var/opt/gitlab/postgresql/repmgr.conf standby promote
```
1. If there are any other standby servers in the cluster, have them follow the new master server
```
# su - gitlab-psql
# repmgr -f /var/opt/gitlab/postgresql/repmgr.conf -h NEW_MASTER -U gitlab_replicator -d repmgr -d /var/opt/gitlab/postgresql/data standby follow
```
1. On the servers that run `gitlab-rails`, set the `gitlab_rails['db_host']` attribute to the new master, and run `gitlab-ctl reconfigure`
1. At this point, you should have a functioning cluster with database writes going to the new master. Now you can recover the failed master server, or remove it from the cluster
1. If you want to remove the node from the cluster, on any other node in the cluster, run:
```
# su - gitlab-psql
$ repmgr -f /var/opt/gitlab/postgresql/repmgr.conf standby unregister --node=X # X should be the value of node in repmgr.conf on the old server
```
1. If the failed master has been recovered, it can be converted to a standby server and follow the new master server[^1]
```
# su - gitlab-psql
# repmgr -f /var/opt/gitlab/postgresql/repmgr.conf -h NEW_MASTER -U gitlab_replicator -d repmgr -d /var/opt/gitlab/postgresql/data standby follow
```
[^1]: When the server is back online, and before you switch it to a standby node, repmgr will report that there are two masters.
If there are any clients that are still writing to the old master, this will cause a split, and the old master will need to be resynced from scratch by performing a `standby clone` before you run `standby follow`
## Configuring the Application
After database setup is complete, the next step is to Configure the GitLab application servers with the appropriate details.
When prompted for `gitlab_rails['db_host']`, this should be set to the master node in your cluster.
This step is covered in [Configuring GitLab for HA](gitlab.md).
---
Read more on high-availability configuration:
1. [Configure Redis](redis.md)
1. [Configure NFS](nfs.md)
1. [Configure the GitLab application servers](gitlab.md)
1. [Configure the load balancers](load_balancer.md)
......@@ -22,6 +22,9 @@ If you use a cloud-managed service, or provide your own PostgreSQL:
## 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
[GitLab downloads](https://about.gitlab.com/downloads). Do not complete other
steps on the download page.
......
......@@ -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
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
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,
there is the option to use object stores like AWS' S3. To do this, set the
`object_store` in your `gitlab.yml`. This relies on valid AWS
credentials to be configured already.
_The artifacts are stored by default in
`/home/git/gitlab/shared/artifacts`._
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following
lines:
```yaml
artifacts:
enabled: true
path: /mnt/storage/artifacts
object_store:
enabled: true
remote_directory: my-bucket-name
remote_directory: "artifacts" # The bucket name
connection:
provider: AWS
aws_access_key_id: S3_KEY_ID
aws_secret_key_id: S3_SECRET_KEY_ID
provider: AWS # Only AWS supported at the moment
aws_access_key_id: AWS_ACESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: eu-central-1
```
This will allow you to migrate existing artifacts to object store,
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. Currently the artifacts migration has to be executed manually:
1. Save the file and [restart GitLab][] for the changes to take effect.
1. Migrate any existing local artifacts to the object storage:
```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
will have the effect that artifacts are _not_ browsable anymore through the web
interface. This limitation will be removed in one of the upcoming releases.
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.
## Expiring artifacts
......@@ -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,
memory and disk I/O.
[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab"
[restart 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#installations-from-source "How to restart GitLab"
[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:
![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
Total running time for a given pipeline would exclude retries and pending
......@@ -229,9 +246,11 @@ respective link in the [Pipelines settings] page.
[stages]: yaml/README.md#stages
[runners]: runners/README.html
[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-6242]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6242
[ce-7931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7931
[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
[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].
This way of triggering can only be used when invoked inside `.gitlab-ci.yml`,
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
build_docs:
stage: deploy
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:
- tags
```
......
......@@ -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.
## 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
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:
- PostgreSQL
- MySQL/MariaDB
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
the right features to support nested groups in an efficient manner; see
<https://gitlab.com/gitlab-org/gitlab-ce/issues/30472> for more information
about this. GitLab Geo also does [not support MySQL](https://docs.gitlab.com/ee/gitlab-geo/database.html#mysql-replication).
We **highly recommend** the use of PostgreSQL instead of MySQL/MariaDB as not all
features of GitLab may work with MySQL/MariaDB:
1. MySQL support for subgroups was [dropped with GitLab 9.3][post].
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
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
available, though the exact requirements depend on the size of the GitLab
installation (e.g. the number of users, projects, etc).
[30472]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472
[zero]: ../update/README.md#upgrading-without-downtime
[post]: https://about.gitlab.com/2017/06/22/gitlab-9-3-released/#dropping-support-for-subgroups-in-mysql
### PostgreSQL Requirements
......@@ -154,6 +129,18 @@ CREATE EXTENSION pg_trgm;
On some systems you may need to install an additional package (e.g.
`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 stores all user sessions and the background task queue.
......@@ -172,6 +159,26 @@ default settings.
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).
## 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
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:
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
- The [Omnibus update guide](http://docs.gitlab.com/omnibus/update/README.html)
......
# 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
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:
[ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772
[permissions]: ../../permissions.md#group
[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
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" }
login_as :user
gitlab_sign_in :user
visit help_path
end
......
......@@ -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')[1].first(:xpath, '..')['data-user-id']).to eq(user2.id.to_s)
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
context 'edit issue' do
......@@ -276,6 +283,13 @@ describe 'New/edit issue', :feature, :js do
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
describe 'sub-group project' do
......
......@@ -96,6 +96,13 @@ describe 'New/edit merge request', feature: true, js: true do
.to end_with(merge_request_path(merge_request))
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
context 'edit merge request' do
......@@ -157,6 +164,13 @@ describe 'New/edit merge request', feature: true, js: true do
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
......
......@@ -39,7 +39,7 @@ describe 'Milestone draggable', feature: true, js: true do
end
it 'assigns issue when it has been dragged to ongoing list' do
login_as(:admin)
gitlab_sign_in(:admin)
create_and_drag_issue
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
context 'when submitting a note' do
before do
login_as :admin
gitlab_sign_in :admin
visit namespace_project_snippet_path(project.namespace, project, snippets[0])
end
......
......@@ -133,6 +133,22 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
expect(page).to have_content('My awesome wiki!')
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
......
......@@ -5,11 +5,10 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
background do
project.team << [user, :master]
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
gitlab_sign_in(user)
visit namespace_project_path(project.namespace, project)
WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
click_link 'Wiki'
visit namespace_project_wikis_path(project.namespace, project)
end
context 'in the user namespace' 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(find('textarea#wiki_content').value).to eq ''
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
......
......@@ -7,6 +7,10 @@ feature 'Master creates tag', feature: true do
before do
project.team << [user, :master]
gitlab_sign_in(user)
end
context 'from tag list' do
before do
visit namespace_project_tags_path(project.namespace, project)
end
......@@ -64,6 +68,20 @@ feature 'Master creates tag', feature: true do
expect(find('.dropdown-menu')).to have_content 'empty-branch'
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)
click_link 'New tag'
......
......@@ -24,6 +24,17 @@ feature 'Master updates tag', feature: true do
expect(page).to have_content 'v1.1.0'
expect(page).to have_content 'Awesome release notes'
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
context 'from a specific tag page' do
......
......@@ -82,6 +82,7 @@ describe ApplicationHelper do
end
describe 'avatar_icon' do
<<<<<<< HEAD
it 'returns an url for the avatar' do
user = create(:user, avatar: File.open(uploaded_image_temp_path))
......@@ -105,19 +106,73 @@ describe ApplicationHelper do
expect(helper.avatar_icon(user.email).to_s)
.to match("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
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)
helper.avatar_icon('foo@example.com', 20, 2)
end
end
end
describe 'using a User' do
it 'returns an URL for the avatar' do
user = create(:user, avatar: File.open(uploaded_image_temp_path))
describe 'using a user' do
context 'when only_path is true' do
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)
.to match("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
context 'when only_path is false' do
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
......
......@@ -7,7 +7,7 @@ const GraphComponent = Vue.extend(graphComponent);
const pipelineJSON = Object.assign(graphJSON, {
triggered: linkedPipelineJSON.triggered,
triggeredBy: linkedPipelineJSON.triggered_by,
triggered_by: linkedPipelineJSON.triggered_by,
});
const defaultPropsData = {
......@@ -83,6 +83,10 @@ describe('graph component', 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 () {
expect(this.component.$el.querySelector('.linked-pipelines-column')).not.toBeNull();
expect(this.component.$el.innerHTML).toContain('Upstream');
......@@ -97,7 +101,7 @@ describe('graph component', function () {
describe('when linked pipelines are not present', function () {
beforeEach(function () {
const pipeline = Object.assign(graphJSON, { triggered: [], triggeredBy: [] });
const pipeline = Object.assign(graphJSON, { triggered: [], triggered_by: [] });
this.component = new GraphComponent({
propsData: { pipeline, isLoading: false },
}).$mount();
......
/* eslint-disable quote-props, quotes, comma-dangle */
export default {
"triggered_by": [{
"triggered_by": {
"id": 129,
"active": true,
"path": "/gitlab-org/gitlab-ce/pipelines/129",
......@@ -56,7 +56,7 @@ export default {
"cancel_path": "/gitlab-org/gitlab-ce/pipelines/129/cancel",
"created_at": "2017-05-24T14:46:20.090Z",
"updated_at": "2017-05-24T14:46:29.906Z"
}],
},
"triggered": [{
"id": 132,
"active": true,
......
......@@ -72,6 +72,34 @@ describe('Merge Request Code Quality', () => {
}, 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', () => {
it('toggles issues', (done) => {
setTimeout(() => {
......
......@@ -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', () => {
expect(el.querySelector('.linked-pipeline-mini-list')).toBeNull();
});
......@@ -149,6 +157,10 @@ describe('MRWidgetPipeline', () => {
}).$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) {
Vue.nextTick(() => {
expect(this.vm.$el.querySelector('.linked-pipeline-mini-list.is-upstream')).not.toBeNull();
......
......@@ -9,7 +9,7 @@ describe('Linked pipeline mini list', () => {
beforeEach(() => {
this.component = new ListComponent({
propsData: {
triggeredBy: mockData.triggered_by,
triggeredBy: [mockData.triggered_by],
},
}).$mount();
});
......
......@@ -536,10 +536,16 @@ describe MergeRequest, models: true do
it "includes project members with developer access and up" do
expect do
developer = create(:user)
project.add_guest(create(:user))
project.add_reporter(create(:user))
project.add_developer(create(:user))
project.add_developer(developer)
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
......
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