Commit 667917ab authored by Gabriel Mazetto's avatar Gabriel Mazetto

Merge branch '9-5-stable' into security-9-5

parents f024f7ef c47ae372
...@@ -2,6 +2,14 @@ ...@@ -2,6 +2,14 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 9.5.1 (2017-08-23)
- [FIXED] Fix merge request pipeline status when pipeline has errors. !13664
- [FIXED] Commit rows would occasionally render with the wrong language.
- [FIXED] Fix caching of future broadcast messages.
- [FIXED] Only require Sidekiq throttling library when enabled, to reduce cache misses.
- Raise Housekeeping timeout to 24 hours. !13719
## 9.5.0 (2017-08-22) ## 9.5.0 (2017-08-22)
- [FIXED] Fix timeouts when creating projects in groups with many members. !13508 - [FIXED] Fix timeouts when creating projects in groups with many members. !13508
......
...@@ -154,7 +154,7 @@ gem 'acts-as-taggable-on', '~> 4.0' ...@@ -154,7 +154,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
gem 'sidekiq', '~> 5.0' gem 'sidekiq', '~> 5.0'
gem 'sidekiq-cron', '~> 0.6.0' gem 'sidekiq-cron', '~> 0.6.0'
gem 'redis-namespace', '~> 1.5.2' gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4' gem 'sidekiq-limit_fetch', '~> 3.4', require: false
# Cron Parser # Cron Parser
gem 'rufus-scheduler', '~> 3.4' gem 'rufus-scheduler', '~> 3.4'
......
...@@ -97,7 +97,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({ ...@@ -97,7 +97,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
return `Avatar for ${assignee.name}`; return `Avatar for ${assignee.name}`;
}, },
showLabel(label) { showLabel(label) {
if (!this.list || !label) return true; if (!label.id) return false;
return true; return true;
}, },
filterByLabel(label, e) { filterByLabel(label, e) {
......
...@@ -74,7 +74,8 @@ export default { ...@@ -74,7 +74,8 @@ export default {
<tbody> <tbody>
<repo-file-options <repo-file-options
:is-mini="isMini" :is-mini="isMini"
:project-name="projectName"/> :project-name="projectName"
/>
<repo-previous-directory <repo-previous-directory
v-if="isRoot" v-if="isRoot"
:prev-url="prevURL" :prev-url="prevURL"
...@@ -84,7 +85,8 @@ export default { ...@@ -84,7 +85,8 @@ export default {
:key="n" :key="n"
:loading="loading" :loading="loading"
:has-files="!!files.length" :has-files="!!files.length"
:is-mini="isMini"/> :is-mini="isMini"
/>
<repo-file <repo-file
v-for="file in files" v-for="file in files"
:key="file.id" :key="file.id"
...@@ -93,7 +95,8 @@ export default { ...@@ -93,7 +95,8 @@ export default {
@linkclicked="fileClicked(file)" @linkclicked="fileClicked(file)"
:is-tree="isTree" :is-tree="isTree"
:has-files="!!files.length" :has-files="!!files.length"
:active-file="activeFile"/> :active-file="activeFile"
/>
</tbody> </tbody>
</table> </table>
</div> </div>
......
...@@ -176,7 +176,7 @@ module EventsHelper ...@@ -176,7 +176,7 @@ module EventsHelper
sanitize( sanitize(
text, text,
tags: %w(a img gl-emoji b pre code p span), tags: %w(a img gl-emoji b pre code p span),
attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-name', 'data-unicode-version'] attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-src', 'data-name', 'data-unicode-version']
) )
end end
......
...@@ -19,11 +19,21 @@ class BroadcastMessage < ActiveRecord::Base ...@@ -19,11 +19,21 @@ class BroadcastMessage < ActiveRecord::Base
after_commit :flush_redis_cache after_commit :flush_redis_cache
def self.current def self.current
Rails.cache.fetch(CACHE_KEY) do messages = Rails.cache.fetch(CACHE_KEY) { current_and_future_messages.to_a }
where('ends_at > :now AND starts_at <= :now', now: Time.zone.now)
.reorder(id: :asc) return messages if messages.empty?
.to_a
end now_or_future = messages.select(&:now_or_future?)
# If there are cached entries but none are to be displayed we'll purge the
# cache so we don't keep running this code all the time.
Rails.cache.delete(CACHE_KEY) if now_or_future.empty?
now_or_future.select(&:now?)
end
def self.current_and_future_messages
where('ends_at > :now', now: Time.zone.now).reorder(id: :asc)
end end
def active? def active?
...@@ -38,6 +48,18 @@ class BroadcastMessage < ActiveRecord::Base ...@@ -38,6 +48,18 @@ class BroadcastMessage < ActiveRecord::Base
ends_at < Time.zone.now ends_at < Time.zone.now
end end
def now?
(starts_at..ends_at).cover?(Time.zone.now)
end
def future?
starts_at > Time.zone.now
end
def now_or_future?
now? || future?
end
def flush_redis_cache def flush_redis_cache
Rails.cache.delete(CACHE_KEY) Rails.cache.delete(CACHE_KEY)
end end
......
...@@ -763,7 +763,7 @@ class Repository ...@@ -763,7 +763,7 @@ class Repository
index = Gitlab::Git::Index.new(raw_repository) index = Gitlab::Git::Index.new(raw_repository)
if start_commit if start_commit
index.read_tree(start_commit.raw_commit.tree) index.read_tree(start_commit.rugged_commit.tree)
parents = [start_commit.sha] parents = [start_commit.sha]
else else
parents = [] parents = []
......
...@@ -176,9 +176,14 @@ module Ci ...@@ -176,9 +176,14 @@ module Ci
end end
def error(message, save: false) def error(message, save: false)
pipeline.errors.add(:base, message) pipeline.tap do
pipeline.drop if save pipeline.errors.add(:base, message)
pipeline
if save
pipeline.drop
update_merge_requests_head_pipeline
end
end
end end
def pipeline_created_counter def pipeline_created_counter
......
...@@ -9,7 +9,8 @@ module Projects ...@@ -9,7 +9,8 @@ module Projects
class HousekeepingService < BaseService class HousekeepingService < BaseService
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
LEASE_TIMEOUT = 3600 # Timeout set to 24h
LEASE_TIMEOUT = 86400
class LeaseTaken < StandardError class LeaseTaken < StandardError
def to_s def to_s
......
...@@ -356,7 +356,9 @@ ...@@ -356,7 +356,9 @@
%fieldset %fieldset
%legend Background Jobs %legend Background Jobs
%p %p
These settings require a restart to take effect. These settings require a
= link_to 'restart', help_page_path('administration/restart_gitlab')
to take effect.
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
= hidden_field_tag :namespace_id, value: current_user.namespace_id = hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-xs-12.col-sm-6.project-path .form-group.col-xs-12.col-sm-6.project-path
= label_tag :path, 'Project name', class: 'label-light' = label_tag :path, 'Project name', class: 'label-light'
= text_field_tag :path, nil, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true
.row .row
.form-group.col-md-12 .form-group.col-md-12
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
.row .row
.form-group.col-sm-12 .form-group.col-sm-12
= hidden_field_tag :namespace_id, @namespace.id = hidden_field_tag :namespace_id, @namespace.id
= hidden_field_tag :path, @path
= label_tag :file, 'GitLab project export', class: 'label-light' = label_tag :file, 'GitLab project export', class: 'label-light'
.form-group .form-group
= file_field_tag :file, class: '' = file_field_tag :file, class: ''
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- notes = commit.notes - notes = commit.notes
- note_count = notes.user.count - note_count = notes.user.count
- cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits)] - cache_key = [project.full_path, commit.id, current_application_settings, note_count, @path.presence, current_controller?(:commits), I18n.locale]
- cache_key.push(commit.status(ref)) if commit.status(ref) - cache_key.push(commit.status(ref)) if commit.status(ref)
= cache(cache_key, expires_in: 1.day) do = cache(cache_key, expires_in: 1.day) do
......
...@@ -237,8 +237,6 @@ ...@@ -237,8 +237,6 @@
.sub-section.rename-respository .sub-section.rename-respository
%h4.warning-title %h4.warning-title
Rename repository Rename repository
%p
Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page.
= render 'projects/errors' = render 'projects/errors'
= form_for([@project.namespace.becomes(Namespace), @project]) do |f| = form_for([@project.namespace.becomes(Namespace), @project]) do |f|
.form-group.project_name_holder .form-group.project_name_holder
......
...@@ -649,6 +649,9 @@ test: ...@@ -649,6 +649,9 @@ test:
default: default:
path: tmp/tests/repositories/ path: tmp/tests/repositories/
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
failure_count_threshold: 999999
failure_wait_time: 0
storage_timeout: 30
broken: broken:
path: tmp/tests/non-existent-repositories path: tmp/tests/non-existent-repositories
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
......
...@@ -37,12 +37,12 @@ def validate_storages_config ...@@ -37,12 +37,12 @@ def validate_storages_config
storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example") storage_validation_error("#{name} is not a valid storage, because it has no `path` key. Refer to gitlab.yml.example for an updated example")
end end
%w(failure_count_threshold failure_wait_time failure_reset_time storage_timeout).each do |setting| %w(failure_count_threshold failure_reset_time storage_timeout).each do |setting|
# Falling back to the defaults is fine! # Falling back to the defaults is fine!
next if repository_storage[setting].nil? next if repository_storage[setting].nil?
unless repository_storage[setting].to_f > 0 unless repository_storage[setting].to_f > 0
storage_validation_error("#{setting}, for storage `#{name}` needs to be greater than 0") storage_validation_error("`#{setting}` for storage `#{name}` needs to be greater than 0")
end end
end end
end end
......
...@@ -10,10 +10,8 @@ end ...@@ -10,10 +10,8 @@ end
# #
module Gitlab module Gitlab
module StrongParameterScalars module StrongParameterScalars
GITLAB_PERMITTED_SCALAR_TYPES = [::UploadedFile].freeze
def permitted_scalar?(value) def permitted_scalar?(value)
super || GITLAB_PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) } super || value.is_a?(::UploadedFile)
end end
end end
end end
......
...@@ -76,7 +76,6 @@ var config = { ...@@ -76,7 +76,6 @@ var config = {
terminal: './terminal/terminal_bundle.js', terminal: './terminal/terminal_bundle.js',
u2f: ['vendor/u2f'], u2f: ['vendor/u2f'],
ui_development_kit: './ui_development_kit.js', ui_development_kit: './ui_development_kit.js',
users: './users/index.js',
raven: './raven/index.js', raven: './raven/index.js',
vue_merge_request_widget: './vue_merge_request_widget/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js',
test: './test.js', test: './test.js',
......
...@@ -55,6 +55,12 @@ By doing so: ...@@ -55,6 +55,12 @@ By doing so:
- John mentions everyone from his team with `@john-team` - John mentions everyone from his team with `@john-team`
- John mentions only his marketing team with `@john-team/marketing` - John mentions only his marketing team with `@john-team/marketing`
## Issues and merge requests within a group
Issues and merge requests are part of projects. For a given group, view all the
[issues](../project/issues/index.md#issues-per-group) and [merge requests](../project/merge_requests/index.md#merge-requests-per-group) across all the projects in that group,
together in a single list view.
## Create a new group ## Create a new group
You can create a group in GitLab from: You can create a group in GitLab from:
......
...@@ -162,6 +162,10 @@ are a tool for working faster and more effectively with your team, ...@@ -162,6 +162,10 @@ are a tool for working faster and more effectively with your team,
by listing all user or group mentions, as well as issues and merge by listing all user or group mentions, as well as issues and merge
requests you're assigned to. requests you're assigned to.
## Search
[Search and filter](search/index.md) through groups, projects, issues, merge requests, files, code, and more.
## Snippets ## Snippets
[Snippets](snippets.md) are code blocks that you want to store in GitLab, from which [Snippets](snippets.md) are code blocks that you want to store in GitLab, from which
......
...@@ -7,7 +7,7 @@ of solving a problem. ...@@ -7,7 +7,7 @@ of solving a problem.
It allows you, your team, and your collaborators to share It allows you, your team, and your collaborators to share
and discuss proposals before and while implementing them. and discuss proposals before and while implementing them.
Issues and the GitLab Issue Tracker are available in all GitLab Issues and the GitLab Issue Tracker are available in all
[GitLab Products](https://about.gitlab.com/products/) as [GitLab Products](https://about.gitlab.com/products/) as
part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/). part of the [GitLab Workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
...@@ -48,11 +48,27 @@ for feature proposals and another one for bug reports. ...@@ -48,11 +48,27 @@ for feature proposals and another one for bug reports.
## Issue Tracker ## Issue Tracker
The issue tracker is the collection of opened and closed issues created in a project. The Issue Tracker is the collection of opened and closed issues created in a project.
It is available for all projects, from the moment the project is created.
![Issue tracker](img/issue_tracker.png) Find the issue tracker by navigating to your **Project's homepage** > **Issues**.
Find the issue tracker by navigating to your **Project's Dashboard** > **Issues**. ### Issues per project
When you access your project's issues, GitLab will present them in a list,
and you can use the tabs available to quickly filter by open and closed issues.
![Project issues list view](img/project_issues_list_view.png)
You can also [search and filter](../../search/index.md#issues-and-merge-requests-per-project) the results more deeply with GitLab's search capacities.
### Issues per group
View all the issues in a group (that is, all the issues across all projects in that
group) by navigating to **Group > Issues**. This view also has the open and closed
issue tabs.
![Group Issues list view](img/group_issues_list_view.png)
## GitLab Issues Functionalities ## GitLab Issues Functionalities
...@@ -120,6 +136,12 @@ to find out more about this feature. ...@@ -120,6 +136,12 @@ to find out more about this feature.
With [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/), you can also With [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/), you can also
create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards). create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
### External Issue Tracker
Alternatively to GitLab's built-in Issue Tracker, you can also use an [external
tracker](../../../integration/external-issue-tracker.md) such as Jira, Redmine,
or Bugzilla.
### Issue's API ### Issue's API
Read through the [API documentation](../../../api/issues.md). Read through the [API documentation](../../../api/issues.md).
...@@ -56,6 +56,23 @@ B. Consider you're a web developer writing a webpage for your company's: ...@@ -56,6 +56,23 @@ B. Consider you're a web developer writing a webpage for your company's:
1. Once approved, your merge request is [squashed and merged](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) (Squash and Merge is available in GitLab Enterprise Edition Starter) 1. Once approved, your merge request is [squashed and merged](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) (Squash and Merge is available in GitLab Enterprise Edition Starter)
1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production 1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production
## Merge requests per project
View all the merge requests within a project by navigating to **Project > Merge Requests**.
When you access your project's merge requests, GitLab will present them in a list,
and you can use the tabs available to quickly filter by open and closed. You can also [search and filter the results](../../search/index.md#issues-and-merge-requests-per-project).
![Project merge requests list view](img/project_merge_requests_list_view.png)
## Merge requests per group
View all the merge requests in a group (that is, all the merge requests across all projects in that
group) by navigating to **Group > Merge Requests**. This view also has the open, merged, and closed
merge request tabs, from which you can [search and filter the results](../../search/index.md#issues-and-merge-requests-per-group).
![Group Issues list view](img/group_merge_requests_list_view.png)
## Authorization for merge requests ## Authorization for merge requests
There are two main ways to have a merge request flow with GitLab: There are two main ways to have a merge request flow with GitLab:
...@@ -141,7 +158,6 @@ all your changes will be available to preview by anyone with the Review Apps lin ...@@ -141,7 +158,6 @@ all your changes will be available to preview by anyone with the Review Apps lin
[Read more about Review Apps.](../../../ci/review_apps/index.md) [Read more about Review Apps.](../../../ci/review_apps/index.md)
## Tips ## Tips
Here are some tips that will help you be more efficient with merge requests in Here are some tips that will help you be more efficient with merge requests in
...@@ -230,4 +246,4 @@ git checkout origin/merge-requests/1 ...@@ -230,4 +246,4 @@ git checkout origin/merge-requests/1
``` ```
[protected branches]: ../protected_branches.md [protected branches]: ../protected_branches.md
[ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition" [ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition"
\ No newline at end of file
...@@ -27,7 +27,7 @@ on the search field on the top-right of your screen: ...@@ -27,7 +27,7 @@ on the search field on the top-right of your screen:
![shortcut to your issues and mrs](img/issues_mrs_shortcut.png) ![shortcut to your issues and mrs](img/issues_mrs_shortcut.png)
## Issues and merge requests per project ### Issues and merge requests per project
If you want to search for issues present in a specific project, navigate to If you want to search for issues present in a specific project, navigate to
a project's **Issues** tab, and click on the field **Search or filter results...**. It will a project's **Issues** tab, and click on the field **Search or filter results...**. It will
...@@ -40,7 +40,7 @@ The same process is valid for merge requests. Navigate to your project's **Merge ...@@ -40,7 +40,7 @@ The same process is valid for merge requests. Navigate to your project's **Merge
and click **Search or filter results...**. Merge requests can be filtered by author, assignee, and click **Search or filter results...**. Merge requests can be filtered by author, assignee,
milestone, and label. milestone, and label.
## Issues and merge requests per group ### Issues and merge requests per group
Similar to **Issues and merge requests per project**, you can also search for issues Similar to **Issues and merge requests per project**, you can also search for issues
within a group. Navigate to a group's **Issues** tab and query search results in within a group. Navigate to a group's **Issues** tab and query search results in
...@@ -48,6 +48,10 @@ the same way as you do for projects. ...@@ -48,6 +48,10 @@ the same way as you do for projects.
![filter issues in a group](img/group_issues_filter.png) ![filter issues in a group](img/group_issues_filter.png)
The same process is valid for merge requests. Navigate to your project's **Merge Requests** tab.
The search and filter UI currently uses dropdowns. In a future release, the same
dynamic UI as above will be carried over here.
## Search history ## Search history
You can view recent searches by clicking on the little arrow-clock icon, which is to the left of the search input. Click the search entry to run that search again. This feature is available for issues and merge requests. Searches are stored locally in your browser. You can view recent searches by clicking on the little arrow-clock icon, which is to the left of the search input. Click the search entry to run that search again. This feature is available for issues and merge requests. Searches are stored locally in your browser.
......
...@@ -77,8 +77,8 @@ EOM ...@@ -77,8 +77,8 @@ EOM
def initialize(merge_request, project) def initialize(merge_request, project)
@merge_request = merge_request @merge_request = merge_request
@our_commit = merge_request.source_branch_head.raw.raw_commit @our_commit = merge_request.source_branch_head.raw.rugged_commit
@their_commit = merge_request.target_branch_head.raw.raw_commit @their_commit = merge_request.target_branch_head.raw.rugged_commit
@project = project @project = project
end end
end end
......
...@@ -14,7 +14,7 @@ module Gitlab ...@@ -14,7 +14,7 @@ module Gitlab
attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator attr_accessor *SERIALIZE_KEYS # rubocop:disable Lint/AmbiguousOperator
delegate :tree, to: :raw_commit delegate :tree, to: :rugged_commit
def ==(other) def ==(other)
return false unless other.is_a?(Gitlab::Git::Commit) return false unless other.is_a?(Gitlab::Git::Commit)
...@@ -287,7 +287,7 @@ module Gitlab ...@@ -287,7 +287,7 @@ module Gitlab
# empty repo. See Repository#diff for keys allowed in the +options+ # empty repo. See Repository#diff for keys allowed in the +options+
# hash. # hash.
def diff_from_parent(options = {}) def diff_from_parent(options = {})
Commit.diff_from_parent(raw_commit, options) Commit.diff_from_parent(rugged_commit, options)
end end
def deltas def deltas
...@@ -335,7 +335,7 @@ module Gitlab ...@@ -335,7 +335,7 @@ module Gitlab
def to_patch(options = {}) def to_patch(options = {})
begin begin
raw_commit.to_mbox(options) rugged_commit.to_mbox(options)
rescue Rugged::InvalidError => ex rescue Rugged::InvalidError => ex
if ex.message =~ /commit \w+ is a merge commit/i if ex.message =~ /commit \w+ is a merge commit/i
'Patch format is not currently supported for merge commits.' 'Patch format is not currently supported for merge commits.'
...@@ -383,6 +383,14 @@ module Gitlab ...@@ -383,6 +383,14 @@ module Gitlab
encode! @committer_email encode! @committer_email
end end
def rugged_commit
@rugged_commit ||= if raw_commit.is_a?(Rugged::Commit)
raw_commit
else
@repository.rev_parse_target(id)
end
end
private private
def init_from_hash(hash) def init_from_hash(hash)
......
...@@ -4,6 +4,7 @@ module Gitlab ...@@ -4,6 +4,7 @@ module Gitlab
class GitAccess class GitAccess
UnauthorizedError = Class.new(StandardError) UnauthorizedError = Class.new(StandardError)
NotFoundError = Class.new(StandardError) NotFoundError = Class.new(StandardError)
ProjectMovedError = Class.new(NotFoundError)
ERROR_MESSAGES = { ERROR_MESSAGES = {
upload: 'You are not allowed to upload code for this project.', upload: 'You are not allowed to upload code for this project.',
...@@ -90,18 +91,18 @@ module Gitlab ...@@ -90,18 +91,18 @@ module Gitlab
end end
def check_project_moved! def check_project_moved!
if redirected_path return unless redirected_path
url = protocol == 'ssh' ? project.ssh_url_to_repo : project.http_url_to_repo
message = <<-MESSAGE.strip_heredoc
Project '#{redirected_path}' was moved to '#{project.full_path}'.
Please update your Git remote and try again: url = protocol == 'ssh' ? project.ssh_url_to_repo : project.http_url_to_repo
message = <<-MESSAGE.strip_heredoc
Project '#{redirected_path}' was moved to '#{project.full_path}'.
git remote set-url origin #{url} Please update your Git remote and try again:
MESSAGE
raise NotFoundError, message git remote set-url origin #{url}
end MESSAGE
raise ProjectMovedError, message
end end
def check_command_disabled!(cmd) def check_command_disabled!(cmd)
......
...@@ -100,5 +100,9 @@ module Gitlab ...@@ -100,5 +100,9 @@ module Gitlab
path = Rails.root.join(SERVER_VERSION_FILE) path = Rails.root.join(SERVER_VERSION_FILE)
path.read.chomp path.read.chomp
end end
def self.encode(s)
s.dup.force_encoding(Encoding::ASCII_8BIT)
end
end end
end end
...@@ -63,8 +63,8 @@ module Gitlab ...@@ -63,8 +63,8 @@ module Gitlab
def tree_entries(repository, revision, path) def tree_entries(repository, revision, path)
request = Gitaly::GetTreeEntriesRequest.new( request = Gitaly::GetTreeEntriesRequest.new(
repository: @gitaly_repo, repository: @gitaly_repo,
revision: revision, revision: GitalyClient.encode(revision),
path: path.presence || '.' path: path.present? ? GitalyClient.encode(path) : '.'
) )
response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request) response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request)
......
...@@ -3,6 +3,8 @@ module Gitlab ...@@ -3,6 +3,8 @@ module Gitlab
class << self class << self
def execute! def execute!
if Gitlab::CurrentSettings.sidekiq_throttling_enabled? if Gitlab::CurrentSettings.sidekiq_throttling_enabled?
require 'sidekiq-limit_fetch'
Gitlab::CurrentSettings.current_application_settings.sidekiq_throttling_queues.each do |queue| Gitlab::CurrentSettings.current_application_settings.sidekiq_throttling_queues.each do |queue|
Sidekiq::Queue[queue].limit = queue_limit Sidekiq::Queue[queue].limit = queue_limit
end end
......
...@@ -3,11 +3,13 @@ require 'spec_helper' ...@@ -3,11 +3,13 @@ require 'spec_helper'
feature 'Import/Export - project import integration test', js: true do feature 'Import/Export - project import integration test', js: true do
include Select2Helper include Select2Helper
let(:user) { create(:user) }
let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') } let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
background do background do
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
gitlab_sign_in(user)
end end
after(:each) do after(:each) do
...@@ -18,57 +20,67 @@ feature 'Import/Export - project import integration test', js: true do ...@@ -18,57 +20,67 @@ feature 'Import/Export - project import integration test', js: true do
let(:user) { create(:admin) } let(:user) { create(:admin) }
let!(:namespace) { create(:namespace, name: "asd", owner: user) } let!(:namespace) { create(:namespace, name: "asd", owner: user) }
before do context 'prefilled the path' do
gitlab_sign_in(user) scenario 'user imports an exported project successfully' do
end visit new_project_path
scenario 'user imports an exported project successfully' do select2(namespace.id, from: '#project_namespace_id')
visit new_project_path fill_in :project_path, with: 'test-project-path', visible: true
click_link 'GitLab export'
select2(namespace.id, from: '#project_namespace_id') expect(page).to have_content('Import an exported GitLab project')
fill_in :project_path, with: 'test-project-path', visible: true expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=test-project-path")
click_link 'GitLab export' expect(Gitlab::ImportExport).to receive(:import_upload_path).with(filename: /\A\h{32}_test-project-path\z/).and_call_original
expect(page).to have_content('Import an exported GitLab project') attach_file('file', file)
expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=test-project-path")
expect(Gitlab::ImportExport).to receive(:import_upload_path).with(filename: /\A\h{32}_test-project-path\z/).and_call_original
attach_file('file', file) expect { click_on 'Import project' }.to change { Project.count }.by(1)
expect { click_on 'Import project' }.to change { Project.count }.from(0).to(1) project = Project.last
expect(project).not_to be_nil
project = Project.last expect(project.issues).not_to be_empty
expect(project).not_to be_nil expect(project.merge_requests).not_to be_empty
expect(project.issues).not_to be_empty expect(project_hook_exists?(project)).to be true
expect(project.merge_requests).not_to be_empty expect(wiki_exists?(project)).to be true
expect(project_hook_exists?(project)).to be true expect(project.import_status).to eq('finished')
expect(wiki_exists?(project)).to be true end
expect(project.import_status).to eq('finished')
end end
scenario 'invalid project' do context 'path is not prefilled' do
project = create(:project, namespace: namespace) scenario 'user imports an exported project successfully' do
visit new_project_path
click_link 'GitLab export'
visit new_project_path fill_in :path, with: 'test-project-path', visible: true
attach_file('file', file)
select2(namespace.id, from: '#project_namespace_id') expect { click_on 'Import project' }.to change { Project.count }.by(1)
fill_in :project_path, with: project.name, visible: true
click_link 'GitLab export'
attach_file('file', file)
click_on 'Import project'
page.within('.flash-container') do project = Project.last
expect(page).to have_content('Project could not be imported') expect(project).not_to be_nil
expect(page).to have_content("Project 'test-project-path' is being imported")
end end
end end
end end
context 'when limited to the default user namespace' do scenario 'invalid project' do
let(:user) { create(:user) } namespace = create(:namespace, name: "asd", owner: user)
before do project = create(:project, namespace: namespace)
gitlab_sign_in(user)
visit new_project_path
select2(namespace.id, from: '#project_namespace_id')
fill_in :project_path, with: project.name, visible: true
click_link 'GitLab export'
attach_file('file', file)
click_on 'Import project'
page.within('.flash-container') do
expect(page).to have_content('Project could not be imported')
end end
end
context 'when limited to the default user namespace' do
scenario 'passes correct namespace ID in the URL' do scenario 'passes correct namespace ID in the URL' do
visit new_project_path visit new_project_path
......
...@@ -62,6 +62,12 @@ describe EventsHelper do ...@@ -62,6 +62,12 @@ describe EventsHelper do
expect(helper.event_note(input)).to eq(expected) expect(helper.event_note(input)).to eq(expected)
end end
it 'preserves data-src for lazy images' do
input = "![ImageTest](/uploads/test.png)"
image_url = "data-src=\"/uploads/test.png\""
expect(helper.event_note(input)).to match(image_url)
end
context 'labels formatting' do context 'labels formatting' do
let(:input) { 'this should be ~label_1' } let(:input) { 'this should be ~label_1' }
......
...@@ -278,6 +278,25 @@ describe('Issue card component', () => { ...@@ -278,6 +278,25 @@ describe('Issue card component', () => {
nodes.includes(label1.color), nodes.includes(label1.color),
).toBe(true); ).toBe(true);
}); });
it('does not render label if label does not have an ID', (done) => {
component.issue.addLabel(new ListLabel({
title: 'closed',
}));
Vue.nextTick()
.then(() => {
expect(
component.$el.querySelectorAll('.label').length,
).toBe(2);
expect(
component.$el.textContent,
).not.toContain('closed');
done();
})
.catch(done.fail);
});
}); });
}); });
}); });
require 'spec_helper' require 'spec_helper'
describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: true, broken_storage: true do describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: true, broken_storage: true do
let(:circuit_breaker) { described_class.new('default') } let(:storage_name) { 'default' }
let(:circuit_breaker) { described_class.new(storage_name) }
let(:hostname) { Gitlab::Environment.hostname } let(:hostname) { Gitlab::Environment.hostname }
let(:cache_key) { "storage_accessible:default:#{hostname}" } let(:cache_key) { "storage_accessible:#{storage_name}:#{hostname}" }
before do
# Override test-settings for the circuitbreaker with something more realistic
# for these specs.
stub_storage_settings('default' => {
'path' => TestEnv.repos_path,
'failure_count_threshold' => 10,
'failure_wait_time' => 30,
'failure_reset_time' => 1800,
'storage_timeout' => 5
},
'broken' => {
'path' => 'tmp/tests/non-existent-repositories',
'failure_count_threshold' => 10,
'failure_wait_time' => 30,
'failure_reset_time' => 1800,
'storage_timeout' => 5
}
)
end
def value_from_redis(name) def value_from_redis(name)
Gitlab::Git::Storage.redis.with do |redis| Gitlab::Git::Storage.redis.with do |redis|
...@@ -96,14 +117,14 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: ...@@ -96,14 +117,14 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end end
describe '#circuit_broken?' do describe '#circuit_broken?' do
it 'is closed when there is no last failure' do it 'is working when there is no last failure' do
set_in_redis(:last_failure, nil) set_in_redis(:last_failure, nil)
set_in_redis(:failure_count, 0) set_in_redis(:failure_count, 0)
expect(circuit_breaker.circuit_broken?).to be_falsey expect(circuit_breaker.circuit_broken?).to be_falsey
end end
it 'is open when there was a recent failure' do it 'is broken when there was a recent failure' do
Timecop.freeze do Timecop.freeze do
set_in_redis(:last_failure, 1.second.ago.to_f) set_in_redis(:last_failure, 1.second.ago.to_f)
set_in_redis(:failure_count, 1) set_in_redis(:failure_count, 1)
...@@ -112,16 +133,34 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: ...@@ -112,16 +133,34 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end end
end end
it 'is open when there are to many failures' do it 'is broken when there are too many failures' do
set_in_redis(:last_failure, 1.day.ago.to_f) set_in_redis(:last_failure, 1.day.ago.to_f)
set_in_redis(:failure_count, 200) set_in_redis(:failure_count, 200)
expect(circuit_breaker.circuit_broken?).to be_truthy expect(circuit_breaker.circuit_broken?).to be_truthy
end end
context 'the `failure_wait_time` is set to 0' do
before do
stub_storage_settings('default' => {
'failure_wait_time' => 0,
'path' => TestEnv.repos_path
})
end
it 'is working even when there is a recent failure' do
Timecop.freeze do
set_in_redis(:last_failure, 0.seconds.ago.to_f)
set_in_redis(:failure_count, 1)
expect(circuit_breaker.circuit_broken?).to be_falsey
end
end
end
end end
describe "storage_available?" do describe "storage_available?" do
context 'when the storage is available' do context 'the storage is available' do
it 'tracks that the storage was accessible an raises the error' do it 'tracks that the storage was accessible an raises the error' do
expect(circuit_breaker).to receive(:track_storage_accessible) expect(circuit_breaker).to receive(:track_storage_accessible)
...@@ -136,8 +175,8 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: ...@@ -136,8 +175,8 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end end
end end
context 'when storage is not available' do context 'storage is not available' do
let(:circuit_breaker) { described_class.new('broken') } let(:storage_name) { 'broken' }
it 'tracks that the storage was inaccessible' do it 'tracks that the storage was inaccessible' do
expect(circuit_breaker).to receive(:track_storage_inaccessible) expect(circuit_breaker).to receive(:track_storage_inaccessible)
...@@ -158,8 +197,8 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state: ...@@ -158,8 +197,8 @@ describe Gitlab::Git::Storage::CircuitBreaker, clean_gitlab_redis_shared_state:
end end
end end
context 'when the storage is not available' do context 'the storage is not available' do
let(:circuit_breaker) { described_class.new('broken') } let(:storage_name) { 'broken' }
it 'raises an error' do it 'raises an error' do
expect(circuit_breaker).to receive(:track_storage_inaccessible) expect(circuit_breaker).to receive(:track_storage_inaccessible)
......
This diff is collapsed.
...@@ -119,5 +119,19 @@ describe Gitlab::GitalyClient::CommitService do ...@@ -119,5 +119,19 @@ describe Gitlab::GitalyClient::CommitService do
client.tree_entries(repository, revision, path) client.tree_entries(repository, revision, path)
end end
context 'with UTF-8 params strings' do
let(:revision) { "branch\u011F" }
let(:path) { "foo/\u011F.txt" }
it 'handles string encodings correctly' do
expect_any_instance_of(Gitaly::CommitService::Stub)
.to receive(:get_tree_entries)
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([])
client.tree_entries(repository, revision, path)
end
end
end end
end end
require 'spec_helper' require 'spec_helper'
describe Gitlab::SidekiqThrottler do describe Gitlab::SidekiqThrottler do
before do
Sidekiq.options[:concurrency] = 35
stub_application_setting(
sidekiq_throttling_enabled: true,
sidekiq_throttling_factor: 0.1,
sidekiq_throttling_queues: %w[build project_cache]
)
end
describe '#execute!' do describe '#execute!' do
it 'sets limits on the selected queues' do context 'when job throttling is enabled' do
described_class.execute! before do
Sidekiq.options[:concurrency] = 35
stub_application_setting(
sidekiq_throttling_enabled: true,
sidekiq_throttling_factor: 0.1,
sidekiq_throttling_queues: %w[build project_cache]
)
end
it 'requires sidekiq-limit_fetch' do
expect(described_class).to receive(:require).with('sidekiq-limit_fetch').and_call_original
described_class.execute!
end
it 'sets limits on the selected queues' do
described_class.execute!
expect(Sidekiq::Queue['build'].limit).to eq 4
expect(Sidekiq::Queue['project_cache'].limit).to eq 4
end
it 'does not set limits on other queues' do
described_class.execute!
expect(Sidekiq::Queue['build'].limit).to eq 4 expect(Sidekiq::Queue['merge'].limit).to be_nil
expect(Sidekiq::Queue['project_cache'].limit).to eq 4 end
end end
it 'does not set limits on other queues' do context 'when job throttling is disabled' do
described_class.execute! it 'does not require sidekiq-limit_fetch' do
expect(described_class).not_to receive(:require).with('sidekiq-limit_fetch')
expect(Sidekiq::Queue['merge'].limit).to be_nil described_class.execute!
end
end end
end end
end end
...@@ -6,7 +6,7 @@ require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_ ...@@ -6,7 +6,7 @@ require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_
describe MigrateProcessCommitWorkerJobs do describe MigrateProcessCommitWorkerJobs do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:commit) { project.commit.raw.raw_commit } let(:commit) { project.commit.raw.rugged_commit }
describe 'Project' do describe 'Project' do
describe 'find_including_path' do describe 'find_including_path' do
......
...@@ -53,6 +53,29 @@ describe BroadcastMessage do ...@@ -53,6 +53,29 @@ describe BroadcastMessage do
2.times { described_class.current } 2.times { described_class.current }
end end
it 'includes messages that need to be displayed in the future' do
create(:broadcast_message)
future = create(
:broadcast_message,
starts_at: Time.now + 10.minutes,
ends_at: Time.now + 20.minutes
)
expect(described_class.current.length).to eq(1)
Timecop.travel(future.starts_at) do
expect(described_class.current.length).to eq(2)
end
end
it 'does not clear the cache if only a future message should be displayed' do
create(:broadcast_message, :future)
expect(Rails.cache).not_to receive(:delete)
expect(described_class.current.length).to eq(0)
end
end end
describe '#active?' do describe '#active?' do
......
...@@ -55,10 +55,15 @@ describe Ci::CreatePipelineService do ...@@ -55,10 +55,15 @@ describe Ci::CreatePipelineService do
context 'when merge requests already exist for this source branch' do context 'when merge requests already exist for this source branch' do
it 'updates head pipeline of each merge request' do it 'updates head pipeline of each merge request' do
merge_request_1 = create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project) merge_request_1 = create(:merge_request, source_branch: 'master',
merge_request_2 = create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project) target_branch: "branch_1",
source_project: project)
head_pipeline = pipeline merge_request_2 = create(:merge_request, source_branch: 'master',
target_branch: "branch_2",
source_project: project)
head_pipeline = execute_service
expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline) expect(merge_request_1.reload.head_pipeline).to eq(head_pipeline)
expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline) expect(merge_request_2.reload.head_pipeline).to eq(head_pipeline)
...@@ -66,9 +71,11 @@ describe Ci::CreatePipelineService do ...@@ -66,9 +71,11 @@ describe Ci::CreatePipelineService do
context 'when there is no pipeline for source branch' do context 'when there is no pipeline for source branch' do
it "does not update merge request head pipeline" do it "does not update merge request head pipeline" do
merge_request = create(:merge_request, source_branch: 'other_branch', target_branch: "branch_1", source_project: project) merge_request = create(:merge_request, source_branch: 'feature',
target_branch: "branch_1",
source_project: project)
head_pipeline = pipeline head_pipeline = execute_service
expect(merge_request.reload.head_pipeline).not_to eq(head_pipeline) expect(merge_request.reload.head_pipeline).not_to eq(head_pipeline)
end end
...@@ -76,13 +83,19 @@ describe Ci::CreatePipelineService do ...@@ -76,13 +83,19 @@ describe Ci::CreatePipelineService do
context 'when merge request target project is different from source project' do context 'when merge request target project is different from source project' do
let!(:target_project) { create(:project, :repository) } let!(:target_project) { create(:project, :repository) }
let!(:forked_project_link) { create(:forked_project_link, forked_to_project: project, forked_from_project: target_project) }
let!(:forked_project_link) do
create(:forked_project_link, forked_to_project: project,
forked_from_project: target_project)
end
it 'updates head pipeline for merge request' do it 'updates head pipeline for merge request' do
merge_request = merge_request = create(:merge_request, source_branch: 'master',
create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project, target_project: target_project) target_branch: "branch_1",
source_project: project,
target_project: target_project)
head_pipeline = pipeline head_pipeline = execute_service
expect(merge_request.reload.head_pipeline).to eq(head_pipeline) expect(merge_request.reload.head_pipeline).to eq(head_pipeline)
end end
...@@ -90,15 +103,36 @@ describe Ci::CreatePipelineService do ...@@ -90,15 +103,36 @@ describe Ci::CreatePipelineService do
context 'when the pipeline is not the latest for the branch' do context 'when the pipeline is not the latest for the branch' do
it 'does not update merge request head pipeline' do it 'does not update merge request head pipeline' do
merge_request = create(:merge_request, source_branch: 'master', target_branch: "branch_1", source_project: project) merge_request = create(:merge_request, source_branch: 'master',
target_branch: "branch_1",
source_project: project)
allow_any_instance_of(Ci::Pipeline).to receive(:latest?).and_return(false) allow_any_instance_of(Ci::Pipeline)
.to receive(:latest?).and_return(false)
pipeline execute_service
expect(merge_request.reload.head_pipeline).to be_nil expect(merge_request.reload.head_pipeline).to be_nil
end end
end end
context 'when pipeline has errors' do
before do
stub_ci_pipeline_yaml_file('some invalid syntax')
end
it 'updates merge request head pipeline reference' do
merge_request = create(:merge_request, source_branch: 'master',
target_branch: 'feature',
source_project: project)
head_pipeline = execute_service
expect(head_pipeline).to be_persisted
expect(head_pipeline.yaml_errors).to be_present
expect(merge_request.reload.head_pipeline).to eq head_pipeline
end
end
end end
context 'auto-cancel enabled' do context 'auto-cancel enabled' do
......
...@@ -39,14 +39,17 @@ module StubConfiguration ...@@ -39,14 +39,17 @@ module StubConfiguration
end end
def stub_storage_settings(messages) def stub_storage_settings(messages)
# Default storage is always required
messages['default'] ||= Gitlab.config.repositories.storages.default
messages.each do |storage_name, storage_settings| messages.each do |storage_name, storage_settings|
storage_settings['path'] ||= TestEnv.repos_path
storage_settings['failure_count_threshold'] ||= 10 storage_settings['failure_count_threshold'] ||= 10
storage_settings['failure_wait_time'] ||= 30 storage_settings['failure_wait_time'] ||= 30
storage_settings['failure_reset_time'] ||= 1800 storage_settings['failure_reset_time'] ||= 1800
storage_settings['storage_timeout'] ||= 5 storage_settings['storage_timeout'] ||= 5
end end
allow(Gitlab.config.repositories).to receive(:storages).and_return(messages) allow(Gitlab.config.repositories).to receive(:storages).and_return(Settingslogic.new(messages))
end end
private private
......
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