Commit b0314140 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-06-14

# Conflicts:
#	app/assets/stylesheets/framework/markdown_area.scss
#	app/assets/stylesheets/pages/merge_requests.scss
#	doc/user/markdown.md

[ci skip]
parents 636b7cab c4a3587c
......@@ -119,7 +119,7 @@ const gfmRules = {
return el.outerHTML;
},
'dl'(el, text) {
let lines = text.trim().split('\n');
let lines = text.replace(/\n\n/g, '\n').trim().split('\n');
// Add two spaces to the front of subsequent list items lines,
// or leave the line entirely blank.
lines = lines.map((l) => {
......@@ -129,9 +129,13 @@ const gfmRules = {
return ` ${line}`;
});
return `<dl>\n${lines.join('\n')}\n</dl>`;
return `<dl>\n${lines.join('\n')}\n</dl>\n`;
},
'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) {
'dt, dd, summary, details'(el, text) {
const tag = el.nodeName.toLowerCase();
return `<${tag}>${text}</${tag}>\n`;
},
'sup, sub, kbd, q, samp, var, ruby, rt, rp, abbr'(el, text) {
const tag = el.nodeName.toLowerCase();
return `<${tag}>${text}</${tag}>`;
},
......@@ -215,22 +219,22 @@ const gfmRules = {
return text.replace(/^- /mg, '1. ');
},
'h1'(el, text) {
return `# ${text.trim()}`;
return `# ${text.trim()}\n`;
},
'h2'(el, text) {
return `## ${text.trim()}`;
return `## ${text.trim()}\n`;
},
'h3'(el, text) {
return `### ${text.trim()}`;
return `### ${text.trim()}\n`;
},
'h4'(el, text) {
return `#### ${text.trim()}`;
return `#### ${text.trim()}\n`;
},
'h5'(el, text) {
return `##### ${text.trim()}`;
return `##### ${text.trim()}\n`;
},
'h6'(el, text) {
return `###### ${text.trim()}`;
return `###### ${text.trim()}\n`;
},
'strong'(el, text) {
return `**${text}**`;
......@@ -241,11 +245,13 @@ const gfmRules = {
'del'(el, text) {
return `~~${text}~~`;
},
'sup'(el, text) {
return `^${text}`;
},
'hr'(el) {
return '-----';
// extra leading \n is to ensure that there is a blank line between
// a list followed by an hr, otherwise this breaks old redcarpet rendering
return '\n-----\n';
},
'p'(el, text) {
return `${text.trim()}\n`;
},
'table'(el) {
const theadEl = el.querySelector('thead');
......@@ -263,7 +269,9 @@ const gfmRules = {
let before = '';
let after = '';
switch (cell.style.textAlign) {
const alignment = cell.align || cell.style.textAlign;
switch (alignment) {
case 'center':
before = ':';
after = ':';
......
......@@ -66,8 +66,14 @@ export default class CreateMergeRequestDropdown {
}
bindEvents() {
this.createMergeRequestButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
this.createTargetButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
this.createMergeRequestButton.addEventListener(
'click',
this.onClickCreateMergeRequestButton.bind(this),
);
this.createTargetButton.addEventListener(
'click',
this.onClickCreateMergeRequestButton.bind(this),
);
this.branchInput.addEventListener('keyup', this.onChangeInput.bind(this));
this.dropdownToggle.addEventListener('click', this.onClickSetFocusOnBranchNameInput.bind(this));
this.refInput.addEventListener('keyup', this.onChangeInput.bind(this));
......@@ -77,7 +83,8 @@ export default class CreateMergeRequestDropdown {
checkAbilityToCreateBranch() {
this.setUnavailableButtonState();
axios.get(this.canCreatePath)
axios
.get(this.canCreatePath)
.then(({ data }) => {
this.setUnavailableButtonState(false);
......@@ -105,7 +112,8 @@ export default class CreateMergeRequestDropdown {
createBranch() {
this.isCreatingBranch = true;
return axios.post(this.createBranchPath)
return axios
.post(this.createBranchPath)
.then(({ data }) => {
this.branchCreated = true;
window.location.href = data.url;
......@@ -116,7 +124,8 @@ export default class CreateMergeRequestDropdown {
createMergeRequest() {
this.isCreatingMergeRequest = true;
return axios.post(this.createMrPath)
return axios
.post(this.createMrPath)
.then(({ data }) => {
this.mergeRequestCreated = true;
window.location.href = data.url;
......@@ -195,7 +204,8 @@ export default class CreateMergeRequestDropdown {
getRef(ref, target = 'all') {
if (!ref) return false;
return axios.get(this.refsPath + ref)
return axios
.get(`${this.refsPath}${encodeURIComponent(ref)}`)
.then(({ data }) => {
const branches = data[Object.keys(data)[0]];
const tags = data[Object.keys(data)[1]];
......@@ -204,7 +214,8 @@ export default class CreateMergeRequestDropdown {
if (target === 'branch') {
result = CreateMergeRequestDropdown.findByValue(branches, ref);
} else {
result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
result =
CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
CreateMergeRequestDropdown.findByValue(tags, ref, true);
this.suggestedRef = result;
}
......@@ -255,11 +266,13 @@ export default class CreateMergeRequestDropdown {
}
isBusy() {
return this.isCreatingMergeRequest ||
return (
this.isCreatingMergeRequest ||
this.mergeRequestCreated ||
this.isCreatingBranch ||
this.branchCreated ||
this.isGettingRef;
this.isGettingRef
);
}
onChangeInput(event) {
......@@ -271,7 +284,8 @@ export default class CreateMergeRequestDropdown {
value = this.branchInput.value;
} else if (event.target === this.refInput) {
target = 'ref';
value = event.target.value.slice(0, event.target.selectionStart) +
value =
event.target.value.slice(0, event.target.selectionStart) +
event.target.value.slice(event.target.selectionEnd);
} else {
return false;
......@@ -396,7 +410,8 @@ export default class CreateMergeRequestDropdown {
showNotAvailableMessage(target) {
const { input, message } = this.getTargetData(target);
const text = target === 'branch' ? __('Branch is already taken') : __('Source is not available');
const text =
target === 'branch' ? __('Branch is already taken') : __('Source is not available');
this.removeMessage(target);
input.classList.add('gl-field-error-outline');
......@@ -459,11 +474,15 @@ export default class CreateMergeRequestDropdown {
// target - 'branch' or 'ref'
// ref - string - the new value to use as branch or ref
updateCreatePaths(target, ref) {
const pathReplacement = `$1${ref}`;
const pathReplacement = `$1${encodeURIComponent(ref)}`;
this.createBranchPath = this.createBranchPath.replace(this.regexps[target].createBranchPath,
pathReplacement);
this.createMrPath = this.createMrPath.replace(this.regexps[target].createMrPath,
pathReplacement);
this.createBranchPath = this.createBranchPath.replace(
this.regexps[target].createBranchPath,
pathReplacement,
);
this.createMrPath = this.createMrPath.replace(
this.regexps[target].createMrPath,
pathReplacement,
);
}
}
......@@ -94,7 +94,7 @@ export default {
<p class="append-bottom-0">
{{ __('Found errors in your .gitlab-ci.yml:') }}
</p>
<p class="append-bottom-0">
<p class="append-bottom-0 break-word">
{{ latestPipeline.yamlError }}
</p>
<p
......
......@@ -362,7 +362,7 @@ export default class MergeRequestTabs {
//
// status - Boolean, true to show, false to hide
toggleLoading(status) {
$('.mr-loading-status .loading').toggleClass('hidden', !status);
$('.mr-loading-status .loading').toggleClass('hide', !status);
}
diffViewType() {
......
......@@ -61,10 +61,13 @@
padding-top: 0;
line-height: 19px;
<<<<<<< HEAD
&.btn.btn-sm {
padding: 2px 5px;
}
=======
>>>>>>> upstream/master
&:focus {
margin-top: -10px;
padding-top: 10px;
......
......@@ -117,6 +117,7 @@
overflow-x: scroll;
white-space: nowrap;
min-height: 200px;
display: flex;
@include media-breakpoint-only(sm) {
height: calc(100vh - #{$issue-board-list-difference-sm});
......@@ -147,17 +148,15 @@
.board {
display: inline-block;
width: calc(85vw - 15px);
flex: 1;
min-width: 300px;
max-width: 400px;
height: 100%;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
white-space: normal;
vertical-align: top;
@include media-breakpoint-up(sm) {
width: 400px;
}
&.is-expandable {
.board-header {
cursor: pointer;
......@@ -165,6 +164,8 @@
}
&.is-collapsed {
flex: none;
min-width: 0;
width: 50px;
.board-header {
......
......@@ -51,12 +51,15 @@
opacity: 0.3;
}
<<<<<<< HEAD
&.btn-sm {
line-height: 1;
padding: 5px 10px;
margin-top: 1px;
}
=======
>>>>>>> upstream/master
&.dropdown-toggle {
.fa {
color: inherit;
......
......@@ -54,7 +54,10 @@ class SnippetsFinder < UnionFinder
end
def authorized_snippets
Snippet.where(feature_available_projects.or(not_project_related))
# This query was intentionally converted to a raw one to get it work in Rails 5.0.
# In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
# Please convert it back when on rails 5.2 as it works again as expected since 5.2.
Snippet.where("#{feature_available_projects} OR #{not_project_related}")
.public_or_visible_to_user(current_user)
end
......@@ -86,18 +89,20 @@ class SnippetsFinder < UnionFinder
def feature_available_projects
# Don't return any project related snippets if the user cannot read cross project
return table[:id].eq(nil) unless Ability.allowed?(current_user, :read_cross_project)
return table[:id].eq(nil).to_sql unless Ability.allowed?(current_user, :read_cross_project)
projects = projects_for_user do |part|
part.with_feature_available_for_user(:snippets, current_user)
end.select(:id)
arel_query = Arel::Nodes::SqlLiteral.new(projects.to_sql)
table[:project_id].in(arel_query)
# This query was intentionally converted to a raw one to get it work in Rails 5.0.
# In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
# Please convert it back when on rails 5.2 as it works again as expected since 5.2.
"snippets.project_id IN (#{projects.to_sql})"
end
def not_project_related
table[:project_id].eq(nil)
table[:project_id].eq(nil).to_sql
end
def table
......
......@@ -173,11 +173,12 @@ module ProjectsHelper
key = [
project.route.cache_key,
project.cache_key,
project.last_activity_date,
controller.controller_name,
controller.action_name,
Gitlab::CurrentSettings.cache_key,
"cross-project:#{can?(current_user, :read_cross_project)}",
'v2.5'
'v2.6'
]
key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
......
......@@ -114,7 +114,7 @@ module CacheMarkdownField
end
def latest_cached_markdown_version
return CacheMarkdownField::CACHE_REDCARPET_VERSION unless cached_markdown_version
return CacheMarkdownField::CACHE_COMMONMARK_VERSION unless cached_markdown_version
if cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
CacheMarkdownField::CACHE_REDCARPET_VERSION
......
......@@ -43,13 +43,18 @@ class GemnasiumService < Service
def execute(data)
return unless supported_events.include?(data[:object_kind])
# Gitaly: this class will be removed https://gitlab.com/gitlab-org/gitlab-ee/issues/6010
repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.path_to_repo
end
Gemnasium::GitlabService.execute(
ref: data[:ref],
before: data[:before],
after: data[:after],
token: token,
api_key: api_key,
repo: project.repository.path_to_repo # Gitaly: fixed by https://gitlab.com/gitlab-org/security-products/gemnasium-migration/issues/9
repo: repo_path
)
end
end
......@@ -44,14 +44,14 @@
.git-empty
%fieldset
%h5 Git global setup
%pre.card.bg-light
%pre.bg-light
:preserve
git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}"
%fieldset
%h5 Create a new repository
%pre.card.bg-light
%pre.bg-light
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
cd #{h @project.path}
......@@ -64,7 +64,7 @@
%fieldset
%h5 Existing folder
%pre.card.bg-light
%pre.bg-light
:preserve
cd existing_folder
git init
......@@ -77,7 +77,7 @@
%fieldset
%h5 Existing Git repository
%pre.card.bg-light
%pre.bg-light
:preserve
cd existing_repo
git remote rename origin old-origin
......
......@@ -6,12 +6,6 @@ class GitGarbageCollectWorker
# Timeout set to 24h
LEASE_TIMEOUT = 86400
GITALY_MIGRATED_TASKS = {
gc: :garbage_collect,
full_repack: :repack_full,
incremental_repack: :repack_incremental
}.freeze
def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
project = Project.find(project_id)
active_uuid = get_lease_uuid(lease_key)
......@@ -27,21 +21,7 @@ class GitGarbageCollectWorker
end
task = task.to_sym
cmd = command(task)
gitaly_migrate(GITALY_MIGRATED_TASKS[task], status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_call(task, project.repository.raw_repository)
else
repo_path = project.repository.path_to_repo
description = "'#{cmd.join(' ')}' in #{repo_path}"
Gitlab::GitLogger.info(description)
output, status = Gitlab::Popen.popen(cmd, repo_path)
Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
end
end
gitaly_call(task, project.repository.raw_repository)
# Refresh the branch cache in case garbage collection caused a ref lookup to fail
flush_ref_caches(project) if task == :gc
......@@ -82,21 +62,12 @@ class GitGarbageCollectWorker
when :incremental_repack
client.repack_incremental
end
end
def command(task)
case task
when :gc
git(write_bitmaps: bitmaps_enabled?) + %w[gc]
when :full_repack
git(write_bitmaps: bitmaps_enabled?) + %w[repack -A -d --pack-kept-objects]
when :incremental_repack
# Normal git repack fails when bitmaps are enabled. It is impossible to
# create a bitmap here anyway.
git(write_bitmaps: false) + %w[repack -d]
else
raise "Invalid gc task: #{task.inspect}"
end
rescue GRPC::NotFound => e
Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
raise Gitlab::Git::Repository::NoRepository.new(e)
rescue GRPC::BadStatus => e
Gitlab::GitLogger.error("#{method} failed:\n#{e}")
raise Gitlab::Git::CommandError.new(e)
end
def flush_ref_caches(project)
......@@ -108,19 +79,4 @@ class GitGarbageCollectWorker
def bitmaps_enabled?
Gitlab::CurrentSettings.housekeeping_bitmaps_enabled
end
def git(write_bitmaps:)
config_value = write_bitmaps ? 'true' : 'false'
%W[git -c repack.writeBitmaps=#{config_value}]
end
def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
Gitlab::GitalyClient.migrate(method, status: status, &block)
rescue GRPC::NotFound => e
Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
raise Gitlab::Git::Repository::NoRepository.new(e)
rescue GRPC::BadStatus => e
Gitlab::GitLogger.error("#{method} failed:\n#{e}")
raise Gitlab::Git::CommandError.new(e)
end
end
---
title: Fix fields for author & assignee in MR API docs.
merge_request: 19798
author: gfyoung
type: fixed
---
title: "[Rails5] Fix snippets_finder arel queries"
merge_request: 19796
author: "@blackst0ne"
type: fixed
---
title: Use CommonMark syntax and rendering for new Markdown content
merge_request: 19331
author:
type: added
---
title: 'Remove incorrect CI doc re: PowerShell'
merge_request: 19622
author: gfyoung
type: fixed
---
title: Rails5 fix stack level too deep
merge_request: 19762
author: Jasper Maes
type: fixed
---
title: 'Rails5 ActionController::ParameterMissing: param is missing or the value is
empty: application_setting'
merge_request: 19763
author: Jasper Maes
type: fixed
---
title: Rails5 fix no implicit conversion of Hash into String. ActionController::Parameters
no longer returns an hash in Rails 5
merge_request: 19792
author: Jasper Maes
type: fixed
---
title: Invalidate cache with project details when repository is updated
merge_request: 19774
author:
type: fixed
---
title: Flex issue board columns
merge_request: 19250
author: Roman Rosluk
type: changed
---
title: Fix branch name encoding for dropdown on issue page
merge_request: 19634
author:
type: fixed
......@@ -12,7 +12,9 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
end
def repository_storage_path
Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
end
end
def repository_path
......
......@@ -64,7 +64,9 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
# we rename suffix instead of removing it
path = path.sub(/\.git\z/, '_git')
check_routes(path.dup, 0, path)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
check_routes(path.dup, 0, path)
end
end
def check_routes(base, counter, path)
......
......@@ -70,18 +70,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
......@@ -190,18 +190,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
......@@ -298,18 +298,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
......@@ -550,14 +550,16 @@ Parameters:
"username": "jarrett",
"id": 5,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/b95567800f828948baf5f4160ebb2473?s=40&d=identicon"
"avatar_url": "http://www.gravatar.com/avatar/b95567800f828948baf5f4160ebb2473?s=40&d=identicon",
"web_url" : "https://gitlab.example.com/jarrett"
},
"assignee": {
"name": "Administrator",
"username": "root",
"id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40&d=identicon"
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40&d=identicon",
"web_url" : "https://gitlab.example.com/root"
},
"source_project_id": 4,
"target_project_id": 4,
......@@ -681,18 +683,18 @@ order for it to take effect:
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 3,
"target_project_id": 4,
......@@ -774,18 +776,18 @@ Must include at least one non-required attribute from above.
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 3,
"target_project_id": 4,
......@@ -884,18 +886,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 4,
"target_project_id": 4,
......@@ -964,18 +966,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
"avatar_url": null,
"web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 4,
"target_project_id": 4,
......
......@@ -799,16 +799,6 @@ cache:
- binaries/
```
If you use **Windows PowerShell** to run your shell scripts you need to replace
`$` with `$env:`:
```yaml
cache:
key: "$env:CI_COMMIT_REF_SLUG"
paths:
- binaries/
```
### `cache:untracked`
Set `untracked: true` to cache all files that are untracked in your Git
......
......@@ -15,11 +15,11 @@ Would you like to contribute to GitLab University? Then please take a look at ou
The curriculum is composed of GitLab videos, screencasts, presentations, projects and external GitLab content hosted on other services and has been organized into the following sections.
1. [GitLab Beginner](#beginner)
1. [GitLab Intermediate](#intermediate)
1. [GitLab Advanced](#advanced)
1. [External Articles](#external)
1. [Resources for GitLab Team Members](#team)
1. [GitLab Beginner](#1-gitlab-beginner)
1. [GitLab Intermediate](#2-gitlab-intermediate)
1. [GitLab Advanced](#3-gitlab-advanced)
1. [External Articles](#4-external-articles)
1. [Resources for GitLab Team Members](#5-resources-for-gitlab-team-members)
---
......@@ -126,7 +126,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [IBM: Continuous Delivery vs Continuous Deployment - Video](https://www.youtube.com/watch?v=igwFj8PPSnw)
1. [Amazon: Transition to Continuous Delivery - Video](https://www.youtube.com/watch?v=esEFaY0FDKc)
2. [TechBeacon: Doing continuous delivery? Focus first on reducing release cycle times](https://techbeacon.com/doing-continuous-delivery-focus-first-reducing-release-cycle-times)
1. See **[Integrations](#integrations)** for integrations with other CI services.
1. See **[Integrations](#39-integrations)** for integrations with other CI services.
#### 2.4. Workflow
......
......@@ -24,7 +24,10 @@ You can use GFM in the following areas:
- snippets (the snippet must be named with a `.md` extension)
- wiki pages
- markdown documents inside the repository (currently only rendered by Redcarpet)
<<<<<<< HEAD
- epics
=======
>>>>>>> upstream/master
You can also use other rich text files in GitLab. You might have to install a
dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
......@@ -550,9 +553,9 @@ Examples:
```no-highlight
1. First ordered list item
2. Another item
* Unordered sub-list.
* Unordered sub-list.
1. Actual numbers don't matter, just that it's a number
1. Ordered sub-list
1. Ordered sub-list
4. And another item.
* Unordered list can use asterisks
......@@ -564,9 +567,9 @@ Become:
1. First ordered list item
2. Another item
* Unordered sub-list.
* Unordered sub-list.
1. Actual numbers don't matter, just that it's a number
1. Ordered sub-list
1. Ordered sub-list
4. And another item.
* Unordered list can use asterisks
......@@ -581,7 +584,11 @@ Example:
```no-highlight
1. First ordered list item
<<<<<<< HEAD
Second paragraph of first item.
=======
Second paragraph of first item.
>>>>>>> upstream/master
2. Another item
```
......
......@@ -285,7 +285,8 @@ module API
attrs[key] = params_hash[key]
end
end
ActionController::Parameters.new(attrs).permit!
permitted_attrs = ActionController::Parameters.new(attrs).permit!
Gitlab.rails5? ? permitted_attrs.to_h : permitted_attrs
end
def filter_by_iid(items, iid)
......
......@@ -10,9 +10,7 @@ module API
detail "This feature was introduced in GitLab 11.0."
end
post do
# Explicitly set CommonMark as markdown engine to use.
# Remove this set when https://gitlab.com/gitlab-org/gitlab-ce/issues/43011 is done.
context = { markdown_engine: :common_mark, only_path: false }
context = { only_path: false }
if params[:project]
project = Project.find_by_full_path(params[:project])
......
......@@ -14,7 +14,7 @@ module Banzai
private
DEFAULT_ENGINE = :redcarpet
DEFAULT_ENGINE = :common_mark
def engine(engine_from_context)
engine_from_context ||= DEFAULT_ENGINE
......
......@@ -22,24 +22,9 @@ module Gitlab
private
def load_blame
raw_output = @repo.gitaly_migrate(:blame, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
load_blame_by_gitaly
else
load_blame_by_shelling_out
end
end
output = encode_utf8(raw_output)
process_raw_blame output
end
def load_blame_by_gitaly
@repo.gitaly_commit_client.raw_blame(@sha, @path)
end
output = encode_utf8(@repo.gitaly_commit_client.raw_blame(@sha, @path))
def load_blame_by_shelling_out
@repo.shell_blame(@sha, @path)
process_raw_blame(output)
end
def process_raw_blame(output)
......
......@@ -120,13 +120,11 @@ module Gitlab
# Default branch in the repository
def root_ref
@root_ref ||= gitaly_migrate(:root_ref, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_ref_client.default_branch_name
else
discover_default_branch
end
end
gitaly_ref_client.default_branch_name
rescue GRPC::NotFound => e
raise NoRepository.new(e.message)
rescue GRPC::Unknown => e
raise Gitlab::Git::CommandError.new(e.message)
end
def rugged
......@@ -152,23 +150,15 @@ module Gitlab
# Returns an Array of branch names
# sorted by name ASC
def branch_names
gitaly_migrate(:branch_names, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_ref_client.branch_names
else
branches.map(&:name)
end
wrapped_gitaly_errors do
gitaly_ref_client.branch_names
end
end
# Returns an Array of Branches
def branches
gitaly_migrate(:branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_ref_client.branches
else
branches_filter
end
wrapped_gitaly_errors do
gitaly_ref_client.branches
end
end
......@@ -200,12 +190,8 @@ module Gitlab
end
def local_branches(sort_by: nil)
gitaly_migrate(:local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_ref_client.local_branches(sort_by: sort_by)
else
branches_filter(filter: :local, sort_by: sort_by)
end
wrapped_gitaly_errors do
gitaly_ref_client.local_branches(sort_by: sort_by)
end
end
......@@ -245,18 +231,6 @@ module Gitlab
# This refs by default not visible in project page and not cloned to client side.
alias_method :has_visible_content?, :has_local_branches?
def has_local_branches_rugged?
rugged.branches.each(:local).any? do |ref|
begin
ref.name && ref.target # ensures the branch is valid
true
rescue Rugged::ReferenceError
false
end
end
end
# Returns the number of valid tags
def tag_count
gitaly_migrate(:tag_names) do |is_enabled|
......@@ -270,12 +244,8 @@ module Gitlab
# Returns an Array of tag names
def tag_names
gitaly_migrate(:tag_names, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_ref_client.tag_names
else
rugged.tags.map { |t| t.name }
end
wrapped_gitaly_errors do
gitaly_ref_client.tag_names
end
end
......@@ -283,12 +253,8 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/390
def tags
gitaly_migrate(:tags, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
tags_from_gitaly
else
tags_from_rugged
end
wrapped_gitaly_errors do
gitaly_ref_client.tags
end
end
......@@ -364,31 +330,6 @@ module Gitlab
end.map(&:name)
end
# Discovers the default branch based on the repository's available branches
#
# - If no branches are present, returns nil
# - If one branch is present, returns its name
# - If two or more branches are present, returns current HEAD or master or first branch
def discover_default_branch
names = branch_names
return if names.empty?
return names[0] if names.length == 1
if rugged_head
extracted_name = Ref.extract_branch_name(rugged_head.name)
return extracted_name if names.include?(extracted_name)
end
if names.include?('master')
'master'
else
names[0]
end
end
def rugged_head
rugged.head
rescue Rugged::ReferenceError
......@@ -1457,6 +1398,16 @@ module Gitlab
raise CommandError.new(e)
end
def wrapped_gitaly_errors(&block)
yield block
rescue GRPC::NotFound => e
raise NoRepository.new(e)
rescue GRPC::InvalidArgument => e
raise ArgumentError.new(e)
rescue GRPC::BadStatus => e
raise CommandError.new(e)
end
def clean_stale_repository_files
gitaly_migrate(:repository_cleanup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
gitaly_repository_client.cleanup if is_enabled && exists?
......@@ -1620,12 +1571,8 @@ module Gitlab
private
def uncached_has_local_branches?
gitaly_migrate(:has_local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_repository_client.has_local_branches?
else
has_local_branches_rugged?
end
wrapped_gitaly_errors do
gitaly_repository_client.has_local_branches?
end
end
......@@ -1745,20 +1692,6 @@ module Gitlab
}
end
# Gitaly note: JV: Trying to get rid of the 'filter' option so we can implement this with 'git'.
def branches_filter(filter: nil, sort_by: nil)
branches = rugged.branches.each(filter).map do |rugged_ref|
begin
target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
rescue Rugged::ReferenceError
# Omit invalid branch
end
end.compact
sort_branches(branches, sort_by)
end
def git_merged_branch_names(branch_names, root_sha)
git_arguments =
%W[branch --merged #{root_sha}
......@@ -1970,37 +1903,11 @@ module Gitlab
end
end
def tags_from_rugged
rugged.references.each("refs/tags/*").map do |ref|
message = nil
if ref.target.is_a?(Rugged::Tag::Annotation)
tag_message = ref.target.message
if tag_message.respond_to?(:chomp)
message = tag_message.chomp
end
end
target_commit = Gitlab::Git::Commit.find(self, ref.target)
Gitlab::Git::Tag.new(self, {
name: ref.name,
target: ref.target,
target_commit: target_commit,
message: message
})
end.sort_by(&:name)
end
def last_commit_for_path_by_rugged(sha, path)
sha = last_commit_id_for_path_by_shelling_out(sha, path)
commit(sha)
end
def tags_from_gitaly
gitaly_ref_client.tags
end
def size_by_shelling_out
popen(%w(du -sk), path).first.strip.to_i
end
......
......@@ -35,11 +35,6 @@ module Gitlab
MAXIMUM_GITALY_CALLS = 35
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
# We have a mechanism to let GitLab automatically opt in to all Gitaly
# features. We want to be able to exclude some features from automatic
# opt-in. That is what EXPLICIT_OPT_IN_REQUIRED is for.
EXPLICIT_OPT_IN_REQUIRED = [Gitlab::GitalyClient::StorageSettings::DISK_ACCESS_DENIED_FLAG].freeze
MUTEX = Mutex.new
class << self
......@@ -251,7 +246,7 @@ module Gitlab
when MigrationStatus::OPT_OUT
true
when MigrationStatus::OPT_IN
opt_into_all_features? && !EXPLICIT_OPT_IN_REQUIRED.include?(feature_name)
opt_into_all_features? && !explicit_opt_in_required.include?(feature_name)
else
false
end
......@@ -261,6 +256,13 @@ module Gitlab
false
end
# We have a mechanism to let GitLab automatically opt in to all Gitaly
# features. We want to be able to exclude some features from automatic
# opt-in. This function has an override in EE.
def self.explicit_opt_in_required
[]
end
# opt_into_all_features? returns true when the current environment
# is one in which we opt into features automatically
def self.opt_into_all_features?
......
......@@ -5,16 +5,18 @@ module SystemCheck
attr_accessor :orphans
def multi_check
Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
storage_path = repository_storage.legacy_disk_path
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
storage_path = repository_storage.legacy_disk_path
$stdout.puts
$stdout.puts "* Storage: #{storage_name} (#{storage_path})".color(:yellow)
$stdout.puts
$stdout.puts "* Storage: #{storage_name} (#{storage_path})".color(:yellow)
repositories = disk_repositories(storage_path)
orphans = (repositories - fetch_repositories(storage_name))
repositories = disk_repositories(storage_path)
orphans = (repositories - fetch_repositories(storage_name))
print_orphans(orphans, storage_name)
print_orphans(orphans, storage_name)
end
end
end
......
......@@ -73,7 +73,7 @@ describe Admin::ApplicationSettingsController do
end
it 'updates the restricted_visibility_levels when empty array is passed' do
put :update, application_setting: { restricted_visibility_levels: [] }
put :update, application_setting: { restricted_visibility_levels: [""] }
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.restricted_visibility_levels).to be_empty
......
......@@ -512,7 +512,7 @@ describe ApplicationController do
context '422 errors' do
it 'logs a response with a string' do
response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json')
response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
......@@ -521,7 +521,7 @@ describe ApplicationController do
it 'logs a response with an array' do
body = ['I want', 'my hat back']
response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json')
response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
......@@ -529,7 +529,7 @@ describe ApplicationController do
end
it 'does not log a string with an empty body' do
response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json')
response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
......@@ -537,7 +537,7 @@ describe ApplicationController do
end
it 'does not log an HTML body' do
response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html')
response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
......
......@@ -319,16 +319,22 @@ describe ProjectsController do
shared_examples_for 'updating a project' do
context 'when only renaming a project path' do
it "sets the repository to the right path after a rename" do
original_repository_path = project.repository.path
original_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.path
end
expect { update_project path: 'renamed_path' }
.to change { project.reload.path }
expect(project.path).to include 'renamed_path'
assign_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
assigns(:repository).path
end
if project.hashed_storage?(:repository)
expect(assigns(:repository).path).to eq(original_repository_path)
expect(assign_repository_path).to eq(original_repository_path)
else
expect(assigns(:repository).path).to include(project.path)
expect(assign_repository_path).to include(project.path)
end
expect(response).to have_gitlab_http_status(302)
......
......@@ -502,6 +502,13 @@ describe 'Copy as GFM', :js do
1. Numbered lists
GFM
# list item followed by an HR
<<-GFM.strip_heredoc,
- list item
-----
GFM
'# Heading',
'## Heading',
'### Heading',
......@@ -515,8 +522,6 @@ describe 'Copy as GFM', :js do
'~~Strikethrough~~',
'2^2',
'-----',
# table
......
......@@ -53,7 +53,7 @@ describe 'GitLab Markdown', :aggregate_failures do
# Shared behavior that all pipelines should exhibit
shared_examples 'all pipelines' do
it 'includes Redcarpet extensions' do
it 'includes extensions' do
aggregate_failures 'does not parse emphasis inside of words' do
expect(doc.to_html).not_to match('foo<em>bar</em>baz')
end
......@@ -81,10 +81,6 @@ describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures 'parses strikethroughs' do
expect(doc).to have_selector(%{del:contains("and this text doesn't")})
end
aggregate_failures 'parses superscript' do
expect(doc).to have_selector('sup', count: 2)
end
end
it 'includes SanitizationFilter' do
......@@ -132,16 +128,24 @@ describe 'GitLab Markdown', :aggregate_failures do
expect(doc).to have_selector('details summary:contains("collapsible")')
end
aggregate_failures 'permits style attribute in th elements' do
expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
aggregate_failures 'permits align attribute in th elements' do
expect(doc.at_css('th:contains("Header")')['align']).to eq 'center'
expect(doc.at_css('th:contains("Row")')['align']).to eq 'right'
expect(doc.at_css('th:contains("Example")')['align']).to eq 'left'
end
aggregate_failures 'permits style attribute in td elements' do
expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
aggregate_failures 'permits align attribute in td elements' do
expect(doc.at_css('td:contains("Foo")')['align']).to eq 'center'
expect(doc.at_css('td:contains("Bar")')['align']).to eq 'right'
expect(doc.at_css('td:contains("Baz")')['align']).to eq 'left'
end
aggregate_failures 'permits superscript elements' do
expect(doc).to have_selector('sup', count: 2)
end
aggregate_failures 'permits subscript elements' do
expect(doc).to have_selector('sub', count: 3)
end
aggregate_failures 'removes `rel` attribute from links' do
......@@ -323,6 +327,31 @@ describe 'GitLab Markdown', :aggregate_failures do
end
end
context 'Redcarpet documents' do
before do
allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
@html = markdown(@feat.raw_markdown)
end
it 'processes certain elements differently' do
aggregate_failures 'parses superscript' do
expect(doc).to have_selector('sup', count: 3)
end
aggregate_failures 'permits style attribute in th elements' do
expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
end
aggregate_failures 'permits style attribute in td elements' do
expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
end
end
end
# Fake a `current_user` helper
def current_user
@feat.user
......
......@@ -31,11 +31,14 @@ describe "User comments on issue", :js do
end
it "adds comment with code block" do
comment = "```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```"
code_block_content = "Command [1]: /usr/local/bin/git , see [text](doc/text)"
comment = "```\n#{code_block_content}\n```"
add_note(comment)
expect(page).to have_content(comment)
wait_for_requests
expect(page.find('pre code').text).to eq code_block_content
end
end
......
......@@ -36,7 +36,7 @@ feature 'Task Lists' do
MARKDOWN
end
let(:nested_tasks_markdown) do
let(:nested_tasks_markdown_redcarpet) do
<<-EOT.strip_heredoc
- [ ] Task a
- [x] Task a.1
......@@ -49,6 +49,19 @@ feature 'Task Lists' do
EOT
end
let(:nested_tasks_markdown) do
<<-EOT.strip_heredoc
- [ ] Task a
- [x] Task a.1
- [ ] Task a.2
- [ ] Task b
1. [ ] Task 1
1. [ ] Task 1.1
1. [x] Task 1.2
EOT
end
before do
Warden.test_mode!
......@@ -141,13 +154,11 @@ feature 'Task Lists' do
end
end
describe 'nested tasks', :js do
let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
shared_examples 'shared nested tasks' do
before do
allow(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
visit_issue(project, issue)
end
it 'renders' do
expect(page).to have_selector('ul.task-list', count: 2)
expect(page).to have_selector('li.task-list-item', count: 7)
......@@ -171,6 +182,30 @@ feature 'Task Lists' do
expect(page).to have_content('marked the task Task 1.1 as complete')
end
end
describe 'nested tasks', :js do
context 'with Redcarpet' do
let(:issue) { create(:issue, description: nested_tasks_markdown_redcarpet, author: user, project: project) }
before do
allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
visit_issue(project, issue)
end
it_behaves_like 'shared nested tasks'
end
context 'with CommonMark' do
let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
before do
allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('CommonMark')
visit_issue(project, issue)
end
it_behaves_like 'shared nested tasks'
end
end
end
describe 'for Notes' do
......
......@@ -43,8 +43,14 @@ This text says this, ~~and this text doesn't~~.
### Superscript
This is my 1^(st) time using superscript in Markdown. Now this is my
2^(nd).
This is my 1<sup>(st)</sup> time using superscript in Markdown. Now this is my
2<sup>(nd)</sup>.
Redcarpet supports this superscript syntax ( x^2 ).
### Subscript
This (C<sub>6</sub>H<sub>12</sub>O<sub>6</sub>) is an example of subscripts in Markdown.
### Next step
......
......@@ -298,7 +298,7 @@ describe MarkupHelper do
it 'preserves code color scheme' do
object = create_object("```ruby\ndef test\n 'hello world'\nend\n```")
expected = "\n<pre class=\"code highlight js-syntax-highlight ruby\">" \
expected = "<pre class=\"code highlight js-syntax-highlight ruby\">" \
"<code><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \
"</code></pre>"
......
......@@ -90,6 +90,10 @@ describe ProjectsHelper do
expect(helper.project_list_cache_key(project)).to include(project.cache_key)
end
it "includes the last activity date" do
expect(helper.project_list_cache_key(project)).to include(project.last_activity_date)
end
it "includes the controller name" do
expect(helper.controller).to receive(:controller_name).and_return("testcontroller")
......@@ -285,7 +289,11 @@ describe ProjectsHelper do
describe '#sanitize_repo_path' do
let(:project) { create(:project, :repository) }
let(:storage_path) { Gitlab.config.repositories.storages.default.legacy_disk_path }
let(:storage_path) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
Gitlab.config.repositories.storages.default.legacy_disk_path
end
end
before do
allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
......
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import CreateMergeRequestDropdown from '~/create_merge_request_dropdown';
import { TEST_HOST } from 'spec/test_constants';
describe('CreateMergeRequestDropdown', () => {
let axiosMock;
let dropdown;
beforeEach(() => {
axiosMock = new MockAdapter(axios);
setFixtures(`
<div id="dummy-wrapper-element">
<div class="available"></div>
<div class="unavailable">
<div class="fa"></div>
<div class="text"></div>
</div>
<div class="js-ref"></div>
<div class="js-create-merge-request"></div>
<div class="js-create-target"></div>
<div class="js-dropdown-toggle"></div>
</div>
`);
const dummyElement = document.getElementById('dummy-wrapper-element');
dropdown = new CreateMergeRequestDropdown(dummyElement);
dropdown.refsPath = `${TEST_HOST}/dummy/refs?search=`;
});
afterEach(() => {
axiosMock.restore();
});
describe('getRef', () => {
it('escapes branch names correctly', done => {
const endpoint = `${dropdown.refsPath}contains%23hash`;
spyOn(axios, 'get').and.callThrough();
axiosMock.onGet(endpoint).replyOnce({});
dropdown
.getRef('contains#hash')
.then(() => {
expect(axios.get).toHaveBeenCalledWith(endpoint);
})
.then(done)
.catch(done.fail);
});
});
describe('updateCreatePaths', () => {
it('escapes branch names correctly', () => {
dropdown.createBranchPath = `${TEST_HOST}/branches?branch_name=some-branch&issue=42`;
dropdown.createMrPath = `${TEST_HOST}/create_merge_request?branch_name=some-branch&ref=master`;
dropdown.updateCreatePaths('branch', 'contains#hash');
expect(dropdown.createBranchPath).toBe(
`${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`,
);
expect(dropdown.createMrPath).toBe(
`${TEST_HOST}/create_merge_request?branch_name=contains%23hash&ref=master`,
);
});
});
});
......@@ -66,7 +66,7 @@ describe('ShortcutsIssuable', function () {
});
describe('with a multi-line selection', () => {
it('quotes the selected lines as a group', () => {
stubSelection('<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>');
stubSelection('<p>Selected line one.</p>\n<p>Selected line two.</p>\n<p>Selected line three.</p>');
this.shortcut.replyWithSelectedText(true);
expect($(this.selector).val()).toBe('> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n');
});
......
......@@ -7,13 +7,13 @@ describe Banzai::Filter::MarkdownFilter do
it 'adds language to lang attribute when specified' do
result = filter("```html\nsome code\n```")
expect(result).to start_with("\n<pre><code lang=\"html\">")
expect(result).to start_with("<pre><code lang=\"html\">")
end
it 'does not add language to lang attribute when not specified' do
result = filter("```\nsome code\n```")
expect(result).to start_with("\n<pre><code>")
expect(result).to start_with("<pre><code>")
end
end
end
......@@ -7,7 +7,7 @@ describe Gitlab::Git::Blame, seed_helper: true do
Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md")
end
shared_examples 'blaming a file' do
describe 'blaming a file' do
context "each count" do
it do
data = []
......@@ -68,12 +68,4 @@ describe Gitlab::Git::Blame, seed_helper: true do
end
end
end
context 'when Gitaly blame feature is enabled' do
it_behaves_like 'blaming a file'
end
context 'when Gitaly blame feature is disabled', :skip_gitaly_mock do
it_behaves_like 'blaming a file'
end
end
......@@ -77,17 +77,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#root_ref' do
context 'with gitaly disabled' do
before do
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
end
it 'calls #discover_default_branch' do
expect(repository).to receive(:discover_default_branch)
repository.root_ref
end
end
it 'returns UTF-8' do
expect(repository.root_ref).to be_utf8
end
......@@ -153,46 +142,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
describe "#discover_default_branch" do
let(:master) { 'master' }
let(:feature) { 'feature' }
let(:feature2) { 'feature2' }
around do |example|
# discover_default_branch will be moved to gitaly-ruby
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
example.run
end
end
it "returns 'master' when master exists" do
expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master])
expect(repository.discover_default_branch).to eq('master')
end
it "returns non-master when master exists but default branch is set to something else" do
File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/feature')
expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master])
expect(repository.discover_default_branch).to eq('feature')
File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/master')
end
it "returns a non-master branch when only one exists" do
expect(repository).to receive(:branch_names).at_least(:once).and_return([feature])
expect(repository.discover_default_branch).to eq('feature')
end
it "returns a non-master branch when more than one exists and master does not" do
expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, feature2])
expect(repository.discover_default_branch).to eq('feature')
end
it "returns nil when no branch exists" do
expect(repository).to receive(:branch_names).at_least(:once).and_return([])
expect(repository.discover_default_branch).to be_nil
end
end
describe '#branch_names' do
subject { repository.branch_names }
......@@ -476,7 +425,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#has_local_branches?' do
shared_examples 'check for local branches' do
context 'check for local branches' do
it { expect(repository.has_local_branches?).to eq(true) }
context 'mutable' do
......@@ -510,14 +459,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
end
context 'with gitaly' do
it_behaves_like 'check for local branches'
end
context 'without gitaly', :skip_gitaly_mock do
it_behaves_like 'check for local branches'
end
end
describe "#delete_branch" do
......@@ -1395,24 +1336,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
# With Gitaly enabled, Gitaly just doesn't return deleted branches.
context 'with deleted branch with Gitaly disabled' do
before do
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
end
it 'returns no results' do
ref = double()
allow(ref).to receive(:name) { 'bad-branch' }
allow(ref).to receive(:target) { raise Rugged::ReferenceError }
branches = double()
allow(branches).to receive(:each) { [ref].each }
allow(repository_rugged).to receive(:branches) { branches }
expect(subject).to be_empty
end
end
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branches
end
......
......@@ -52,7 +52,9 @@ describe Gitlab::GitAccessWiki do
context 'when the wiki repository does not exist' do
it 'returns not found' do
wiki_repo = project.wiki.repository
FileUtils.rm_rf(wiki_repo.path)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
FileUtils.rm_rf(wiki_repo.path)
end
# Sanity check for rm_rf
expect(wiki_repo.exists?).to eq(false)
......
......@@ -6,7 +6,11 @@ require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_
describe MigrateProcessCommitWorkerJobs do
let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
let(:commit) { project.commit.raw.rugged_commit }
let(:commit) do
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.commit.raw.rugged_commit
end
end
describe 'Project' do
describe 'find_including_path' do
......
......@@ -49,10 +49,14 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
end
it 'renames the repository of any projects' do
expect(updated_project.repository.path)
repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
updated_project.repository.path
end
expect(repo_path)
.to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
expect(File.directory?(updated_project.repository.path)).to eq(true)
expect(File.directory?(repo_path)).to eq(true)
end
it 'creates a redirect route for renamed projects' do
......
......@@ -156,7 +156,7 @@ describe CacheMarkdownField do
end
it { expect(thing.foo_html).to eq(updated_html) }
it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION) }
it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
end
describe '#cached_html_up_to_date?' do
......@@ -234,7 +234,7 @@ describe CacheMarkdownField do
it 'returns default version when version is nil' do
thing.cached_markdown_version = nil
is_expected.to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
is_expected.to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
......@@ -261,7 +261,7 @@ describe CacheMarkdownField do
thing.cached_markdown_version = nil
thing.refresh_markdown_cache
expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
......@@ -346,7 +346,7 @@ describe CacheMarkdownField do
expect(thing.foo_html).to eq(updated_html)
expect(thing.baz_html).to eq(updated_html)
expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
......@@ -366,7 +366,7 @@ describe CacheMarkdownField do
expect(thing.foo_html).to eq(updated_html)
expect(thing.baz_html).to eq(updated_html)
expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
end
......
......@@ -3306,7 +3306,7 @@ describe Project do
project.rename_repo
expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
end
end
......@@ -3467,7 +3467,7 @@ describe Project do
it 'updates project full path in .git/config' do
project.rename_repo
expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
end
end
......@@ -3911,13 +3911,13 @@ describe Project do
it 'writes full path in .git/config when key is missing' do
project.write_repository_config
expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
expect(rugged_config['gitlab.fullpath']).to eq project.full_path
end
it 'updates full path in .git/config when key is present' do
project.write_repository_config(gl_full_path: 'old/path')
expect { project.write_repository_config }.to change { project.repository.rugged.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
end
it 'does not raise an error with an empty repository' do
......@@ -4203,4 +4203,10 @@ describe Project do
let(:uploader_class) { AttachmentUploader }
end
end
def rugged_config
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.rugged.config
end
end
end
......@@ -196,7 +196,11 @@ describe ProjectWiki do
before do
subject.wiki # Make sure the wiki repo exists
BareRepoOperations.new(subject.repository.path_to_repo).commit_file(image, 'image.png')
repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
subject.repository.path_to_repo
end
BareRepoOperations.new(repo_path).commit_file(image, 'image.png')
end
it 'returns the latest version of the file if it exists' do
......
......@@ -74,7 +74,9 @@ describe RemoteMirror do
mirror.update_attribute(:url, 'http://foo:baz@test.com')
config = repo.raw_repository.rugged.config
config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
repo.raw_repository.rugged.config
end
expect(config["remote.#{mirror.remote_name}.url"]).to eq('http://foo:baz@test.com')
end
......
......@@ -522,7 +522,6 @@ describe API::Internal do
context 'the project path was changed' do
let(:project) { create(:project, :repository, :legacy_storage) }
let!(:old_path_to_repo) { project.repository.path_to_repo }
let!(:repository) { project.repository }
before do
......
......@@ -272,8 +272,11 @@ describe Projects::CreateService, '#execute' do
it 'writes project full path to .git/config' do
project = create_project(user, opts)
rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.rugged
end
expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
expect(rugged.config['gitlab.fullpath']).to eq project.full_path
end
def create_project(user, opts)
......
......@@ -9,7 +9,7 @@ RSpec.configure do |config|
# Use 'and_wrap_original' to make sure the arguments are valid
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_wrap_original do |m, *args|
m.call(*args)
!Gitlab::GitalyClient::EXPLICIT_OPT_IN_REQUIRED.include?(args.first)
!Gitlab::GitalyClient.explicit_opt_in_required.include?(args.first)
end
end
end
......
# monkey patch which fixes serialization matcher in Rails 5
# https://github.com/thoughtbot/shoulda-matchers/issues/913
# This can be removed when a new version of shoulda-matchers
# is released
module Shoulda
module Matchers
class RailsShim
def self.serialized_attributes_for(model)
if defined?(::ActiveRecord::Type::Serialized)
# Rails 5+
serialized_columns = model.columns.select do |column|
model.type_for_attribute(column.name).is_a?(
::ActiveRecord::Type::Serialized
)
end
serialized_columns.inject({}) do |hash, column| # rubocop:disable Style/EachWithObject
hash[column.name.to_s] = model.type_for_attribute(column.name).coder
hash
end
else
model.serialized_attributes
end
end
end
end
end
......@@ -11,36 +11,63 @@ describe GitGarbageCollectWorker do
subject { described_class.new }
describe "#perform" do
shared_examples 'flushing ref caches' do |gitaly|
context 'with active lease_uuid' do
context 'with active lease_uuid' do
before do
allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
end
it "flushes ref caches when the task if 'gc'" do
expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
.and_return(nil)
expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
subject.perform(project.id, :gc, lease_key, lease_uuid)
end
end
context 'with different lease than the active one' do
before do
allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid)
end
it 'returns silently' do
expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
subject.perform(project.id, :gc, lease_key, lease_uuid)
end
end
context 'with no active lease' do
before do
allow(subject).to receive(:get_lease_uuid).and_return(false)
end
context 'when is able to get the lease' do
before do
allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid)
end
it "flushes ref caches when the task if 'gc'" do
expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original
expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
if gitaly
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
.and_return(nil)
else
expect(Gitlab::Popen).to receive(:popen)
.with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
end
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
.and_return(nil)
expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
subject.perform(project.id, :gc, lease_key, lease_uuid)
subject.perform(project.id)
end
end
context 'with different lease than the active one' do
context 'when no lease can be obtained' do
before do
allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid)
expect(subject).to receive(:try_obtain_lease).and_return(false)
end
it 'returns silently' do
......@@ -49,63 +76,9 @@ describe GitGarbageCollectWorker do
expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
subject.perform(project.id, :gc, lease_key, lease_uuid)
subject.perform(project.id)
end
end
context 'with no active lease' do
before do
allow(subject).to receive(:get_lease_uuid).and_return(false)
end
context 'when is able to get the lease' do
before do
allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid)
end
it "flushes ref caches when the task if 'gc'" do
expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
if gitaly
expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
.and_return(nil)
else
expect(Gitlab::Popen).to receive(:popen)
.with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
end
expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
subject.perform(project.id)
end
end
context 'when no lease can be obtained' do
before do
expect(subject).to receive(:try_obtain_lease).and_return(false)
end
it 'returns silently' do
expect(subject).not_to receive(:command)
expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
subject.perform(project.id)
end
end
end
end
context "with Gitaly turned on" do
it_should_behave_like 'flushing ref caches', true
end
context "with Gitaly turned off", :disable_gitaly do
it_should_behave_like 'flushing ref caches', false
end
context "repack_full" do
......
......@@ -44,7 +44,9 @@ describe RepositoryRemoveRemoteWorker do
end
def create_remote_branch(remote_name, branch_name, target)
rugged = project.repository.rugged
rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
project.repository.rugged
end
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
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