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);
}
if (connector) {
text.push(connector);
}
if (newIssuesText) {
text.push(newIssuesText);
}
text.push('.');
}
return text;
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
......
This diff is collapsed.
......@@ -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
object_store:
enabled: true
path: /mnt/storage/artifacts
object_store:
enabled: true
remote_directory: my-bucket-name
connection:
provider: AWS
aws_access_key_id: S3_KEY_ID
aws_secret_key_id: S3_SECRET_KEY_ID
region: eu-central-1
remote_directory: "artifacts" # The bucket name
connection:
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
```
```bash
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,61 +7,79 @@ feature 'Master creates tag', feature: true do
before do
project.team << [user, :master]
gitlab_sign_in(user)
visit namespace_project_tags_path(project.namespace, project)
end
scenario 'with an invalid name displays an error' do
create_tag_in_form(tag: 'v 1.0', ref: 'master')
context 'from tag list' do
before do
visit namespace_project_tags_path(project.namespace, project)
end
expect(page).to have_content 'Tag name invalid'
end
scenario 'with an invalid name displays an error' do
create_tag_in_form(tag: 'v 1.0', ref: 'master')
scenario 'with an invalid reference displays an error' do
create_tag_in_form(tag: 'v2.0', ref: 'foo')
expect(page).to have_content 'Tag name invalid'
end
expect(page).to have_content 'Target foo is invalid'
end
scenario 'with an invalid reference displays an error' do
create_tag_in_form(tag: 'v2.0', ref: 'foo')
scenario 'that already exists displays an error' do
create_tag_in_form(tag: 'v1.1.0', ref: 'master')
expect(page).to have_content 'Target foo is invalid'
end
expect(page).to have_content 'Tag v1.1.0 already exists'
end
scenario 'that already exists displays an error' do
create_tag_in_form(tag: 'v1.1.0', ref: 'master')
expect(page).to have_content 'Tag v1.1.0 already exists'
end
scenario 'with multiline message displays the message in a <pre> block' do
create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world")
scenario 'with multiline message displays the message in a <pre> block' do
create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world")
expect(current_path).to eq(
namespace_project_tag_path(project.namespace, project, 'v3.0'))
expect(page).to have_content 'v3.0'
page.within 'pre.wrap' do
expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
expect(current_path).to eq(
namespace_project_tag_path(project.namespace, project, 'v3.0'))
expect(page).to have_content 'v3.0'
page.within 'pre.wrap' do
expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
end
end
end
scenario 'with multiline release notes parses the release note as Markdown' do
create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world")
scenario 'with multiline release notes parses the release note as Markdown' do
create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world")
expect(current_path).to eq(
namespace_project_tag_path(project.namespace, project, 'v4.0'))
expect(page).to have_content 'v4.0'
page.within '.description' do
expect(page).to have_content 'Awesome release notes'
expect(page).to have_selector('ul li', count: 2)
expect(current_path).to eq(
namespace_project_tag_path(project.namespace, project, 'v4.0'))
expect(page).to have_content 'v4.0'
page.within '.description' do
expect(page).to have_content 'Awesome release notes'
expect(page).to have_selector('ul li', count: 2)
end
end
scenario 'opens dropdown for ref', js: true do
click_link 'New tag'
ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
page.within ref_row do
ref_input = find('[name="ref"]', visible: false)
expect(ref_input.value).to eq 'master'
expect(find('.dropdown-toggle-text')).to have_content 'master'
find('.js-branch-select').trigger('click')
expect(find('.dropdown-menu')).to have_content 'empty-branch'
end
end
end
scenario 'opens dropdown for ref', js: true do
click_link 'New tag'
ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
page.within ref_row do
ref_input = find('[name="ref"]', visible: false)
expect(ref_input.value).to eq 'master'
expect(find('.dropdown-toggle-text')).to have_content 'master'
context 'from new tag page' do
before do
visit new_namespace_project_tag_path(project.namespace, project)
end
find('.js-branch-select').trigger('click')
it 'description has autocomplete', :js do
find('#release_description').native.send_keys('')
fill_in 'release_description', with: '@'
expect(find('.dropdown-menu')).to have_content 'empty-branch'
expect(page).to have_selector('.atwho-view')
end
end
......
......@@ -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)) }
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
it 'calls gravatar_icon when no User exists with the given email' do
expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
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)
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