Commit b2c281b8 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-10-17

# Conflicts:
#	CHANGELOG.md
#	Gemfile.lock
#	Gemfile.rails5.lock
#	app/assets/javascripts/jobs/components/job_app.vue
#	app/assets/javascripts/jobs/store/getters.js
#	app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
#	locale/gitlab.pot
#	qa/qa/page/project/menu.rb
#	spec/javascripts/pipelines/graph/stage_column_component_spec.js

[ci skip]
parents 0bc528ae 1d7453d3
......@@ -3,6 +3,18 @@ documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.3.6 (2018-10-17)
<<<<<<< HEAD
=======
- No changes.
## 11.3.5 (2018-10-15)
### Fixed (2 changes)
- Fix loading issue on some merge request discussion. !21982
- Fix project deletion when there is a export available. !22276
>>>>>>> upstream/master
- No changes.
......
......@@ -302,7 +302,10 @@ GEM
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-markup (1.7.0)
<<<<<<< HEAD
gitlab-license (1.0.0)
=======
>>>>>>> upstream/master
gitlab-markup (1.6.4)
gitlab-sidekiq-fetcher (0.3.0)
sidekiq (~> 5)
......@@ -1033,7 +1036,10 @@ DEPENDENCIES
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.118.1)
github-markup (~> 1.7.0)
<<<<<<< HEAD
gitlab-license (~> 1.0)
=======
>>>>>>> upstream/master
gitlab-markup (~> 1.6.4)
gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4)
......
......@@ -305,7 +305,10 @@ GEM
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-markup (1.7.0)
<<<<<<< HEAD
gitlab-license (1.0.0)
=======
>>>>>>> upstream/master
gitlab-markup (1.6.4)
gitlab-sidekiq-fetcher (0.3.0)
sidekiq (~> 5)
......@@ -1042,7 +1045,10 @@ DEPENDENCIES
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.118.1)
github-markup (~> 1.7.0)
<<<<<<< HEAD
gitlab-license (~> 1.0)
=======
>>>>>>> upstream/master
gitlab-markup (~> 1.6.4)
gitlab-sidekiq-fetcher
gitlab-styles (~> 2.4)
......
......@@ -18,27 +18,28 @@ export default {
};
</script>
<template>
<div class="blank-state-row">
<div class="blank-state-center">
<h2 class="blank-state-title js-blank-state-title">
{{ s__("Environments|You don't have any environments right now.") }}
</h2>
<div class="empty-state">
<div class="text-content">
<h4 class="blank-state-title js-blank-state-title">
{{ s__("Environments|You don't have any environments right now") }}
</h4>
<p class="blank-state-text">
{{ s__(`Environments|Environments are places where
code gets deployed, such as staging or production.`) }}
<br />
code gets deployed, such as staging or production.`) }}
<a :href="helpPath">
{{ s__("Environments|Read more about environments") }}
</a>
</p>
<a
v-if="canCreateEnvironment"
:href="newPath"
class="btn btn-success js-new-environment-button"
>
{{ s__("Environments|New environment") }}
</a>
<div class="text-center">
<a
v-if="canCreateEnvironment"
:href="newPath"
class="btn btn-success js-new-environment-button"
>
{{ s__("Environments|New environment") }}
</a>
</div>
</div>
</div>
</template>
......@@ -5,9 +5,12 @@
import bp from '~/breakpoints';
import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import Callout from '~/vue_shared/components/callout.vue';
<<<<<<< HEAD
// ee-only start
import SharedRunner from 'ee/jobs/components/shared_runner_limit_block.vue';
// ee-only end
=======
>>>>>>> upstream/master
import createStore from '../store';
import EmptyState from './empty_state.vue';
import EnvironmentsBlock from './environments_block.vue';
......@@ -29,7 +32,10 @@
Log,
LogTopBar,
StuckBlock,
<<<<<<< HEAD
SharedRunner,
=======
>>>>>>> upstream/master
Sidebar,
},
props: {
......@@ -207,6 +213,7 @@
:runners-path="runnerSettingsUrl"
/>
<<<<<<< HEAD
<shared-runner
v-if="shouldRenderSharedRunnerLimitWarning"
class="js-shared-runner-limit"
......@@ -215,6 +222,8 @@
:runners-path="runnerHelpUrl"
/>
=======
>>>>>>> upstream/master
<environments-block
v-if="hasEnvironment"
class="js-job-environment"
......@@ -230,8 +239,13 @@
/>
<!--job log -->
<<<<<<< HEAD
<div
v-if="hasTrace"
=======
<div
v-if="hasTrace"
>>>>>>> upstream/master
class="build-trace-container prepend-top-default">
<log-top-bar
:class="{
......
......@@ -51,6 +51,7 @@ export const isJobStuck = state =>
(!_.isEmpty(state.job.status) && state.job.status.group === 'pending') &&
(!_.isEmpty(state.job.runners) && state.job.runners.available === false);
<<<<<<< HEAD
// ee-only start
/**
* Shared runners limit is only rendered when
......@@ -64,6 +65,8 @@ export const shouldRenderSharedRunnerLimitWarning = state =>
state.job.runners.quota.used >= state.job.runners.quota.limit;
// ee-only end
=======
>>>>>>> upstream/master
export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete;
// prevent babel-plugin-rewire from generating an invalid default during karma tests
......
......@@ -31,12 +31,15 @@ export default {
default: '',
},
<<<<<<< HEAD
hasTriggeredBy: {
type: Boolean,
required: true,
},
},
=======
>>>>>>> upstream/master
methods: {
groupId(group) {
return `ci-badge-${_.escape(group.name)}`;
......
......@@ -8,7 +8,7 @@ module SendsBlob
include SendFileUpload
end
def send_blob(blob, params = {})
def send_blob(repository, blob, params = {})
if blob
headers['X-Content-Type-Options'] = 'nosniff'
......
......@@ -8,7 +8,7 @@ class Projects::AvatarsController < Projects::ApplicationController
def show
@blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git)
send_blob(@blob)
send_blob(@repository, @blob)
end
def destroy
......
......@@ -83,7 +83,7 @@ class Projects::BlobController < Projects::ApplicationController
def destroy
create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
success_path: -> { project_tree_path(@project, @branch_name) },
success_path: -> { after_delete_path },
failure_view: :show,
failure_path: project_blob_path(@project, @id))
end
......@@ -191,6 +191,15 @@ class Projects::BlobController < Projects::ApplicationController
end
# rubocop: enable CodeReuse/ActiveRecord
def after_delete_path
branch = BranchesFinder.new(@repository, search: @ref).execute.first
if @repository.tree(branch.target, tree_path).entries.empty?
project_tree_path(@project, @ref)
else
project_tree_path(@project, File.join(@ref, tree_path))
end
end
def editor_variables
@branch_name = params[:branch_name]
......@@ -255,9 +264,6 @@ class Projects::BlobController < Projects::ApplicationController
def show_json
set_last_commit_sha
path_segments = @path.split('/')
path_segments.pop
tree_path = path_segments.join('/')
json = {
id: @blob.id,
......@@ -283,4 +289,8 @@ class Projects::BlobController < Projects::ApplicationController
render json: json
end
def tree_path
@path.rpartition('/').first
end
end
......@@ -12,6 +12,6 @@ class Projects::RawController < Projects::ApplicationController
def show
@blob = @repository.blob_at(@commit.id, @path)
send_blob(@blob, inline: (params[:inline] != 'false'))
send_blob(@repository, @blob, inline: (params[:inline] != 'false'))
end
end
......@@ -2,6 +2,7 @@
class Projects::WikisController < Projects::ApplicationController
include PreviewMarkdown
include SendsBlob
include Gitlab::Utils::StrongMemoize
before_action :authorize_read_wiki!
......@@ -26,16 +27,8 @@ class Projects::WikisController < Projects::ApplicationController
set_encoding_error unless valid_encoding?
render 'show'
elsif file = @project_wiki.find_file(params[:id], params[:version_id])
response.headers['Content-Security-Policy'] = "default-src 'none'"
response.headers['X-Content-Security-Policy'] = "default-src 'none'"
send_data(
file.raw_data,
type: file.mime_type,
disposition: 'inline',
filename: file.name
)
elsif file_blob
send_blob(@project_wiki.repository, file_blob)
elsif can?(current_user, :create_wiki, @project) && view_param == 'create'
@page = build_page(title: params[:id])
......@@ -164,4 +157,14 @@ class Projects::WikisController < Projects::ApplicationController
def set_encoding_error
flash.now[:notice] = "The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository."
end
def file_blob
strong_memoize(:file_blob) do
commit = @project_wiki.repository.commit(@project_wiki.default_branch)
next unless commit
@project_wiki.repository.blob_at(commit.id, params[:id])
end
end
end
......@@ -150,7 +150,9 @@ module BlobHelper
# example of Javascript) we tell the browser of the victim not to
# execute untrusted data.
def safe_content_type(blob)
if blob.text?
if blob.extension == 'svg'
blob.mime_type
elsif blob.text?
'text/plain; charset=utf-8'
elsif blob.image?
blob.content_type
......@@ -159,6 +161,12 @@ module BlobHelper
end
end
def content_disposition(blob, inline)
return 'attachment' if blob.extension == 'svg'
inline ? 'inline' : 'attachment'
end
def ref_project
@ref_project ||= @target_project || @project
end
......
......@@ -6,7 +6,7 @@ module WorkhorseHelper
# Send a Git blob through Workhorse
def send_git_blob(repository, blob, inline: true)
headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
headers['Content-Disposition'] = inline ? 'inline' : 'attachment'
headers['Content-Disposition'] = content_disposition(blob, inline)
headers['Content-Type'] = safe_content_type(blob)
render plain: ""
end
......
......@@ -14,6 +14,9 @@ class JiraService < IssueTrackerService
format: { with: Gitlab::Regex.jira_transition_id_regex, message: "transition ids can have only numbers which can be split with , or ;" },
allow_blank: true
# JIRA cloud version is deprecating authentication via username and password.
# We should use username/password for JIRA server and email/api_token for JIRA cloud,
# for more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/49936.
prop_accessor :username, :password, :url, :api_url, :jira_issue_transition_id, :title, :description
before_update :reset_password
......@@ -95,8 +98,8 @@ class JiraService < IssueTrackerService
[
{ type: 'text', name: 'url', title: 'Web URL', placeholder: 'https://jira.example.com', required: true },
{ type: 'text', name: 'api_url', title: 'JIRA API URL', placeholder: 'If different from Web URL' },
{ type: 'text', name: 'username', placeholder: '', required: true },
{ type: 'password', name: 'password', placeholder: '', required: true },
{ type: 'text', name: 'username', title: 'Username or Email', placeholder: 'Use a username for server version and an email for cloud version', required: true },
{ type: 'password', name: 'password', title: 'Password or API token', placeholder: 'Use a password for server version and an API token for cloud version', required: true },
{ type: 'text', name: 'jira_issue_transition_id', title: 'Transition ID(s)', placeholder: 'Use , or ; to separate multiple transition IDs' }
]
end
......
......@@ -56,7 +56,6 @@ class PrometheusService < MonitoringService
name: 'api_url',
title: 'API URL',
placeholder: s_('PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/'),
help: s_('PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server.'),
required: true
}
]
......
......@@ -492,7 +492,20 @@ class Repository
end
def blob_at(sha, path)
Blob.decorate(raw_repository.blob_at(sha, path), project)
blob = Blob.decorate(raw_repository.blob_at(sha, path), project)
# Don't attempt to return a special result if there is no blob at all
return unless blob
# Don't attempt to return a special result unless we're looking at HEAD
return blob unless head_commit&.sha == sha
case path
when head_tree&.readme_path
ReadmeBlob.new(blob, self)
else
blob
end
rescue Gitlab::Git::Repository::NoRepository
nil
end
......@@ -574,9 +587,7 @@ class Repository
cache_method :merge_request_template_names, fallback: []
def readme
if readme = tree(:head)&.readme
ReadmeBlob.new(readme, self)
end
head_tree&.readme
end
def rendered_readme
......
......@@ -2,6 +2,7 @@
class Tree
include Gitlab::MarkupHelper
include Gitlab::Utils::StrongMemoize
attr_accessor :repository, :sha, :path, :entries
......@@ -16,32 +17,36 @@ class Tree
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive)
end
def readme
return @readme if defined?(@readme)
available_readmes = blobs.select do |blob|
Gitlab::FileDetector.type_of(blob.name) == :readme
end
previewable_readmes = available_readmes.select do |blob|
previewable?(blob.name)
end
plain_readmes = available_readmes.select do |blob|
plain?(blob.name)
def readme_path
strong_memoize(:readme_path) do
available_readmes = blobs.select do |blob|
Gitlab::FileDetector.type_of(blob.name) == :readme
end
previewable_readmes = available_readmes.select do |blob|
previewable?(blob.name)
end
plain_readmes = available_readmes.select do |blob|
plain?(blob.name)
end
# Prioritize previewable over plain readmes
entry = previewable_readmes.first || plain_readmes.first
next nil unless entry
if path == '/'
entry.name
else
File.join(path, entry.name)
end
end
end
# Prioritize previewable over plain readmes
readme_tree = previewable_readmes.first || plain_readmes.first
# Return if we can't preview any of them
if readme_tree.nil?
return @readme = nil
def readme
strong_memoize(:readme) do
repository.blob_at(sha, readme_path) if readme_path
end
readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
@readme = repository.blob_at(sha, readme_path)
end
def trees
......
......@@ -160,7 +160,9 @@ class WikiPage
# Returns boolean True or False if this instance
# is an old version of the page.
def historical?
@page.historical? && last_version.sha != version.sha
return false unless last_commit_sha && version
@page.historical? && last_commit_sha != version.sha
end
# Returns boolean True or False if this instance
......
......@@ -86,7 +86,7 @@
- if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to project_issues_path(@project), class: 'shortcuts-issues' do
= link_to project_issues_path(@project), class: 'shortcuts-issues qa-issues-item' do
.nav-icon-container
= sprite_icon('issues')
%span.nav-item-name
......@@ -115,7 +115,7 @@
= boards_link_text
= nav_link(controller: :labels) do
= link_to project_labels_path(@project), title: _('Labels') do
= link_to project_labels_path(@project), title: _('Labels'), class: 'qa-labels-link' do
%span
= _('Labels')
......
......@@ -8,7 +8,7 @@
- if can_admin_label
- content_for(:header_content) do
.nav-controls
= link_to _('New label'), new_project_label_path(@project), class: "btn btn-success"
= link_to _('New label'), new_project_label_path(@project), class: "btn btn-success qa-label-create-new"
- if labels_or_filters
#promote-label-modal
......
......@@ -2,4 +2,5 @@
= link_to wiki_page.title, project_wiki_path(@project, wiki_page)
%small (#{wiki_page.format})
.float-right
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
- if wiki_page.last_version
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
......@@ -11,8 +11,9 @@
.nav-text
%h2.wiki-page-title= @page.title.capitalize
%span.wiki-last-edit-by
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.last_version.authored_date)}
- if @page.last_version
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.last_version.authored_date)}
.nav-controls
= render 'main_links'
......
......@@ -24,6 +24,6 @@
- elsif type == 'select'
= form.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control", disabled: disabled}
- elsif type == 'password'
= form.password_field name, autocomplete: "new-password", class: "form-control", required: value.blank? && required, disabled: disabled
= form.password_field name, autocomplete: "new-password", placeholder: placeholder, class: "form-control", required: value.blank? && required, disabled: disabled
- if help
%span.form-text.text-muted= help
......@@ -20,7 +20,7 @@
= hidden_field_tag data_options[:field_name], use_id ? label.try(:id) : label.try(:title), id: nil
.dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{ class: classes.join(' '), type: "button", data: dropdown_data }
%button.dropdown-menu-toggle.js-label-select.js-multiselect.qa-issuable-label{ class: classes.join(' '), type: "button", data: dropdown_data }
- apply_is_default_styles = (selected.nil? || selected.empty?) && !no_default_styles
%span.dropdown-toggle-text{ class: ("is-default" if apply_is_default_styles) }
= multi_label_name(selected, label_name)
......
......@@ -98,7 +98,7 @@
= icon('spinner spin', class: 'hidden block-loading', 'aria-hidden': 'true')
- if can_edit_issuable
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
.value.issuable-show-labels.dont-hide.hide-collapsed{ class: ("has-labels" if selected_labels.any?) }
.value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any?
- selected_labels.each do |label|
= link_to_label(label, subject: issuable.project, type: issuable.to_ability_name)
......
......@@ -4,18 +4,18 @@
.form-group.row
= f.label :title, class: 'col-form-label col-sm-2'
.col-sm-10
= f.text_field :title, class: "form-control", required: true, autofocus: true
= f.text_field :title, class: "form-control qa-label-title", required: true, autofocus: true
.form-group.row
= f.label :description, class: 'col-form-label col-sm-2'
.col-sm-10
= f.text_field :description, class: "form-control js-quick-submit"
= f.text_field :description, class: "form-control js-quick-submit qa-label-description"
.form-group.row
= f.label :color, "Background color", class: 'col-form-label col-sm-2'
.col-sm-10
.input-group
.input-group-prepend
.input-group-text.label-color-preview &nbsp;
= f.text_field :color, class: "form-control"
= f.text_field :color, class: "form-control qa-label-color"
.form-text.text-muted
Choose any color.
%br
......@@ -30,5 +30,5 @@
- if @label.persisted?
= f.submit 'Save changes', class: 'btn btn-success js-save-button'
- else
= f.submit 'Create label', class: 'btn btn-success js-save-button'
= f.submit 'Create label', class: 'btn btn-success js-save-button qa-label-create-button'
= link_to 'Cancel', back_path, class: 'btn btn-cancel'
---
title: On deletion of a file in sub directory in web IDE redirect to the sub directory
instead of project root
merge_request: 21465
author: George Thomas @thegeorgeous
type: changed
---
title: Use cached readme contents when available
merge_request: 22325
author:
type: performance
---
title: Fix a bug displaying certain wiki pages
merge_request: 22377
author:
type: fixed
---
title: Fix bug with wiki attachments content disposition
merge_request: 22220
author:
type: fixed
---
title: Remove prometheus configuration help text
merge_request: 22413
author: George Tsiolis
type: other
---
title: Update environments empty state
merge_request: 22297
author: George Tsiolis
type: other
---
title: Update JIRA service UI to accept email and API token
merge_request:
author:
type: other
---
description: "Set and configure Git protocol v2"
---
# Configuring Git Protocol v2
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/46555) in GitLab 11.4.
---
Git protocol v2 improves the v1 wire protocol in several ways and is
enabled by default in GitLab for HTTP requests. In order to enable SSH,
further configuration is needed by the administrator.
More details about the new features and improvements are available in
the [Google Open Source Blog](https://opensource.googleblog.com/2018/05/introducing-git-protocol-version-2.html)
and the [protocol documentation](https://github.com/git/git/blob/master/Documentation/technical/protocol-v2.txt).
## Requirements
From the client side, `git` `v2.18.0` or newer must be installed.
From the server side, if we want to configure SSH we need to set the `sshd`
server to accept the `GIT_PROTOCOL` environment,
```
# /etc/ssh/sshd_config
AcceptEnv GIT_PROTOCOL
```
Once configured, restart the SSH daemon. In Ubuntu, run:
```sh
sudo service ssh restart
```
## Instructions
In order to use the new protocol, clients need to either pass the configuration
`-c protocol.version=2` to the git command, or set it globally:
```sh
git config --global protocol.version 2
```
### HTTP connections
Verify Git v2 is used by the client:
```sh
GIT_TRACE_CURL=1 git -c protocol.version=2 ls-remote https://your-gitlab-instance.com/group/repo.git 2>&1 | grep Git-Protocol
```
You should see that the `Git-Protocol` header is sent:
```
16:29:44.577888 http.c:657 => Send header: Git-Protocol: version=2
```
Verify Git v2 is used by the server:
```sh
GIT_TRACE_PACKET=1 git -c protocol.version=2 ls-remote https://your-gitlab-instance.com/group/repo.git 2>&1 | head
```
Example response using Git protocol v2:
```sh
$ GIT_TRACE_PACKET=1 git -c protocol.version=2 ls-remote https://your-gitlab-instance.com/group/repo.git 2>&1 | head
10:42:50.574485 pkt-line.c:80 packet: git< # service=git-upload-pack
10:42:50.574653 pkt-line.c:80 packet: git< 0000
10:42:50.574673 pkt-line.c:80 packet: git< version 2
10:42:50.574679 pkt-line.c:80 packet: git< agent=git/2.18.1
10:42:50.574684 pkt-line.c:80 packet: git< ls-refs
10:42:50.574688 pkt-line.c:80 packet: git< fetch=shallow
10:42:50.574693 pkt-line.c:80 packet: git< server-option
10:42:50.574697 pkt-line.c:80 packet: git< 0000
10:42:50.574817 pkt-line.c:80 packet: git< version 2
10:42:50.575308 pkt-line.c:80 packet: git< agent=git/2.18.1
```
### SSH Connections
Verify Git v2 is used by the client:
```sh
GIT_SSH_COMMAND="ssh -v" git -c protocol.version=2 ls-remote ssh://your-gitlab-instance.com:group/repo.git 2>&1 |grep GIT_PROTOCOL
```
You should see that the `GIT_PROTOCOL` environment variable is sent:
```
debug1: Sending env GIT_PROTOCOL = version=2
```
For the server side, you can use the [same examples from HTTP](#http-connections), changing the
URL to use SSH.
......@@ -37,6 +37,30 @@ options:
circumstances it could lead to data loss if a failure occurs before data has
synced.
### Known issues
On some customer systems, we have seen NFS clients slow precipitously due to
[excessive network traffic from numerous `TEST_STATEID` NFS
messages](https://gitlab.com/gitlab-org/gitlab-ce/issues/52017). This is
likely due to a [Linux kernel
bug](https://bugzilla.redhat.com/show_bug.cgi?id=1552203) that may be fixed in
[more recent kernels with this
commit](https://github.com/torvalds/linux/commit/95da1b3a5aded124dd1bda1e3cdb876184813140).
Users encountering a similar issue may be advised to disable the NFS server
delegation feature, which is an optimization to reduce the number of network
round-trips needed to read or write files. To disable NFS server delegations
on an Linux NFS server, do the following:
1. On the NFS server, run:
```sh
echo 0 > /proc/sys/fs/leases-enable
sysctl -w fs.leases-enable=0
```
2. Restart the NFS server process. For example, on CentOS run `service nfs restart`.
## AWS Elastic File System
GitLab strongly recommends against using AWS Elastic File System (EFS).
......@@ -63,10 +87,11 @@ GitLab.com:
10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
```
Notice several options that you should consider using:
Note there are several options that you should consider using:
| Setting | Description |
| ------- | ----------- |
| `vers=4.1` |NFS v4.1 should be used instead of v4.0 because there is a Linux [NFS client bug in v4.0](https://gitlab.com/gitlab-org/gitaly/issues/1339) that can cause significant problems due to stale data.
| `nofail` | Don't halt boot process waiting for this mount to become available
| `lookupcache=positive` | Tells the NFS client to honor `positive` cache results but invalidates any `negative` cache results. Negative cache results cause problems with Git. Specifically, a `git push` can fail to register uniformly across all NFS clients. The negative cache causes the clients to 'remember' that the files did not exist previously.
......
......@@ -151,6 +151,7 @@ created in snippets, wikis, and repos.
- [Custom Git hooks](custom_hooks.md): Custom Git hooks (on the filesystem) for when webhooks aren't enough.
- [Git LFS configuration](../workflow/lfs/lfs_administration.md): Learn how to configure LFS for GitLab.
- [Housekeeping](housekeeping.md): Keep your Git repositories tidy and fast.
- [Configuring Git Protocol v2](git_protocol.md): Git protocol version 2 support.
## Monitoring GitLab
......
......@@ -148,6 +148,9 @@ This label documents the planned timeline & urgency which is used to measure aga
| ~P3 | Medium Priority | Within the next 3 releases (approx one quarter or 90 days) |
| ~P4 | Low Priority | Anything outside the next 3 releases (more than one quarter or 120 days) |
If an issue seems to fall between two priority labels, assign it to the higher-
priority label.
## Severity labels
Severity labels help us clearly communicate the impact of a ~bug on users.
......@@ -159,6 +162,10 @@ Severity labels help us clearly communicate the impact of a ~bug on users.
| ~S3 | Major Severity | Broken Feature, workaround acceptable | Can create merge requests only from the Merge Requests page, not through the Issue. |
| ~S4 | Low Severity | Functionality inconvenience or cosmetic issue | Label colors are incorrect / not being displayed. |
If an issue seems to fall between two severity labels, even taking the
[severity impact guidance](#severity-impact-guidance) into account, assign
it to the higher-severity label.
### Severity impact guidance
Severity levels can be applied further depending on the facet of the impact; e.g. Affected customers, GitLab.com availability, performance and etc. The below is a guideline.
......
......@@ -2,20 +2,21 @@
> [Introduced][ce-30469] in GitLab 9.3.
Conversational Development Index (ConvDev) gives you an overview of your entire
instance's feature usage, from idea to production. It looks at your usage in the
past 30 days, averaged over the number of active users in that time period. It also
provides a lead score per feature, which is calculated based on GitLab's analysis
of top performing instances, based on [usage ping data][ping] that GitLab has
The Conversational Development Index (ConvDev Index) gives you an overview of your entire
instance's adoption of [Concurrent DevOps](https://about.gitlab.com/concurrent-devops/)
from planning to monitoring. It displays the usage of these GitLab features over
the last 30 days, averaged over the number of active users in that time period. It also
provides a Lead score per feature, which is calculated based on GitLab's analysis
of top-performing instances based on [usage ping data][ping] that GitLab has
collected. Your score is compared to the lead score, expressed as a percentage.
The overall index score is an average over all your feature scores.
Your overall index score is an average of all your feature score percentages.
![ConvDev index](img/convdev_index.png)
The page also provides helpful links to articles and GitLab docs, to help you
improve your scores.
Your GitLab instance's usage ping must be activated in order to use this feature.
Your GitLab instance's [usage ping][ping] must be activated in order to use this feature.
Usage ping data is aggregated on GitLab's servers for analysis. Your usage
information is **not sent** to any other GitLab instances.
......
......@@ -45,56 +45,11 @@ project in Jira and then enter the correct values in GitLab.
### Configuring Jira
We need to create a user in Jira which will have access to all projects that
need to integrate with GitLab. Login to your Jira instance as admin and under
*Administration*, go to *User Management* and create a new user.
When connecting to **JIRA Server**, which supports basic authentication, a **username and password** are required. Check the link below and proceed to the next step:
* [Setting up an user in JIRA server](jira_server_configuration.md)
As an example, we'll create a user named `gitlab` and add it to the `Jira-developers`
group.
**It is important that the user `gitlab` has 'write' access to projects in Jira**
We have split this stage in steps so it is easier to follow.
1. Log in to your Jira instance as an administrator and under **Administration**
go to **User Management** to create a new user.
![Jira user management link](img/jira_user_management_link.png)
1. The next step is to create a new user (e.g., `gitlab`) who has write access
to projects in Jira. Enter the user's name and a _valid_ e-mail address
since Jira sends a verification e-mail to set up the password.
_**Note:** Jira creates the username automatically by using the e-mail
prefix. You can change it later, if needed. Our integration does not support SSO (such as SAML). You will need to create
an HTTP basic authentication password. You can do this by visiting the user
profile, looking up the username, and setting a password._
![Jira create new user](img/jira_create_new_user.png)
1. Now, let's create a `gitlab-developers` group which will have write access
to projects in Jira. Go to the **Groups** tab and select **Create group**.
![Jira create new user](img/jira_create_new_group.png)
Give it an optional description and click **Create group**.
![Jira create new group](img/jira_create_new_group_name.png)
1. To give the newly-created group 'write' access, go to
**Application access ➔ View configuration** and add the `gitlab-developers`
group to Jira Core.
![Jira group access](img/jira_group_access.png)
1. Add the `gitlab` user to the `gitlab-developers` group by going to
**Users ➔ GitLab user ➔ Add group** and selecting the `gitlab-developers`
group from the dropdown menu. Notice that the group says _Access_, which is
intended as part of this process.
![Jira add user to group](img/jira_add_user_to_group.png)
The Jira configuration is complete. Write down the new Jira username and its
password as they will be needed when configuring GitLab in the next section.
When connecting to **JIRA Cloud**, which supports authentication via API token, an **email and API token**, are required. Check the link below and proceed to the next step:
* [Setting up an user in JIRA cloud](jira_cloud_configuration.md)
### Configuring GitLab
......@@ -117,8 +72,8 @@ in the table below.
| ----- | ----------- |
| `Web URL` | The base URL to the Jira instance web interface which is being linked to this GitLab project. E.g., `https://Jira.example.com`. |
| `Jira API URL` | The base URL to the Jira instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. |
| `Username` | The user name created in [configuring Jira step](#configuring-jira). Using the email address will cause `401 unauthorized`. |
| `Password` |The password of the user created in [configuring Jira step](#configuring-jira). |
| `Username/Email` | Created when [configuring Jira step](#configuring-jira). Use `username` for **JIRA server** or `email` for **JIRA cloud**. |
| `Password/API token` |Created in [configuring Jira step](#configuring-jira). Use `password` for **JIRA server** or `API token` for **JIRA cloud**. |
| `Transition ID` | This is the ID of a transition that moves issues to the desired state. It is possible to insert transition ids separated by `,` or `;` which means the issue will be moved to each state after another using the given order. **Closing Jira issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
### Obtaining a transition ID
......
# Creating an API token in JIRA cloud
An API token is needed when integrating with JIRA Cloud, follow the steps
below to create one:
1. Log in to https://id.atlassian.com with your email.
2. **Click API tokens**, then **Create API token**.
![JIRA API token](img/jira_api_token_menu.png)
![JIRA API token](img/jira_api_token.png)
3. Make sure to write down your new API token as you will need it in the next [steps](jira.md#configuring-gitlab).
NOTE: **Note**
It is important that the user associated with this email has 'write' access to projects in JIRA.
The JIRA configuration is complete. You are going to need this new created token and the email you used to log in when [configuring GitLab in the next section](jira.md#configuring-gitlab).
# Creating a username and password for JIRA server
We need to create a user in Jira which will have access to all projects that
need to integrate with GitLab. Login to your Jira instance as admin and under
*Administration*, go to *User Management* and create a new user.
As an example, we'll create a user named `gitlab` and add it to the `Jira-developers`
group.
NOTE: **Note**
It is important that the user `gitlab` has 'write' access to projects in Jira.
We have split this stage in steps so it is easier to follow.
1. Log in to your Jira instance as an administrator and under **Administration**
go to **User Management** to create a new user.
![Jira user management link](img/jira_user_management_link.png)
2. The next step is to create a new user (e.g., `gitlab`) who has write access
to projects in Jira. Enter the user's name and a _valid_ e-mail address
since Jira sends a verification e-mail to set up the password.
_**Note:** Jira creates the username automatically by using the e-mail
prefix. You can change it later, if needed. Our integration does not support SSO (such as SAML). You will need to create
an HTTP basic authentication password. You can do this by visiting the user
profile, looking up the username, and setting a password._
![Jira create new user](img/jira_create_new_user.png)
3. Create a `gitlab-developers` group which will have write access
to projects in Jira. Go to the **Groups** tab and select **Create group**.
![Jira create new user](img/jira_create_new_group.png)
Give it an optional description and click **Create group**.
![Jira create new group](img/jira_create_new_group_name.png)
4. To give the newly-created group 'write' access, go to
**Application access > View configuration** and add the `gitlab-developers`
group to Jira Core.
![Jira group access](img/jira_group_access.png)
5. Add the `gitlab` user to the `gitlab-developers` group by going to
**Users > GitLab user > Add group** and selecting the `gitlab-developers`
group from the dropdown menu. Notice that the group says _Access_, which is
intended as part of this process.
![Jira add user to group](img/jira_add_user_to_group.png)
The Jira configuration is complete. Write down the new Jira username and its
password as they will be needed when [configuring GitLab in the next section](jira.md#configuring-gitlab).
......@@ -72,7 +72,7 @@ module Gitlab
# and `safe_max_bytes`
#
# :expanded ::
# If true, patch raw data will not be included in the diff after
# If false, patch raw data will not be included in the diff after
# `max_files`, `max_lines` or any of the limits in `limits` are
# exceeded
def filter_diff_options(options, default_options = {})
......
......@@ -3024,7 +3024,7 @@ msgstr ""
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now."
msgid "Environments|You don't have any environments right now"
msgstr ""
msgid "Environments|protected"
......@@ -6336,10 +6336,14 @@ msgstr ""
msgid "Promote to Group Milestone"
msgstr ""
<<<<<<< HEAD
msgid "Promote to group label"
msgstr ""
msgid "Promotions|Don't show me this again"
=======
msgid "PrometheusService|Common metrics"
>>>>>>> upstream/master
msgstr ""
msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
......
......@@ -46,6 +46,7 @@ module QA
autoload :Group, 'qa/factory/resource/group'
autoload :Issue, 'qa/factory/resource/issue'
autoload :Project, 'qa/factory/resource/project'
autoload :Label, 'qa/factory/resource/label'
autoload :MergeRequest, 'qa/factory/resource/merge_request'
autoload :ProjectImportedFromGithub, 'qa/factory/resource/project_imported_from_github'
autoload :MergeRequestFromFork, 'qa/factory/resource/merge_request_from_fork'
......@@ -241,6 +242,11 @@ module QA
autoload :Banner, 'qa/page/layout/banner'
end
module Label
autoload :New, 'qa/page/label/new'
autoload :Index, 'qa/page/label/index'
end
module MergeRequest
autoload :New, 'qa/page/merge_request/new'
autoload :Show, 'qa/page/merge_request/show'
......
require 'securerandom'
module QA
module Factory
module Resource
class Label < Factory::Base
attr_accessor :title,
:description,
:color
product(:title) { |factory| factory.title }
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-label'
end
def initialize
@title = "qa-test-#{SecureRandom.hex(8)}"
@description = 'This is a test label'
@color = '#0033CC'
end
def fabricate!
project.visit!
Page::Project::Menu.act { go_to_labels }
Page::Label::Index.act { go_to_new_label }
Page::Label::New.perform do |page|
page.fill_title(@title)
page.fill_description(@description)
page.fill_color(@color)
page.create_label
end
end
end
end
end
end
......@@ -52,6 +52,10 @@ module QA
page.fill_title(@title)
page.fill_description(@description)
page.choose_milestone(@milestone) if @milestone
labels.each do |label|
page.select_label(label)
end
page.create_merge_request
end
end
......
module QA
module Page
module Label
class Index < Page::Base
view 'app/views/projects/labels/index.html.haml' do
element :label_create_new
end
def go_to_new_label
click_element :label_create_new
end
end
end
end
end
module QA
module Page
module Label
class New < Page::Base
view 'app/views/shared/labels/_form.html.haml' do
element :label_title
element :label_description
element :label_color
element :label_create_button
end
def create_label
click_element :label_create_button
end
def fill_title(title)
fill_element :label_title, title
end
def fill_description(description)
fill_element :label_description, description
end
def fill_color(color)
fill_element :label_color, color
end
end
end
end
end
......@@ -22,6 +22,10 @@ module QA
element :issuable_dropdown_menu_milestone
end
view 'app/views/shared/issuable/_label_dropdown.html.haml' do
element :issuable_label
end
def create_merge_request
click_element :issuable_create_button
end
......@@ -40,6 +44,12 @@ module QA
click_on milestone.title
end
end
def select_label(label)
click_element :issuable_label
click_link label.title
end
end
end
end
......
......@@ -25,6 +25,10 @@ module QA
element :squash_checkbox
end
view 'app/views/shared/issuable/_sidebar.html.haml' do
element :labels_block
end
def fast_forward_possible?
!has_text?('Fast-forward merge is not possible')
end
......@@ -66,6 +70,13 @@ module QA
end
end
def has_label?(label)
page.within(element_selector_css(:labels_block)) do
element = find('span', text: label)
!element.nil?
end
end
def merge!
# The merge button is disabled on load
wait do
......
......@@ -22,6 +22,7 @@ module QA
element :activity_link, "title: _('Activity')" # rubocop:disable QA/ElementWithPattern
element :wiki_link_text, "Wiki" # rubocop:disable QA/ElementWithPattern
element :milestones_link
element :labels_link
end
view 'app/assets/javascripts/fly_out_nav.js' do
......@@ -104,6 +105,7 @@ module QA
end
end
<<<<<<< HEAD
def click_repository
within_sidebar do
click_link('Repository')
......@@ -114,10 +116,22 @@ module QA
within_sidebar do
click_link('Epics')
end
=======
def go_to_labels
hover_issues { click_element :labels_link }
>>>>>>> upstream/master
end
private
def hover_issues
within_sidebar do
find_element(:issues_item).hover
yield
end
end
def hover_settings
within_sidebar do
find('.qa-settings-item').hover
......
......@@ -16,16 +16,26 @@ module QA
milestone.project = current_project
end
new_label = Factory::Resource::Label.fabricate! do |label|
label.project = current_project
label.title = 'qa-mr-test-label'
label.description = 'Merge Request label'
end
Factory::Resource::MergeRequest.fabricate! do |merge_request|
merge_request.title = 'This is a merge request with a milestone'
merge_request.description = 'Great feature with milestone'
merge_request.project = current_project
merge_request.milestone = current_milestone
merge_request.labels.push(new_label)
end
expect(page).to have_content('This is a merge request with a milestone')
expect(page).to have_content('Great feature with milestone')
expect(page).to have_content(/Opened [\w\s]+ ago/)
Page::MergeRequest::Show.perform do |merge_request|
expect(merge_request).to have_content('This is a merge request with a milestone')
expect(merge_request).to have_content('Great feature with milestone')
expect(merge_request).to have_content(/Opened [\w\s]+ ago/)
expect(merge_request).to have_label(new_label.title)
end
Page::Issuable::Sidebar.perform do |sidebar|
expect(sidebar).to have_milestone(current_milestone.title)
......
......@@ -343,4 +343,76 @@ describe Projects::BlobController do
end
end
end
describe 'DELETE destroy' do
let(:user) { create(:user) }
let(:project_root_path) { project_tree_path(project, 'master') }
before do
project.add_maintainer(user)
sign_in(user)
end
context 'for a file in a subdirectory' do
let(:default_params) do
{
namespace_id: project.namespace,
project_id: project,
id: 'master/files/whitespace',
original_branch: 'master',
branch_name: 'master',
commit_message: 'Delete whitespace'
}
end
let(:after_delete_path) { project_tree_path(project, 'master/files') }
it 'redirects to the sub directory' do
delete :destroy, default_params
expect(response).to redirect_to(after_delete_path)
end
end
context 'if deleted file is the last one in a subdirectory' do
let(:default_params) do
{
namespace_id: project.namespace,
project_id: project,
id: 'master/bar/branch-test.txt',
original_branch: 'master',
branch_name: 'master',
commit_message: 'Delete whitespace'
}
end
it 'redirects to the project root' do
delete :destroy, default_params
expect(response).to redirect_to(project_root_path)
end
context 'when deleting a file in a branch other than master' do
let(:default_params) do
{
namespace_id: project.namespace,
project_id: project,
id: 'binary-encoding/foo/bar/.gitkeep',
original_branch: 'binary-encoding',
branch_name: 'binary-encoding',
commit_message: 'Delete whitespace'
}
end
let(:after_delete_path) { project_tree_path(project, 'binary-encoding') }
it 'redirects to the project root of the branch' do
delete :destroy, default_params
expect(response).to redirect_to(after_delete_path)
end
end
end
end
end
......@@ -22,20 +22,18 @@ describe Projects::WikisController do
subject { get :show, namespace_id: project.namespace, project_id: project, id: wiki_title }
context 'when page content encoding is invalid' do
it 'limits the retrieved pages for the sidebar' do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
it 'limits the retrieved pages for the sidebar' do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
# empty? call
expect(project_wiki).to receive(:pages).with(limit: 1).and_call_original
# Sidebar entries
expect(project_wiki).to receive(:pages).with(limit: 15).and_call_original
# empty? call
expect(project_wiki).to receive(:pages).with(limit: 1).and_call_original
# Sidebar entries
expect(project_wiki).to receive(:pages).with(limit: 15).and_call_original
subject
subject
expect(response).to have_http_status(:ok)
expect(response.body).to include(wiki_title)
end
expect(response).to have_http_status(:ok)
expect(response.body).to include(wiki_title)
end
context 'when page content encoding is invalid' do
......@@ -48,6 +46,42 @@ describe Projects::WikisController do
expect(flash[:notice]).to eq 'The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.'
end
end
context 'when page is a file' do
include WikiHelpers
let(:path) { upload_file_to_wiki(project, user, file_name) }
before do
subject
end
subject { get :show, namespace_id: project.namespace, project_id: project, id: path }
context 'when file is an image' do
let(:file_name) { 'dk.png' }
it 'renders the content inline' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
end
context 'when file is a svg' do
let(:file_name) { 'unsanitized.svg' }
it 'renders the content as an attachment' do
expect(response.headers['Content-Disposition']).to match(/^attachment/)
end
end
end
context 'when file is a pdf' do
let(:file_name) { 'git-cheat-sheet.pdf' }
it 'sets the content type to application/octet-stream' do
expect(response.headers['Content-Type']).to eq 'application/octet-stream'
end
end
end
end
describe 'POST #preview_markdown' do
......
......@@ -95,7 +95,7 @@ describe 'Environments page', :js do
end
it 'does not show environments and counters are set to zero' do
expect(page).to have_content('You don\'t have any environments right now.')
expect(page).to have_content('You don\'t have any environments right now')
expect(page.find('.js-environments-tab-available .badge').text).to eq('0')
expect(page.find('.js-environments-tab-stopped .badge').text).to eq('0')
......
......@@ -31,7 +31,7 @@ describe 'Projects > Files > User deletes files', :js do
fill_in(:commit_message, with: 'New commit message', visible: true)
click_button('Delete file')
expect(current_path).to eq(project_tree_path(project, 'master'))
expect(current_path).to eq(project_tree_path(project, 'master/'))
expect(page).not_to have_content('.gitignore')
end
end
......
......@@ -54,7 +54,7 @@ describe 'User views empty wiki' do
it_behaves_like 'empty wiki and non-accessible issues'
end
context 'when user is logged in and a memeber' do
context 'when user is logged in and a member' do
let(:project) { create(:project, :public, :wiki_repo) }
before do
......
......@@ -2,12 +2,15 @@ require 'spec_helper'
describe 'User views a wiki page' do
shared_examples 'wiki page user view' do
include WikiHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:path) { 'image.png' }
let(:wiki_page) do
create(:wiki_page,
wiki: project.wiki,
attrs: { title: 'home', content: 'Look at this [image](image.jpg)\n\n ![alt text](image.jpg)' })
attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" })
end
before do
......@@ -82,33 +85,26 @@ describe 'User views a wiki page' do
expect(find('.wiki-pages')).to have_content(wiki_page.title.capitalize)
end
it 'shows a file stored in a page' do
raw_file = Gitlab::GitalyClient::WikiFile.new(
mime_type: 'image/jpeg',
name: 'images/image.jpg',
path: 'images/image.jpg',
raw_data: ''
)
wiki_file = Gitlab::Git::WikiFile.new(raw_file)
allow(wiki_file).to receive(:mime_type).and_return('image/jpeg')
allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file)
context 'shows a file stored in a page' do
let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/image.jpg']")
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
it do
expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image')
click_on('image')
expect(current_path).to match('wikis/image.jpg')
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
expect(current_path).to match("wikis/#{path}")
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
end
end
it 'shows the creation page if file does not exist' do
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image')
expect(current_path).to match('wikis/image.jpg')
expect(current_path).to match("wikis/#{path}")
expect(page).to have_content('New Wiki Page')
expect(page).to have_content('Create page')
end
......
......@@ -26,7 +26,7 @@ describe('environments empty state', () => {
it('renders empty state and new environment button', () => {
expect(
vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
).toEqual('You don\'t have any environments right now.');
).toEqual('You don\'t have any environments right now');
expect(
vm.$el.querySelector('.js-new-environment-button').getAttribute('href'),
......@@ -46,7 +46,7 @@ describe('environments empty state', () => {
it('renders empty state without new button', () => {
expect(
vm.$el.querySelector('.js-blank-state-title').textContent.trim(),
).toEqual('You don\'t have any environments right now.');
).toEqual('You don\'t have any environments right now');
expect(
vm.$el.querySelector('.js-new-environment-button'),
......
......@@ -50,7 +50,7 @@ describe('Environment', () => {
expect(
component.$el.querySelector('.js-blank-state-title').textContent,
).toContain('You don\'t have any environments right now.');
).toContain('You don\'t have any environments right now');
});
});
......@@ -136,7 +136,7 @@ describe('Environment', () => {
it('should render empty state', () => {
expect(
component.$el.querySelector('.js-blank-state-title').textContent,
).toContain('You don\'t have any environments right now.');
).toContain('You don\'t have any environments right now');
});
});
......
......@@ -35,7 +35,10 @@ describe('stage column component', () => {
component = mountComponent(StageColumnComponent, {
title: 'foo',
groups: mockGroups,
<<<<<<< HEAD
hasTriggeredBy: false,
=======
>>>>>>> upstream/master
});
});
......
......@@ -570,28 +570,28 @@ describe Project do
end
describe "#readme_url" do
let(:project) { create(:project, :repository, path: "somewhere") }
context 'with a non-existing repository' do
it 'returns nil' do
allow(project.repository).to receive(:tree).with(:head).and_return(nil)
let(:project) { create(:project) }
it 'returns nil' do
expect(project.readme_url).to be_nil
end
end
context 'with an existing repository' do
context 'when no README exists' do
it 'returns nil' do
allow_any_instance_of(Tree).to receive(:readme).and_return(nil)
let(:project) { create(:project, :empty_repo) }
it 'returns nil' do
expect(project.readme_url).to be_nil
end
end
context 'when a README exists' do
let(:project) { create(:project, :repository) }
it 'returns the README' do
expect(project.readme_url).to eql("#{Gitlab.config.gitlab.url}/#{project.namespace.full_path}/somewhere/blob/master/README.md")
expect(project.readme_url).to eq("#{project.web_url}/blob/master/README.md")
end
end
end
......
......@@ -461,6 +461,24 @@ describe Repository do
it { is_expected.to be_nil }
end
context 'regular blob' do
subject { repository.blob_at(repository.head_commit.sha, '.gitignore') }
it { is_expected.to be_an_instance_of(::Blob) }
end
context 'readme blob on HEAD' do
subject { repository.blob_at(repository.head_commit.sha, 'README.md') }
it { is_expected.to be_an_instance_of(::ReadmeBlob) }
end
context 'readme blob not on HEAD' do
subject { repository.blob_at(repository.find_branch('feature').target, 'README.md') }
it { is_expected.to be_an_instance_of(::Blob) }
end
end
describe '#merged_to_root_ref?' do
......@@ -1922,23 +1940,25 @@ describe Repository do
describe '#readme', :use_clean_rails_memory_store_caching do
context 'with a non-existing repository' do
it 'returns nil' do
allow(repository).to receive(:tree).with(:head).and_return(nil)
let(:project) { create(:project) }
it 'returns nil' do
expect(repository.readme).to be_nil
end
end
context 'with an existing repository' do
context 'when no README exists' do
it 'returns nil' do
allow_any_instance_of(Tree).to receive(:readme).and_return(nil)
let(:project) { create(:project, :empty_repo) }
it 'returns nil' do
expect(repository.readme).to be_nil
end
end
context 'when a README exists' do
let(:project) { create(:project, :repository) }
it 'returns the README' do
expect(repository.readme).to be_an_instance_of(ReadmeBlob)
end
......
......@@ -457,6 +457,12 @@ describe WikiPage do
end
describe '#historical?' do
let(:page) { wiki.find_page('Update') }
let(:old_version) { page.versions.last.id }
let(:old_page) { wiki.find_page('Update', old_version) }
let(:latest_version) { page.versions.first.id }
let(:latest_page) { wiki.find_page('Update', latest_version) }
before do
create_page('Update', 'content')
@page = wiki.find_page('Update')
......@@ -468,23 +474,27 @@ describe WikiPage do
end
it 'returns true when requesting an old version' do
old_version = @page.versions.last.id
old_page = wiki.find_page('Update', old_version)
expect(old_page.historical?).to eq true
expect(old_page.historical?).to be_truthy
end
it 'returns false when requesting latest version' do
latest_version = @page.versions.first.id
latest_page = wiki.find_page('Update', latest_version)
expect(latest_page.historical?).to eq false
expect(latest_page.historical?).to be_falsy
end
it 'returns false when version is nil' do
latest_page = wiki.find_page('Update', nil)
expect(latest_page.historical?).to be_falsy
end
it 'returns false when the last version is nil' do
expect(old_page).to receive(:last_version) { nil }
expect(old_page.historical?).to be_falsy
end
it 'returns false when the version is nil' do
expect(old_page).to receive(:version) { nil }
expect(latest_page.historical?).to eq false
expect(old_page.historical?).to be_falsy
end
end
......
module WikiHelpers
extend self
def upload_file_to_wiki(project, user, file_name)
opts = {
file_name: file_name,
file_content: File.read(expand_fixture_path(file_name))
}
::Wikis::CreateAttachmentService.new(project, user, opts)
.execute[:result][:file_path]
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment