Commit 6fae9319 authored by Steve Halasz's avatar Steve Halasz

Merge branch 'master' into 18256-secret-token-docs

parents 7612f1c4 8765d2f9
......@@ -252,6 +252,9 @@ coverage:
notify:slack:
stage: post-test
variables:
USE_DB: "false"
USE_BUNDLE_INSTALL: "false"
script:
- ./scripts/notify_slack.sh "#builds" "Build on \`$CI_BUILD_REF_NAME\` failed! Commit \`$(git log -1 --oneline)\` See <https://gitlab.com/gitlab-org/$(basename "$PWD")/commit/"$CI_BUILD_REF"/builds>"
when: on_failure
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased)
- Fix don't pass a local variable called `i` to a partial. !20510 (herminiotorres)
- Fix rename `add_users_into_project` and `projects_ids`. !20512 (herminiotorres)
- Fix the title of the toggle dropdown button. !5515 (herminiotorres)
- Improve diff performance by eliminating redundant checks for text blobs
- Convert switch icon into icon font (ClemMakesApps)
- Remove magic comments (`# encoding: UTF-8`) from Ruby files. !5456 (winniehell)
- Add support for relative links starting with ./ or / to RelativeLinkFilter (winniehell)
- Ignore URLs starting with // in Markdown links !5677 (winniehell)
- Fix CI status icon link underline (ClemMakesApps)
- The Repository class is now instrumented
- Cache the commit author in RequestStore to avoid extra lookups in PostReceive
......@@ -15,14 +18,19 @@ v 8.11.0 (unreleased)
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
- Optimize maximum user access level lookup in loading of notes
- Add "No one can push" as an option for protected branches. !5081
- Improve performance of AutolinkFilter#text_parse by using XPath
- Environments have an url to link to
- Update `timeago` plugin to use multiple string/locale settings
- Remove unused images (ClemMakesApps)
- Limit git rev-list output count to one in forced push check
- Clean up unused routes (Josef Strzibny)
- Add green outline to New Branch button. !5447 (winniehell)
- Improve performance of syntax highlighting Markdown code blocks
- Update to gitlab_git 10.4.1 and take advantage of preserved Ref objects
- Remove delay when hitting "Reply..." button on page with a lot of discussions
- Retrieve rendered HTML from cache in one request
- Fix renaming repository when name contains invalid chararacters under project settings
- Fix devise deprecation warnings.
- Optimize checking if a user has read access to a list of issues !5370
- Nokogiri's various parsing methods are now instrumented
- Add simple identifier to public SSH keys (muteor)
......@@ -31,14 +39,17 @@ v 8.11.0 (unreleased)
- Include old revision in merge request update hooks (Ben Boeckel)
- Add build event color in HipChat messages (David Eisner)
- Make fork counter always clickable. !5463 (winniehell)
- Gitlab::Highlight is now instrumented
- All created issues, API or WebUI, can be submitted to Akismet for spam check !5333
- The overhead of instrumented method calls has been reduced
- Remove `search_id` of labels dropdown filter to fix 'Missleading URI for labels in Merge Requests and Issues view'. !5368 (Scott Le)
- Load project invited groups and members eagerly in `ProjectTeam#fetch_members`
- Bump gitlab_git to speedup DiffCollection iterations
- Rewrite description of a blocked user in admin settings. (Elias Werberich)
- Make branches sortable without push permission !5462 (winniehell)
- Check for Ci::Build artifacts at database level on pipeline partial
- Convert image diff background image to CSS (ClemMakesApps)
- Remove unnecessary index_projects_on_builds_enabled index from the projects table
- Make "New issue" button in Issue page less obtrusive !5457 (winniehell)
- Gitlab::Metrics.current_transaction needs to be public for RailsQueueDuration
- Fix search for notes which belongs to deleted objects
......@@ -54,12 +65,23 @@ v 8.11.0 (unreleased)
- Make error pages responsive (Takuya Noguchi)
- Fix skip_repo parameter being ignored when destroying a namespace
- Change requests_profiles resource constraint to catch virtually any file
- Bump gitlab_git to lazy load compare commits
- Reduce number of queries made for merge_requests/:id/diffs
- Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
- Fix RequestProfiler::Middleware error when code is reloaded in development
- Catch what warden might throw when profiling requests to re-throw it
- Add description to new_issue email and new_merge_request_email in text/plain content type. !5663 (dixpac)
- Speed up and reduce memory usage of Commit#repo_changes, Repository#expire_avatar_cache and IrkerWorker
- Document that webhook secret token is sent in X-Gitlab-Token HTTP header
- Add unfold links for Side-by-Side view. !5415 (Tim Masliuchenko)
v 8.10.5 (unreleased)
v 8.10.4
- Don't close referenced upstream issues from a forked project.
- Fixes issue with dropdowns `enter` key not working correctly. !5544
- Fix Import/Export project import not working in HA mode. !5618
- Fix Import/Export error checking versions. !5638
v 8.10.3
- Fix Import/Export issue importing milestones and labels not associated properly. !5426
......
......@@ -462,7 +462,8 @@ merge request:
- string literal quoting style **Option A**: single quoted by default
1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Testing](doc/development/testing.md)
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript)
1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/master/es5)
1. [SCSS styleguide][scss-styleguide]
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
contributors to enhance security
......
......@@ -53,7 +53,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.4.3'
gem 'gitlab_git', '~> 10.4.5'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......@@ -326,7 +326,7 @@ group :production do
gem 'gitlab_meta', '7.0'
end
gem 'newrelic_rpm', '~> 3.14'
gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.3.0'
......
......@@ -278,7 +278,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab_git (10.4.3)
gitlab_git (10.4.5)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -404,7 +404,7 @@ GEM
nested_form (0.3.2)
net-ldap (0.12.1)
net-ssh (3.0.1)
newrelic_rpm (3.14.1.311)
newrelic_rpm (3.16.0.318)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
......@@ -870,7 +870,7 @@ DEPENDENCIES
github-linguist (~> 4.7.0)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.4.3)
gitlab_git (~> 10.4.5)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
......@@ -902,7 +902,7 @@ DEPENDENCIES
mysql2 (~> 0.3.16)
nested_form (~> 0.3.2)
net-ssh (~> 3.0.1)
newrelic_rpm (~> 3.14)
newrelic_rpm (~> 3.16)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0)
octokit (~> 4.3.0)
......
......@@ -287,7 +287,7 @@
$('.page-with-sidebar').toggleClass('page-sidebar-collapsed page-sidebar-expanded').removeClass('page-sidebar-pinned');
$('.navbar-fixed-top').removeClass('header-pinned-nav');
}
return $document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
$document.off('click', '.js-nav-pin').on('click', '.js-nav-pin', function(e) {
var $page, $pinBtn, $tooltip, $topNav, doPinNav, tooltipText;
e.preventDefault();
$pinBtn = $(e.currentTarget);
......@@ -315,6 +315,8 @@
$tooltip.find('.tooltip-inner').text(tooltipText);
return $pinBtn.attr('title', tooltipText).tooltip('fixTitle');
});
});
// Custom time ago
gl.utils.shortTimeAgo($('.js-short-timeago'));
});
}).call(this);
......@@ -10,7 +10,7 @@
$(document).off('click', '.js-unfold');
$(document).on('click', '.js-unfold', (function(_this) {
return function(event) {
var line_number, link, offset, old_line, params, prev_new_line, prev_old_line, ref, ref1, since, target, to, unfold, unfoldBottom;
var line_number, link, file, offset, old_line, params, prev_new_line, prev_old_line, ref, ref1, since, target, to, unfold, unfoldBottom;
target = $(event.target);
unfoldBottom = target.hasClass('js-unfold-bottom');
unfold = true;
......@@ -31,14 +31,16 @@
unfold = false;
}
}
link = target.parents('.diff-file').attr('data-blob-diff-path');
file = target.parents('.diff-file');
link = file.data('blob-diff-path');
params = {
since: since,
to: to,
bottom: unfoldBottom,
offset: offset,
unfold: unfold,
indent: 1
indent: 1,
view: file.data('view')
};
return $.get(link, params, function(response) {
return target.parent().replaceWith(response);
......@@ -48,26 +50,13 @@
}
Diff.prototype.lineNumbers = function(line) {
var i, l, len, line_number, line_numbers, lines, results;
if (!line.children().length) {
return [0, 0];
}
lines = line.children().slice(0, 2);
line_numbers = (function() {
var i, len, results;
results = [];
for (i = 0, len = lines.length; i < len; i++) {
l = lines[i];
results.push($(l).attr('data-linenumber'));
}
return results;
})();
results = [];
for (i = 0, len = line_numbers.length; i < len; i++) {
line_number = line_numbers[i];
results.push(parseInt(line_number));
}
return results;
return line.find('.diff-line-num').map(function() {
return parseInt($(this).data('linenumber'));
});
};
return Diff;
......
......@@ -28,38 +28,43 @@
};
})(this));
timeout = "";
this.input.on("keyup", (function(_this) {
return function(e) {
this.input
.on('keydown', function (e) {
var keyCode = e.which;
if (keyCode === 13) {
e.preventDefault()
}
})
.on('keyup', function(e) {
var keyCode;
keyCode = e.which;
if (ARROW_KEY_CODES.indexOf(keyCode) >= 0) {
return;
}
if (_this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
if (this.input.val() !== "" && !$inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.addClass(HAS_VALUE_CLASS);
} else if (_this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
} else if (this.input.val() === "" && $inputContainer.hasClass(HAS_VALUE_CLASS)) {
$inputContainer.removeClass(HAS_VALUE_CLASS);
}
if (keyCode === 13) {
return false;
}
if (_this.options.remote) {
if (this.options.remote) {
clearTimeout(timeout);
return timeout = setTimeout(function() {
var blur_field;
blur_field = _this.shouldBlur(keyCode);
if (blur_field && _this.filterInputBlur) {
_this.input.blur();
var blurField = this.shouldBlur(keyCode);
if (blurField && this.filterInputBlur) {
this.input.blur();
}
return _this.options.query(_this.input.val(), function(data) {
return _this.options.callback(data);
});
}, 250);
return this.options.query(this.input.val(), function(data) {
return this.options.callback(data);
}.bind(this));
}.bind(this), 250);
} else {
return _this.filter(_this.input.val());
return this.filter(this.input.val());
}
};
})(this));
}.bind(this));
}
GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
......@@ -382,6 +387,7 @@
GitLabDropdown.prototype.opened = function() {
var contentHtml;
currentIndex = -1;
this.addArrowKeyEvent();
if (this.options.setIndeterminateIds) {
this.options.setIndeterminateIds.call(this);
......@@ -619,7 +625,7 @@
var $input, ARROW_KEY_CODES, selector;
ARROW_KEY_CODES = [38, 40];
$input = this.dropdown.find(".dropdown-input-field");
selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator)';
selector = '.dropdown-content li:not(.divider,.dropdown-header,.separator):visible';
if (this.dropdown.find(".dropdown-toggle-page").length) {
selector = ".dropdown-page-one " + selector;
}
......@@ -647,7 +653,7 @@
return false;
}
if (currentKeyCode === 13 && currentIndex !== -1) {
return _this.selectRowAtIndex(e, currentIndex);
return _this.selectRowAtIndex(e, $('.is-focused', _this.dropdown).closest('li').index() - 1);
}
};
})(this));
......
......@@ -8,13 +8,16 @@
base.utils = {};
}
w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
w.gl.utils.formatDate = function(datetime) {
return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
};
w.gl.utils.getDayName = function(date) {
return this.days[date.getDay()];
};
return w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago) {
w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago) {
if (setTimeago == null) {
setTimeago = true;
}
......@@ -31,6 +34,39 @@
});
}
};
w.gl.utils.shortTimeAgo = function($el) {
var shortLocale, tmpLocale;
shortLocale = {
prefixAgo: null,
prefixFromNow: null,
suffixAgo: 'ago',
suffixFromNow: 'from now',
seconds: '1 min',
minute: '1 min',
minutes: '%d mins',
hour: '1 hr',
hours: '%d hrs',
day: '1 day',
days: '%d days',
month: '1 month',
months: '%d months',
year: '1 year',
years: '%d years',
wordSeparator: ' ',
numbers: []
};
tmpLocale = $.timeago.settings.strings;
$el.each(function(el) {
var $el1;
$el1 = $(this);
return $el1.attr('title', gl.utils.formatDate($el.attr('datetime')));
});
$.timeago.settings.strings = shortLocale;
$el.timeago();
$.timeago.settings.strings = tmpLocale;
};
})(window);
}).call(this);
......@@ -89,8 +89,14 @@
toggleLabel: function(obj, $el) {
return $el.text().trim();
},
clicked: function(e) {
return $dropdown.closest('form').submit();
clicked: function(selected, $el, e) {
e.preventDefault()
if ($('input[name="ref"]').length) {
var $form = $dropdown.closest('form'),
action = $form.attr('action'),
divider = action.indexOf('?') < 0 ? '?' : '&';
Turbolinks.visit(action + '' + divider + '' + $form.serialize());
}
}
});
});
......
......@@ -114,6 +114,12 @@ ul.content-list {
font-size: $list-font-size;
color: $list-text-color;
&.no-description {
.title {
line-height: $list-text-height;
}
}
.title {
font-weight: 600;
}
......@@ -134,12 +140,11 @@ ul.content-list {
}
.controls {
padding-top: 1px;
float: right;
> .control-text {
margin-right: $gl-padding-top;
line-height: 40px;
line-height: $list-text-height;
&:last-child {
margin-right: 0;
......@@ -150,7 +155,7 @@ ul.content-list {
> .btn-group {
margin-right: $gl-padding-top;
display: inline-block;
margin-top: 4px;
margin-top: 3px;
margin-bottom: 4px;
&:last-child {
......
......@@ -43,6 +43,7 @@ $gl-header-color: $gl-title-color;
$list-font-size: $gl-font-size;
$list-title-color: $gl-title-color;
$list-text-color: $gl-text-color;
$list-text-height: 42px;
/*
* Markdown
......
......@@ -23,15 +23,9 @@
}
.group-row {
&.no-description {
.group-name {
line-height: 44px;
}
}
.stats {
float: right;
line-height: 44px;
line-height: $list-text-height;
color: $gl-gray;
span {
......
......@@ -512,18 +512,12 @@ pre.light-well {
.project-row {
border-color: $table-border-color;
&.no-description {
.project {
line-height: 40px;
}
}
.project-full-name {
@include str-truncated;
}
.controls {
line-height: 40px;
line-height: $list-text-height;
a:hover {
text-decoration: none;
......
......@@ -12,13 +12,14 @@ class Import::GitlabProjectsController < Import::BaseController
return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
end
imported_file = project_params[:file].path + "-import"
import_upload_path = Gitlab::ImportExport.import_upload_path(filename: project_params[:file].original_filename)
FileUtils.copy_entry(project_params[:file].path, imported_file)
FileUtils.mkdir_p(File.dirname(import_upload_path))
FileUtils.copy_entry(project_params[:file].path, import_upload_path)
@project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id],
current_user,
File.expand_path(imported_file),
import_upload_path,
project_params[:path]).execute
if @project.saved?
......
......@@ -76,6 +76,8 @@ class Projects::BlobController < Projects::ApplicationController
end
def diff
apply_diff_view_cookie!
@form = UnfoldForm.new(params)
@lines = Gitlab::Highlight.highlight_lines(repository, @ref, @path)
@lines = @lines[@form.since - 1..@form.to - 1]
......
......@@ -101,7 +101,7 @@ class SessionsController < Devise::SessionsController
# Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil
redirect_to user_omniauth_authorize_path(provider.to_sym)
redirect_to omniauth_authorize_path(:user, provider)
end
def valid_otp_attempt?(user)
......
......@@ -163,9 +163,13 @@ module ApplicationHelper
# `html_class` argument is provided.
#
# Returns an HTML-safe String
def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
def time_ago_with_tooltip(time, placement: 'top', html_class: '', skip_js: false, short_format: false)
css_classes = short_format ? 'js-short-timeago' : 'js-timeago'
css_classes << " #{html_class}" unless html_class.blank?
css_classes << ' js-timeago-pending' unless skip_js
element = content_tag :time, time.to_s,
class: "#{html_class} js-timeago #{"js-timeago-pending" unless skip_js}",
class: css_classes,
datetime: time.to_time.getutc.iso8601,
title: time.to_time.in_time_zone.to_s(:medium),
data: { toggle: 'tooltip', placement: placement, container: 'body' }
......
......@@ -13,12 +13,11 @@ module DiffHelper
end
def diff_view
diff_views = %w(inline parallel)
if diff_views.include?(cookies[:diff_view])
cookies[:diff_view]
else
diff_views.first
@diff_view ||= begin
diff_views = %w(inline parallel)
diff_view = cookies[:diff_view]
diff_view = diff_views.first unless diff_views.include?(diff_view)
diff_view.to_sym
end
end
......@@ -33,12 +32,23 @@ module DiffHelper
options
end
def unfold_bottom_class(bottom)
bottom ? 'js-unfold js-unfold-bottom' : ''
end
def diff_match_line(old_pos, new_pos, text: '', view: :inline, bottom: false)
content = content_tag :td, text, class: "line_content match #{view == :inline ? '' : view}"
cls = ['diff-line-num', 'unfold', 'js-unfold']
cls << 'js-unfold-bottom' if bottom
html = ''
if old_pos
html << content_tag(:td, '...', class: cls + ['old_line'], data: { linenumber: old_pos })
html << content unless view == :inline
end
if new_pos
html << content_tag(:td, '...', class: cls + ['new_line'], data: { linenumber: new_pos })
html << content
end
def unfold_class(unfold)
unfold ? 'unfold js-unfold' : ''
html.html_safe
end
def diff_line_content(line, line_type = nil)
......@@ -67,11 +77,11 @@ module DiffHelper
end
def inline_diff_btn
diff_btn('Inline', 'inline', diff_view == 'inline')
diff_btn('Inline', 'inline', diff_view == :inline)
end
def parallel_diff_btn
diff_btn('Side-by-side', 'parallel', diff_view == 'parallel')
diff_btn('Side-by-side', 'parallel', diff_view == :parallel)
end
def submodule_link(blob, ref, repository = @repository)
......@@ -103,7 +113,8 @@ module DiffHelper
commit = commit_for_diff(diff_file)
{
blob_diff_path: namespace_project_blob_diff_path(project.namespace, project,
tree_join(commit.id, diff_file.file_path))
tree_join(commit.id, diff_file.file_path)),
view: diff_view
}
end
......
......@@ -6,6 +6,10 @@ class Ability
return [] unless user.is_a?(User)
return [] if user.blocked?
abilities_by_subject_class(user: user, subject: subject)
end
def abilities_by_subject_class(user:, subject:)
case subject
when CommitStatus then commit_status_abilities(user, subject)
when Project then project_abilities(user, subject)
......
......@@ -21,19 +21,19 @@ class ProjectMember < Member
# or symbol like :master representing role
#
# Ex.
# add_users_into_projects(
# add_users_to_projects(
# project_ids,
# user_ids,
# ProjectMember::MASTER
# )
#
# add_users_into_projects(
# add_users_to_projects(
# project_ids,
# user_ids,
# :master
# )
#
def add_users_into_projects(project_ids, user_ids, access, current_user = nil)
def add_users_to_projects(project_ids, user_ids, access, current_user = nil)
access_level = if roles_hash.has_key?(access)
roles_hash[access]
elsif roles_hash.values.include?(access.to_i)
......
......@@ -378,11 +378,6 @@ class Project < ActiveRecord::Base
joins(join_body).reorder('join_note_counts.amount DESC')
end
# Deletes gitlab project export files older than 24 hours
def remove_gitlab_exports!
Gitlab::Popen.popen(%W(find #{Gitlab::ImportExport.storage_path} -not -path #{Gitlab::ImportExport.storage_path} -mmin +1440 -delete))
end
end
def repository_storage_path
......
......@@ -34,7 +34,7 @@ class ProjectTeam
end
def add_users(users, access, current_user = nil)
ProjectMember.add_users_into_projects(
ProjectMember.add_users_to_projects(
[project.id],
users,
access,
......
class ImportExportCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 1440
attr_reader :mmin, :path
def initialize(mmin = LAST_MODIFIED_TIME_IN_MINUTES)
@mmin = mmin
@path = Gitlab::ImportExport.storage_path
end
def execute
Gitlab::Metrics.measure(:import_export_clean_up) do
return unless File.directory?(path)
clean_up_export_files
end
end
private
def clean_up_export_files
Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete))
end
end
class RepositoryArchiveCleanUpService
LAST_MODIFIED_TIME_IN_MINUTES = 120
attr_reader :mmin, :path
def initialize(mmin = LAST_MODIFIED_TIME_IN_MINUTES)
@mmin = mmin
@path = Gitlab.config.gitlab.repository_downloads_path
......@@ -17,8 +19,6 @@ class RepositoryArchiveCleanUpService
private
attr_reader :mmin, :path
def clean_up_old_archives
run(%W(find #{path} -not -path #{path} -type f \( -name \*.tar -o -name \*.bz2 -o -name \*.tar.gz -o -name \*.zip \) -maxdepth 2 -mmin +#{mmin} -delete))
end
......
......@@ -228,6 +228,9 @@
= f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_artifacts_size, class: 'form-control'
.help-block
Set the maximum file size each build's artifacts can have
= link_to "(?)", help_page_path("user/admin_area/settings/continuous_integration", anchor: "maximum-artifacts-size")
- if Gitlab.config.registry.enabled
%fieldset
......@@ -385,4 +388,4 @@
.form-actions
= f.submit 'Save', class: 'btn btn-save'
\ No newline at end of file
= f.submit 'Save', class: 'btn btn-save'
......@@ -140,12 +140,10 @@
.panel-heading
This user is blocked
.panel-body
%p Blocking user has the following effects:
%p A blocked user cannot:
%ul
%li User will not be able to login
%li User will not be able to access git repositories
%li Personal projects will be left
%li Owned groups will be left
%li Log in
%li Access Git repositories
%br
= link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-info", data: { confirm: 'Are you sure?' }
- else
......
= form_tag(user_omniauth_authorize_path("crowd"), id: 'new_crowd_user' ) do
= form_tag(omniauth_authorize_path(:user, :crowd), id: 'new_crowd_user' ) do
= text_field_tag :username, nil, {class: "form-control top", placeholder: "Username", autofocus: "autofocus"}
= password_field_tag :password, nil, {class: "form-control bottom", placeholder: "Password"}
- if devise_mapping.rememberable?
......
......@@ -5,4 +5,4 @@
- providers.each do |provider|
%span.light
- has_icon = provider_has_icon?(provider)
= link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn'), "data-no-turbolink" => "true"
= link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn'), "data-no-turbolink" => "true"
......@@ -3,3 +3,5 @@ New Issue was created.
Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %>
Author: <%= @issue.author_name %>
Assignee: <%= @issue.assignee_name %>
<%= @issue.description %>
......@@ -6,3 +6,5 @@ New Merge Request <%= @merge_request.to_reference %>
Author: <%= @merge_request.author_name %>
Assignee: <%= @merge_request.assignee_name %>
<%= @merge_request.description %>
......@@ -70,7 +70,7 @@
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
Disconnect
- else
= link_to user_omniauth_authorize_path(provider), method: :post, class: 'provider-btn not-active', "data-no-turbolink" => "true" do
= link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active', "data-no-turbolink" => "true" do
Connect
%hr
- if current_user.can_change_username?
......
- if @lines.present?
- line_class = diff_view == :inline ? '' : diff_view
- if @form.unfold? && @form.since != 1 && !@form.bottom?
%tr.line_holder
= render "projects/diffs/match_line", { line: @match_line,
line_old: @form.since, line_new: @form.since, bottom: false, new_file: false }
%tr.line_holder{ class: line_class }
= diff_match_line @form.since, @form.since, text: @match_line, view: diff_view
- @lines.each_with_index do |line, index|
- line_new = index + @form.since
- line_old = line_new - @form.offset
%tr.line_holder{ id: line_old }
%td.old_line.diff-line-num{ data: { linenumber: line_old } }
= link_to raw(line_old), "##{line_old}"
%td.new_line.diff-line-num{ data: { linenumber: line_old } }
= link_to raw(line_new) , "##{line_old}"
%td.line_content.noteable_line==#{' ' * @form.indent}#{line}
- line_content = capture do
%td.line_content.noteable_line{ class: line_class }==#{' ' * @form.indent}#{line}
%tr.line_holder{ id: line_old, class: line_class }
- case diff_view
- when :inline
%td.old_line.diff-line-num{ data: { linenumber: line_old } }
%a{href: "##{line_old}", data: { linenumber: line_old }}
%td.new_line.diff-line-num{ data: { linenumber: line_new } }
%a{href: "##{line_new}", data: { linenumber: line_new }}
= line_content
- when :parallel
%td.old_line.diff-line-num{data: { linenumber: line_old }}
= link_to raw(line_old), "##{line_old}"
= line_content
%td.new_line.diff-line-num{data: { linenumber: line_new }}
= link_to raw(line_new), "##{line_new}"
= line_content
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc
%tr.line_holder{ id: @form.to }
= render "projects/diffs/match_line", { line: @match_line,
line_old: @form.to, line_new: @form.to, bottom: true, new_file: false }
%tr.line_holder{ id: @form.to, class: line_class }
= diff_match_line @form.to, @form.to, text: @match_line, view: diff_view, bottom: true
......@@ -53,7 +53,7 @@
- if pipeline.finished_at
%p.finished-at
= icon("calendar")
#{time_ago_with_tooltip(pipeline.finished_at)}
#{time_ago_with_tooltip(pipeline.finished_at, short_format: true, skip_js: true)}
%td.pipeline-actions
.controls.hidden-xs.pull-right
......
......@@ -13,7 +13,7 @@
.nothing-here-block.diff-collapsed{data: { diff_for_path: url } }
This diff is collapsed. Click to expand it.
- elsif diff_file.diff_lines.length > 0
- if diff_view == 'parallel'
- if diff_view == :parallel
= render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob
- else
= render "projects/diffs/text_file", diff_file: diff_file
......
- show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true)
- diff_files = diffs.diff_files
- if diff_view == 'parallel'
- if diff_view == :parallel
- fluid_layout true
.content-block.oneline-block.files-changed
......@@ -29,5 +29,5 @@
- next unless blob
- blob.load_all_data!(diffs.project.repository) unless blob.only_display_raw?
= render 'projects/diffs/file', i: index, project: diffs.project,
= render 'projects/diffs/file', index: index, project: diffs.project,
diff_file: diff_file, diff_commit: diff_commit, blob: blob
.diff-file.file-holder{id: "diff-#{i}", data: diff_file_html_data(project, diff_file)}
.diff-file.file-holder{id: "diff-#{index}", data: diff_file_html_data(project, diff_file)}
.file-title{id: "file-path-#{hexdigest(diff_file.file_path)}"}
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "#diff-#{i}"
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_commit, project: project, url: "#diff-#{index}"
- unless diff_file.submodule?
.file-actions.hidden-xs
......
......@@ -4,8 +4,7 @@
%tr.line_holder{ plain ? { class: type} : { class: type, id: line_code } }
- case type
- when 'match'
= render "projects/diffs/match_line", { line: line.text,
line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file }
= diff_match_line line.old_pos, line.new_pos, text: line.text
- when 'nonewline'
%td.old_line.diff-line-num
%td.new_line.diff-line-num
......
%td.old_line.diff-line-num{data: {linenumber: line_old},
class: [unfold_bottom_class(bottom), unfold_class(!new_file)]}
\...
%td.new_line.diff-line-num{data: {linenumber: line_new},
class: [unfold_bottom_class(bottom), unfold_class(!new_file)]}
\...
%td.line_content.match= line
/ Side-by-side diff view
%div.text-file.diff-wrap-lines.code.file-content.js-syntax-highlight{ data: diff_view_data }
%table
- last_line = 0
- diff_file.parallel_diff_lines.each do |line|
- left = line[:left]
- right = line[:right]
- last_line = right.new_pos if right
%tr.line_holder.parallel
- if left
- if left.meta?
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.match= left.text
= diff_match_line left.old_pos, nil, text: left.text, view: :parallel
- else
- left_line_code = diff_file.line_code(left)
- left_position = diff_file.position(left)
......@@ -21,8 +22,7 @@
- if right
- if right.meta?
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.match= left.text
= diff_match_line nil, right.new_pos, text: left.text, view: :parallel
- else
- right_line_code = diff_file.line_code(right)
- right_position = diff_file.position(right)
......@@ -37,3 +37,5 @@
- discussion_left, discussion_right = parallel_diff_discussions(left, right, diff_file)
- if discussion_left || discussion_right
= render "discussions/parallel_diff_discussion", discussion_left: discussion_left, discussion_right: discussion_right
- if !diff_file.new_file && last_line > 0
= diff_match_line last_line, last_line, bottom: true, view: :parallel
......@@ -15,6 +15,5 @@
- if discussion
= render "discussions/diff_discussion", discussion: discussion
- if last_line > 0
= render "projects/diffs/match_line", { line: "",
line_old: last_line, line_new: last_line, bottom: true, new_file: diff_file.new_file }
- if !diff_file.new_file && last_line > 0
= diff_match_line last_line, last_line, bottom: true
......@@ -2,7 +2,7 @@
- page_description @merge_request.description
- page_card_attributes @merge_request.card_attributes
- if diff_view == 'parallel'
- if diff_view == :parallel
- fluid_layout true
.merge-request{'data-url' => merge_request_path(@merge_request)}
......
class GitlabRemoveProjectExportWorker
class ImportExportProjectCleanupWorker
include Sidekiq::Worker
sidekiq_options queue: :default
def perform
Project.remove_gitlab_exports!
ImportExportCleanUpService.new.execute
end
end
......@@ -287,9 +287,9 @@ Settings.cron_jobs['admin_email_worker']['job_class'] = 'AdminEmailWorker'
Settings.cron_jobs['repository_archive_cache_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['repository_archive_cache_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['repository_archive_cache_worker']['job_class'] = 'RepositoryArchiveCacheWorker'
Settings.cron_jobs['gitlab_remove_project_export_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['gitlab_remove_project_export_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['gitlab_remove_project_export_worker']['job_class'] = 'GitlabRemoveProjectExportWorker'
Settings.cron_jobs['import_export_project_cleanup_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['import_export_project_cleanup_worker']['cron'] ||= '0 * * * *'
Settings.cron_jobs['import_export_project_cleanup_worker']['job_class'] = 'ImportExportProjectCleanupWorker'
Settings.cron_jobs['requests_profiles_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['requests_profiles_worker']['cron'] ||= '0 0 * * *'
Settings.cron_jobs['requests_profiles_worker']['job_class'] = 'RequestsProfilesWorker'
......
......@@ -145,6 +145,9 @@ if Gitlab::Metrics.enabled?
config.instrument_methods(Rinku)
config.instrument_instance_methods(Repository)
config.instrument_methods(Gitlab::Highlight)
config.instrument_instance_methods(Gitlab::Highlight)
end
GC::Profiler.enable
......
class Gitlab::Seeder::Builds
STAGES = %w[build notify_build test notify_test deploy notify_deploy]
def initialize(project)
@project = project
end
......@@ -8,26 +8,26 @@ class Gitlab::Seeder::Builds
def seed!
pipelines.each do |pipeline|
begin
build_create!(pipeline, name: 'build:linux', stage: 'build')
build_create!(pipeline, name: 'build:osx', stage: 'build')
build_create!(pipeline, name: 'build:linux', stage: 'build', status_event: :success)
build_create!(pipeline, name: 'build:osx', stage: 'build', status_event: :success)
build_create!(pipeline, name: 'slack post build', stage: 'notify_build')
build_create!(pipeline, name: 'slack post build', stage: 'notify_build', status_event: :success)
build_create!(pipeline, name: 'rspec:linux', stage: 'test')
build_create!(pipeline, name: 'rspec:windows', stage: 'test')
build_create!(pipeline, name: 'rspec:windows', stage: 'test')
build_create!(pipeline, name: 'rspec:osx', stage: 'test')
build_create!(pipeline, name: 'spinach:linux', stage: 'test')
build_create!(pipeline, name: 'spinach:osx', stage: 'test')
build_create!(pipeline, name: 'cucumber:linux', stage: 'test')
build_create!(pipeline, name: 'cucumber:osx', stage: 'test')
build_create!(pipeline, name: 'rspec:linux', stage: 'test', status_event: :success)
build_create!(pipeline, name: 'rspec:windows', stage: 'test', status_event: :success)
build_create!(pipeline, name: 'rspec:windows', stage: 'test', status_event: :success)
build_create!(pipeline, name: 'rspec:osx', stage: 'test', status_event: :success)
build_create!(pipeline, name: 'spinach:linux', stage: 'test', status: :pending)
build_create!(pipeline, name: 'spinach:osx', stage: 'test', status_event: :cancel)
build_create!(pipeline, name: 'cucumber:linux', stage: 'test', status_event: :run)
build_create!(pipeline, name: 'cucumber:osx', stage: 'test', status_event: :drop)
build_create!(pipeline, name: 'slack post test', stage: 'notify_test')
build_create!(pipeline, name: 'slack post test', stage: 'notify_test', status_event: :success)
build_create!(pipeline, name: 'staging', stage: 'deploy', environment: 'staging')
build_create!(pipeline, name: 'production', stage: 'deploy', environment: 'production', when: 'manual')
build_create!(pipeline, name: 'staging', stage: 'deploy', environment: 'staging', status_event: :success)
build_create!(pipeline, name: 'production', stage: 'deploy', environment: 'production', when: 'manual', status: :success)
commit_status_create!(pipeline, name: 'jenkins')
commit_status_create!(pipeline, name: 'jenkins', status: :success)
print '.'
rescue ActiveRecord::RecordInvalid
......@@ -48,7 +48,7 @@ class Gitlab::Seeder::Builds
def build_create!(pipeline, opts = {})
attributes = build_attributes_for(pipeline, opts)
build = Ci::Build.new(attributes)
build = Ci::Build.create!(attributes)
if opts[:name].start_with?('build')
artifacts_cache_file(artifacts_archive_path) do |file|
......@@ -60,23 +60,20 @@ class Gitlab::Seeder::Builds
end
end
build.save!
build.update(status: build_status)
if %w(running success failed).include?(build.status)
# We need to set build trace after saving a build (id required)
build.trace = FFaker::Lorem.paragraphs(6).join("\n\n")
end
end
def commit_status_create!(pipeline, opts = {})
attributes = commit_status_attributes_for(pipeline, opts)
GenericCommitStatus.create(attributes)
GenericCommitStatus.create!(attributes)
end
def commit_status_attributes_for(pipeline, opts)
{ name: 'test build', stage: 'test', stage_idx: stage_index(opts[:stage]),
ref: 'master', user: build_user, project: @project, pipeline: pipeline,
ref: 'master', tag: false, user: build_user, project: @project, pipeline: pipeline,
created_at: Time.now, updated_at: Time.now
}.merge(opts)
end
......
......@@ -14,7 +14,7 @@ class MoveFromDevelopersCanMergeToProtectedBranchesMergeAccess < ActiveRecord::M
def up
execute <<-HEREDOC
INSERT into protected_branch_merge_access_levels (protected_branch_id, access_level, created_at, updated_at)
SELECT id, (CASE WHEN developers_can_merge THEN 1 ELSE 0 END), now(), now()
SELECT id, (CASE WHEN developers_can_merge THEN 30 ELSE 40 END), now(), now()
FROM protected_branches
HEREDOC
end
......@@ -23,7 +23,7 @@ class MoveFromDevelopersCanMergeToProtectedBranchesMergeAccess < ActiveRecord::M
execute <<-HEREDOC
UPDATE protected_branches SET developers_can_merge = TRUE
WHERE id IN (SELECT protected_branch_id FROM protected_branch_merge_access_levels
WHERE access_level = 1);
WHERE access_level = 30);
HEREDOC
end
end
......@@ -14,7 +14,7 @@ class MoveFromDevelopersCanPushToProtectedBranchesPushAccess < ActiveRecord::Mig
def up
execute <<-HEREDOC
INSERT into protected_branch_push_access_levels (protected_branch_id, access_level, created_at, updated_at)
SELECT id, (CASE WHEN developers_can_push THEN 1 ELSE 0 END), now(), now()
SELECT id, (CASE WHEN developers_can_push THEN 30 ELSE 40 END), now(), now()
FROM protected_branches
HEREDOC
end
......@@ -23,7 +23,7 @@ class MoveFromDevelopersCanPushToProtectedBranchesPushAccess < ActiveRecord::Mig
execute <<-HEREDOC
UPDATE protected_branches SET developers_can_push = TRUE
WHERE id IN (SELECT protected_branch_id FROM protected_branch_push_access_levels
WHERE access_level = 1);
WHERE access_level = 30);
HEREDOC
end
end
......@@ -14,6 +14,6 @@ class RemoveDevelopersCanPushFromProtectedBranches < ActiveRecord::Migration
end
def down
add_column_with_default(:protected_branches, :developers_can_push, :boolean, default: false, null: false)
add_column_with_default(:protected_branches, :developers_can_push, :boolean, default: false, allow_null: false)
end
end
......@@ -14,6 +14,6 @@ class RemoveDevelopersCanMergeFromProtectedBranches < ActiveRecord::Migration
end
def down
add_column_with_default(:protected_branches, :developers_can_merge, :boolean, default: false, null: false)
add_column_with_default(:protected_branches, :developers_can_merge, :boolean, default: false, allow_null: false)
end
end
class RemoveBuildsEnableIndexOnProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
remove_index :projects, column: :builds_enabled if index_exists?(:projects, :builds_enabled)
end
end
# rubocop:disable all
# 20141121133009_add_timestamps_to_members.rb was meant to ensure that all
# rows in the members table had created_at and updated_at set, following an
# error in a previous migration. This failed to set all rows in at least one
# case: https://gitlab.com/gitlab-org/gitlab-ce/issues/20568
#
# Why this happened is lost in the mists of time, so repeat the SQL query
# without speculation, just in case more than one person was affected.
class AddTimestampsToMembersAgain < ActiveRecord::Migration
DOWNTIME = false
def up
execute "UPDATE members SET created_at = NOW() WHERE created_at IS NULL"
execute "UPDATE members SET updated_at = NOW() WHERE updated_at IS NULL"
end
def down
# no change
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160726093600) do
ActiveRecord::Schema.define(version: 20160804150737) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -852,7 +852,6 @@ ActiveRecord::Schema.define(version: 20160726093600) do
end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
add_index "projects", ["builds_enabled"], name: "index_projects_on_builds_enabled", using: :btree
add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
......
......@@ -54,7 +54,5 @@
## Contributor documentation
- [Documentation styleguide](development/doc_styleguide.md) Use this styleguide if you are
contributing to documentation.
- [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
- [Development](development/README.md) All styleguides and explanations how to contribute.
- [Legal](legal/README.md) Contributor license agreements.
# Build artifacts administration
>**Notes:**
>- Introduced in GitLab 8.2 and GitLab Runner 0.7.0.
>- Starting from GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format
changed to `ZIP`.
>- This is the administration documentation. For the user guide see
[user/project/builds/artifacts.md](../user/project/builds/artifacts.md).
Artifacts is a list of files and directories which are attached to a build
after it completes successfully. This feature is enabled by default in all
GitLab installations. Keep reading if you want to know how to disable it.
## Disabling build artifacts
To disable artifacts site-wide, follow the steps below.
---
**In Omnibus installations:**
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
```ruby
gitlab_rails['artifacts_enabled'] = false
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**In installations from source:**
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
```yaml
artifacts:
enabled: false
```
1. Save the file and [restart GitLab][] for the changes to take effect.
## Storing build artifacts
After a successful build, GitLab Runner uploads an archive containing the build
artifacts to GitLab.
To change the location where the artifacts are stored, follow the steps below.
---
**In Omnibus installations:**
_The artifacts are stored by default in
`/var/opt/gitlab/gitlab-rails/shared/artifacts`._
1. To change the storage path for example to `/mnt/storage/artifacts`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
```ruby
gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts"
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**In installations from source:**
_The artifacts are stored by default in
`/home/git/gitlab/shared/artifacts`._
1. To change the storage path for example to `/mnt/storage/artifacts`, edit
`/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
```yaml
artifacts:
enabled: true
path: /mnt/storage/artifacts
```
1. Save the file and [restart GitLab][] for the changes to take effect.
## Set the maximum file size of the artifacts
Provided the artifacts are enabled, you can change the maximum file size of the
artifacts through the [Admin area settings](../user/admin_area/settings/continuous_integration#maximum-artifacts-size).
[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab"
[restart gitlab]: restart_gitlab.md "How to restart GitLab"
......@@ -14,7 +14,7 @@
- [Use variables in your `.gitlab-ci.yml`](variables/README.md)
- [Use SSH keys in your build environment](ssh_keys/README.md)
- [Trigger builds through the API](triggers/README.md)
- [Build artifacts](build_artifacts/README.md)
- [Build artifacts](../user/project/builds/artifacts.md)
- [User permissions](../user/permissions.md#gitlab-ci)
- [API](../api/ci/README.md)
- [CI services (linked docker containers)](services/README.md)
# Introduction to build artifacts
This document was moved to:
Artifacts is a list of files and directories which are attached to a build
after it completes successfully. This feature is enabled by default in all GitLab installations.
_If you are searching for ways to use artifacts, jump to
[Defining artifacts in `.gitlab-ci.yml`](#defining-artifacts-in-gitlab-ciyml)._
Since GitLab 8.2 and [GitLab Runner] 0.7.0, build artifacts that are created by
GitLab Runner are uploaded to GitLab and are downloadable as a single archive
(`tar.gz`) using the GitLab UI.
Starting from GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format
changed to `ZIP`, and it is now possible to browse its contents, with the added
ability of downloading the files separately.
**Note:**
The artifacts browser will be available only for new artifacts that are sent
to GitLab using GitLab Runner version 1.0 and up. It will not be possible to
browse old artifacts already uploaded to GitLab.
## Disabling build artifacts
To disable artifacts site-wide, follow the steps below.
---
**In Omnibus installations:**
1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
```ruby
gitlab_rails['artifacts_enabled'] = false
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**In installations from source:**
1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
```yaml
artifacts:
enabled: false
```
1. Save the file and [restart GitLab][] for the changes to take effect.
## Defining artifacts in `.gitlab-ci.yml`
A simple example of using the artifacts definition in `.gitlab-ci.yml` would be
the following:
```yaml
pdf:
script: xelatex mycv.tex
artifacts:
paths:
- mycv.pdf
```
A job named `pdf` calls the `xelatex` command in order to build a pdf file from
the latex source file `mycv.tex`. We then define the `artifacts` paths which in
turn are defined with the `paths` keyword. All paths to files and directories
are relative to the repository that was cloned during the build.
For more examples on artifacts, follow the
[separate artifacts yaml documentation](../yaml/README.md#artifacts).
## Storing build artifacts
After a successful build, GitLab Runner uploads an archive containing the build
artifacts to GitLab.
To change the location where the artifacts are stored, follow the steps below.
---
**In Omnibus installations:**
_The artifacts are stored by default in
`/var/opt/gitlab/gitlab-rails/shared/artifacts`._
1. To change the storage path for example to `/mnt/storage/artifacts`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
```ruby
gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts"
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
---
**In installations from source:**
_The artifacts are stored by default in
`/home/git/gitlab/shared/artifacts`._
1. To change the storage path for example to `/mnt/storage/artifacts`, edit
`/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
```yaml
artifacts:
enabled: true
path: /mnt/storage/artifacts
```
1. Save the file and [restart GitLab][] for the changes to take effect.
## Browsing build artifacts
When GitLab receives an artifacts archive, an archive metadata file is also
generated. This metadata file describes all the entries that are located in the
artifacts archive itself. The metadata file is in a binary format, with
additional GZIP compression.
GitLab does not extract the artifacts archive in order to save space, memory
and disk I/O. It instead inspects the metadata file which contains all the
relevant information. This is especially important when there is a lot of
artifacts, or an archive is a very large file.
---
After a successful build, if you visit the build's specific page, you can see
that there are two buttons.
One is for downloading the artifacts archive and the other for browsing its
contents.
![Build artifacts browser button](img/build_artifacts_browser_button.png)
---
The archive browser shows the name and the actual file size of each file in the
archive. If your artifacts contained directories, then you are also able to
browse inside them.
Below you can see an image of three different file formats, as well as two
directories.
![Build artifacts browser](img/build_artifacts_browser.png)
---
## Downloading build artifacts
If you need to download the whole archive, there are buttons in various places
inside GitLab that make that possible.
1. While on the builds page, you can see the download icon for each build's
artifacts archive in the right corner
1. While inside a specific build, you are presented with a download button
along with the one that browses the archive
1. And finally, when browsing an archive you can see the download button at
the top right corner
---
Note that GitLab does not extract the entire artifacts archive to send just a
single file to the user.
When clicking on a specific file, [GitLab Workhorse] extracts it from the
archive and the download begins.
This implementation saves space, memory and disk I/O.
[gitlab runner]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner "GitLab Runner repository"
[reconfigure gitlab]: ../../administration/restart_gitlab.md "How to restart GitLab documentation"
[restart gitlab]: ../../administration/restart_gitlab.md "How to restart GitLab documentation"
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
- [user/project/builds/artifacts.md](../../user/project/builds/artifacts.md) - user guide
- [administration/build_artifacts.md](../../administration/build_artifacts.md) - administrator guide
# Development
## Outside of docs
- [CONTRIBUTING.md](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) main contributing guide
- [PROCESS.md](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/PROCESS.md) contributing process
- [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit) to install a development version
## Styleguides
- [Documentation styleguide](development/doc_styleguide.md) Use this styleguide if you are
contributing to documentation.
- [Migration Style Guide](migration_style_guide.md) for creating safe migrations
- [Testing standards and style guidelines](testing.md)
- [UI guide](ui_guide.md) for building GitLab with existing css styles and elements
- [SQL guidelines](sql.md) for SQL guidelines
## Process
- [Code review guidelines](code_review.md) for reviewing code and having code reviewed.
## Backend howtos
- [Architecture](architecture.md) of GitLab
- [CI setup](ci_setup.md) for testing GitLab
- [Code review guidelines](code_review.md) for reviewing code and having code
reviewed.
- [Gotchas](gotchas.md) to avoid
- [How to dump production data to staging](db_dump.md)
- [Instrumentation](instrumentation.md)
- [Licensing](licensing.md) for ensuring license compliance
- [Migration Style Guide](migration_style_guide.md) for creating safe migrations
- [Performance guidelines](performance.md)
- [Rake tasks](rake_tasks.md) for development
- [Shell commands](shell_commands.md) in the GitLab codebase
- [Sidekiq debugging](sidekiq_debugging.md)
- [SQL guidelines](sql.md) for SQL guidelines
- [Testing standards and style guidelines](testing.md)
- [UI guide](ui_guide.md) for building GitLab with existing css styles and elements
- [What requires downtime?](what_requires_downtime.md)
## Compliance
- [Licensing](licensing.md) for ensuring license compliance
......@@ -41,10 +41,10 @@ Rubocop](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/.rubocop.yml#L9
[Exception]: http://stackoverflow.com/q/10048173/223897
## Don't use inline CoffeeScript in views
## Don't use inline CoffeeScript/JavaScript in views
Using the inline `:coffee` or `:coffeescript` Haml filters comes with a
performance overhead.
performance overhead. Using inline JavaScript is not a good way to structure your code and should be avoided.
_**Note:** We've [removed these two filters](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/hamlit.rb)
in an initializer._
......@@ -52,6 +52,7 @@ in an initializer._
### Further reading
- Pull Request: [Replace CoffeeScript block into JavaScript in Views](https://git.io/vztMu)
- Stack Overflow: [Why you should not write inline JavaScript](http://programmers.stackexchange.com/questions/86589/why-should-i-avoid-inline-scripting)
- Stack Overflow: [Performance implications of using :coffescript filter inside HAML templates?](http://stackoverflow.com/a/17571242/223897)
## ID-based CSS selectors need to be a bit more specific
......
# What requires downtime?
When working with a database certain operations can be performed without taking
GitLab offline, others do require a downtime period. This guide describes
various operations and their impact.
## Adding Columns
On PostgreSQL you can safely add a new column to an existing table as long as it
does **not** have a default value. For example, this query would not require
downtime:
```sql
ALTER TABLE projects ADD COLUMN random_value int;
```
Add a column _with_ a default however does require downtime. For example,
consider this query:
```sql
ALTER TABLE projects ADD COLUMN random_value int DEFAULT 42;
```
This requires updating every single row in the `projects` table so that
`random_value` is set to `42` by default. This requires updating all rows and
indexes in a table. This in turn acquires enough locks on the table for it to
effectively block any other queries.
As of MySQL 5.6 adding a column to a table is still quite an expensive
operation, even when using `ALGORITHM=INPLACE` and `LOCK=NONE`. This means
downtime _may_ be required when modifying large tables as otherwise the
operation could potentially take hours to complete.
## Dropping Columns
On PostgreSQL you can safely remove an existing column without the need for
downtime. When you drop a column in PostgreSQL it's not immediately removed,
instead it is simply disabled. The data is removed on the next vacuum run.
On MySQL this operation requires downtime.
While database wise dropping a column may be fine on PostgreSQL this operation
still requires downtime because the application code may still be using the
column that was removed. For example, consider the following migration:
```ruby
class MyMigration < ActiveRecord::Migration
def change
remove_column :projects, :dummy
end
end
```
Now imagine that the GitLab instance is running and actively uses the `dummy`
column. If we were to run the migration this would result in the GitLab instance
producing errors whenever it tries to use the `dummy` column.
As a result of the above downtime _is_ required when removing a column, even
when using PostgreSQL.
## Changing Column Constraints
Generally changing column constraints requires checking all rows in the table to
see if they meet the new constraint, unless a constraint is _removed_. For
example, changing a column that previously allowed NULL values to not allow NULL
values requires the database to verify all existing rows.
The specific behaviour varies a bit between databases but in general the safest
approach is to assume changing constraints requires downtime.
## Changing Column Types
This operation requires downtime.
## Adding Indexes
Adding indexes is an expensive process that blocks INSERT and UPDATE queries for
the duration. When using PostgreSQL one can work arounds this by using the
`CONCURRENTLY` option:
```sql
CREATE INDEX CONCURRENTLY index_name ON projects (column_name);
```
Migrations can take advantage of this by using the method
`add_concurrent_index`. For example:
```ruby
class MyMigration < ActiveRecord::Migration
def change
add_concurrent_index :projects, :column_name
end
end
```
When running this on PostgreSQL the `CONCURRENTLY` option mentioned above is
used. On MySQL this method produces a regular `CREATE INDEX` query.
MySQL doesn't really have a workaround for this. Supposedly it _can_ create
indexes without the need for downtime but only for variable width columns. The
details on this are a bit sketchy. Since it's better to be safe than sorry one
should assume that adding indexes requires downtime on MySQL.
## Dropping Indexes
Dropping an index does not require downtime on both PostgreSQL and MySQL.
## Adding Tables
This operation is safe as there's no code using the table just yet.
## Dropping Tables
This operation requires downtime as application code may still be using the
table.
## Adding Foreign Keys
Adding foreign keys acquires an exclusive lock on both the source and target
tables in PostgreSQL. This requires downtime as otherwise the entire application
grinds to a halt for the duration of the operation.
On MySQL this operation also requires downtime _unless_ foreign key checks are
disabled. Because this means checks aren't enforced this is not ideal, as such
one should assume MySQL also requires downtime.
## Removing Foreign Keys
This operation should not require downtime on both PostgreSQL and MySQL.
## Updating Data
Updating data should generally be safe. The exception to this is data that's
being migrated from one version to another while the application still produces
data in the old version.
For example, imagine the application writes the string `'dog'` to a column but
it really is meant to write `'cat'` instead. One might think that the following
migration is all that is needed to solve this problem:
```ruby
class MyMigration < ActiveRecord::Migration
def up
execute("UPDATE some_table SET column = 'cat' WHERE column = 'dog';")
end
end
```
Unfortunately this is not enough. Because the application is still running and
using the old value this may result in the table still containing rows where
`column` is set to `dog`, even after the migration finished.
In these cases downtime _is_ required, even for rarely updated tables.
# Continuous integration Admin settings
## Maximum artifacts size
The maximum size of the [build artifacts][art-yml] can be set in the Admin area
of your GitLab instance. The value is in MB and the default is 100MB. Note that
this setting is set for each build.
1. Go to **Admin area > Settings** (`/admin/application_settings`).
![Admin area settings button](img/admin_area_settings_button.png)
1. Change the value of the maximum artifacts size (in MB):
![Admin area maximum artifacts size](img/admin_area_maximum_artifacts_size.png)
1. Hit **Save** for the changes to take effect.
[art-yml]: ../../../administration/build_artifacts.md
# Introduction to build artifacts
>**Notes:**
>- Since GitLab 8.2 and GitLab Runner 0.7.0, build artifacts that are created by
GitLab Runner are uploaded to GitLab and are downloadable as a single archive
(`tar.gz`) using the GitLab UI.
>- Starting from GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format
changed to `ZIP`, and it is now possible to browse its contents, with the added
ability of downloading the files separately.
>- The artifacts browser will be available only for new artifacts that are sent
to GitLab using GitLab Runner version 1.0 and up. It will not be possible to
browse old artifacts already uploaded to GitLab.
>- This is the user documentation. For the administration guide see
[administration/build_artifacts.md](../../../administration/build_artifacts.md).
Artifacts is a list of files and directories which are attached to a build
after it completes successfully. This feature is enabled by default in all GitLab installations.
## Defining artifacts in `.gitlab-ci.yml`
A simple example of using the artifacts definition in `.gitlab-ci.yml` would be
the following:
```yaml
pdf:
script: xelatex mycv.tex
artifacts:
paths:
- mycv.pdf
```
A job named `pdf` calls the `xelatex` command in order to build a pdf file from
the latex source file `mycv.tex`. We then define the `artifacts` paths which in
turn are defined with the `paths` keyword. All paths to files and directories
are relative to the repository that was cloned during the build.
For more examples on artifacts, follow the artifacts reference in
[`.gitlab-ci.yml` documentation](../../../ci/yaml/README.md#artifacts).
## Browsing build artifacts
When GitLab receives an artifacts archive, an archive metadata file is also
generated. This metadata file describes all the entries that are located in the
artifacts archive itself. The metadata file is in a binary format, with
additional GZIP compression.
GitLab does not extract the artifacts archive in order to save space, memory
and disk I/O. It instead inspects the metadata file which contains all the
relevant information. This is especially important when there is a lot of
artifacts, or an archive is a very large file.
---
After a build finishes, if you visit the build's specific page, you can see
that there are two buttons. One is for downloading the artifacts archive and
the other for browsing its contents.
![Build artifacts browser button](img/build_artifacts_browser_button.png)
---
The archive browser shows the name and the actual file size of each file in the
archive. If your artifacts contained directories, then you are also able to
browse inside them.
Below you can see how browsing looks like. In this case we have browsed inside
the archive and at this point there is one directory and one HTML file.
![Build artifacts browser](img/build_artifacts_browser.png)
---
## Downloading build artifacts
>**Note:**
GitLab does not extract the entire artifacts archive to send just a single file
to the user. When clicking on a specific file, [GitLab Workhorse] extracts it
from the archive and the download begins. This implementation saves space,
memory and disk I/O.
If you need to download the whole archive, there are buttons in various places
inside GitLab that make that possible.
1. While on the pipelines page, you can see the download icon for each build's
artifacts archive in the right corner:
![Build artifacts in Pipelines page](img/build_artifacts_pipelines_page.png)
1. While on the builds page, you can see the download icon for each build's
artifacts archive in the right corner:
![Build artifacts in Builds page](img/build_artifacts_builds_page.png)
1. While inside a specific build, you are presented with a download button
along with the one that browses the archive:
![Build artifacts browser button](img/build_artifacts_browser_button.png)
1. And finally, when browsing an archive you can see the download button at
the top right corner:
![Build artifacts browser](img/build_artifacts_browser.png)
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
......@@ -236,6 +236,15 @@ Feature: Project Merge Requests
And I unfold diff
Then I should see additional file lines
@javascript
Scenario: I unfold diff in Side-by-Side view
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
And I visit merge request page "Bug NS-05"
And I click on the Changes tab
And I click Side-by-side Diff tab
And I unfold diff
Then I should see additional file lines
@javascript
Scenario: I show comments on a merge request side-by-side diff with comments in multiple files
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
......
......@@ -477,6 +477,9 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step 'I click Side-by-side Diff tab' do
find('a', text: 'Side-by-side').trigger('click')
# Waits for load
expect(page).to have_css('.parallel')
end
step 'I should see comments on the side-by-side diff page' do
......
......@@ -31,6 +31,14 @@ module Banzai
# Text matching LINK_PATTERN inside these elements will not be linked
IGNORE_PARENTS = %w(a code kbd pre script style).to_set
# The XPath query to use for finding text nodes to parse.
TEXT_QUERY = %Q(descendant-or-self::text()[
not(#{IGNORE_PARENTS.map { |p| "ancestor::#{p}" }.join(' or ')})
and contains(., '://')
and not(starts-with(., 'http'))
and not(starts-with(., 'ftp'))
])
def call
return doc if context[:autolink] == false
......@@ -66,16 +74,11 @@ module Banzai
# Autolinks any text matching LINK_PATTERN that Rinku didn't already
# replace
def text_parse
search_text_nodes(doc).each do |node|
doc.xpath(TEXT_QUERY).each do |node|
content = node.to_html
next if has_ancestor?(node, IGNORE_PARENTS)
next unless content.match(LINK_PATTERN)
# If Rinku didn't link this, there's probably a good reason, so we'll
# skip it too
next if content.start_with?(*%w(http https ftp))
html = autolink_filter(content)
next if html == content
......
......@@ -35,6 +35,7 @@ module Banzai
def process_link_attr(html_attr)
return if html_attr.blank?
return if html_attr.value.start_with?('//')
uri = URI(html_attr.value)
if uri.relative? && uri.path.present?
......@@ -92,7 +93,7 @@ module Banzai
parts = request_path.split('/')
parts.pop if uri_type(request_path) != :tree
path.sub!(%r{^\./}, '')
path.sub!(%r{\A\./}, '')
while path.start_with?('../')
parts.pop
......
......@@ -17,15 +17,12 @@ module Banzai
def highlight_node(node)
language = node.attr('class')
code = node.text
code = node.text
css_classes = "code highlight"
lexer = Rouge::Lexer.find_fancy(language) || Rouge::Lexers::PlainText
formatter = Rouge::Formatters::HTML.new
lexer = lexer_for(language)
begin
code = formatter.format(lexer.lex(code))
code = format(lex(lexer, code))
css_classes << " js-syntax-highlight #{lexer.tag}"
rescue
......@@ -41,14 +38,27 @@ module Banzai
private
# Separate method so it can be instrumented.
def lex(lexer, code)
lexer.lex(code)
end
def format(tokens)
rouge_formatter.format(tokens)
end
def lexer_for(language)
(Rouge::Lexer.find(language) || Rouge::Lexers::PlainText).new
end
def replace_parent_pre_element(node, highlighted)
# Replace the parent `pre` element with the entire highlighted block
node.parent.replace(highlighted)
end
# Override Rouge::Plugins::Redcarpet#rouge_formatter
def rouge_formatter(lexer)
Rouge::Formatters::HTML.new
def rouge_formatter(lexer = nil)
@rouge_formatter ||= Rouge::Formatters::HTML.new
end
end
end
......
......@@ -22,7 +22,9 @@ module Gitlab
@extractor.analyze(closing_statements.join(" "))
@extractor.issues
@extractor.issues.reject do |issue|
@extractor.project.forked_from?(issue.project) # Don't extract issues on original project
end
end
end
end
......@@ -13,6 +13,10 @@ module Gitlab
File.join(Settings.shared['path'], 'tmp/project_exports')
end
def import_upload_path(filename:)
File.join(storage_path, 'uploads', filename)
end
def project_filename
"project.json"
end
......
......@@ -25,7 +25,7 @@ module Gitlab
def verify_version!(version)
if Gem::Version.new(version) > Gem::Version.new(Gitlab::ImportExport.version)
raise Gitlab::ImportExport::Error("Import version mismatch: Required <= #{Gitlab::ImportExport.version} but was #{version}")
raise Gitlab::ImportExport::Error.new("Import version mismatch: Required <= #{Gitlab::ImportExport.version} but was #{version}")
else
true
end
......
......@@ -4,13 +4,13 @@ namespace :gitlab do
task all_users_to_all_projects: :environment do |t, args|
user_ids = User.where(admin: false).pluck(:id)
admin_ids = User.where(admin: true).pluck(:id)
projects_ids = Project.pluck(:id)
project_ids = Project.pluck(:id)
puts "Importing #{user_ids.size} users into #{projects_ids.size} projects"
ProjectMember.add_users_into_projects(projects_ids, user_ids, ProjectMember::DEVELOPER)
puts "Importing #{user_ids.size} users into #{project_ids.size} projects"
ProjectMember.add_users_to_projects(project_ids, user_ids, ProjectMember::DEVELOPER)
puts "Importing #{admin_ids.size} admins into #{projects_ids.size} projects"
ProjectMember.add_users_into_projects(projects_ids, admin_ids, ProjectMember::MASTER)
puts "Importing #{admin_ids.size} admins into #{project_ids.size} projects"
ProjectMember.add_users_to_projects(project_ids, admin_ids, ProjectMember::MASTER)
end
desc "GitLab | Add a specific user to all projects (as a developer)"
......@@ -18,7 +18,7 @@ namespace :gitlab do
user = User.find_by(email: args.email)
project_ids = Project.pluck(:id)
puts "Importing #{user.email} users into #{project_ids.size} projects"
ProjectMember.add_users_into_projects(project_ids, Array.wrap(user.id), ProjectMember::DEVELOPER)
ProjectMember.add_users_to_projects(project_ids, Array.wrap(user.id), ProjectMember::DEVELOPER)
end
desc "GitLab | Add all users to all groups (admin users are added as owners)"
......
......@@ -26,10 +26,10 @@ namespace :gitlab do
namespace_path = ENV['NAMESPACE']
projects = find_projects(namespace_path)
projects_ids = projects.pluck(:id)
project_ids = projects.pluck(:id)
puts "Removing webhooks with the url '#{web_hook_url}' ... "
count = WebHook.where(url: web_hook_url, project_id: projects_ids, type: 'ProjectHook').delete_all
count = WebHook.where(url: web_hook_url, project_id: project_ids, type: 'ProjectHook').delete_all
puts "#{count} webhooks were removed."
end
......
......@@ -128,7 +128,7 @@ feature 'Login', feature: true do
end
allow(Gitlab::OAuth::Provider).to receive_messages(providers: [:saml], config_for: saml_config)
allow(Gitlab.config.omniauth).to receive_messages(messages)
allow_any_instance_of(Object).to receive(:user_omniauth_authorize_path).with('saml').and_return('/users/auth/saml')
expect_any_instance_of(Object).to receive(:omniauth_authorize_path).with(:user, "saml").and_return('/users/auth/saml')
end
it 'should show 2FA prompt after OAuth login' do
......
require 'rails_helper'
feature 'Ref switcher', feature: true, js: true do
include WaitForAjax
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
before do
project.team << [user, :master]
login_as(user)
visit namespace_project_tree_path(project.namespace, project, 'master')
end
it 'allow user to change ref by enter key' do
click_button 'master'
wait_for_ajax
page.within '.project-refs-form' do
input = find('input[type="search"]')
input.set 'expand'
input.native.send_keys :down
input.native.send_keys :down
input.native.send_keys :enter
expect(page).to have_content 'expand-collapse-files'
end
end
end
......@@ -218,12 +218,12 @@ describe ApplicationHelper do
end
it 'includes a default js-timeago class' do
expect(element.attr('class')).to eq 'time_ago js-timeago js-timeago-pending'
expect(element.attr('class')).to eq 'js-timeago js-timeago-pending'
end
it 'accepts a custom html_class' do
expect(element(html_class: 'custom_class').attr('class')).
to eq 'custom_class js-timeago js-timeago-pending'
to eq 'js-timeago custom_class js-timeago-pending'
end
it 'accepts a custom tooltip placement' do
......@@ -244,6 +244,19 @@ describe ApplicationHelper do
it 'converts to Time' do
expect { helper.time_ago_with_tooltip(Date.today) }.not_to raise_error
end
it 'add class for the short format and includes inline script' do
timeago_element = element(short_format: 'short')
expect(timeago_element.attr('class')).to eq 'js-short-timeago js-timeago-pending'
script_element = timeago_element.next_element
expect(script_element.name).to eq 'script'
end
it 'add class for the short format and does not include inline script' do
timeago_element = element(short_format: 'short', skip_js: true)
expect(timeago_element.attr('class')).to eq 'js-short-timeago'
expect(timeago_element.next_element).to eq nil
end
end
describe 'render_markup' do
......
......@@ -15,22 +15,22 @@ describe DiffHelper do
it 'returns a valid value when cookie is set' do
helper.request.cookies[:diff_view] = 'parallel'
expect(helper.diff_view).to eq 'parallel'
expect(helper.diff_view).to eq :parallel
end
it 'returns a default value when cookie is invalid' do
helper.request.cookies[:diff_view] = 'invalid'
expect(helper.diff_view).to eq 'inline'
expect(helper.diff_view).to eq :inline
end
it 'returns a default value when cookie is nil' do
expect(helper.request.cookies).to be_empty
expect(helper.diff_view).to eq 'inline'
expect(helper.diff_view).to eq :inline
end
end
describe 'diff_options' do
it 'should return no collapse false' do
expect(diff_options).to include(no_collapse: false)
......@@ -59,26 +59,6 @@ describe DiffHelper do
end
end
describe 'unfold_bottom_class' do
it 'should return empty string when bottom line shouldnt be unfolded' do
expect(unfold_bottom_class(false)).to eq('')
end
it 'should return js class when bottom lines should be unfolded' do
expect(unfold_bottom_class(true)).to include('js-unfold-bottom')
end
end
describe 'unfold_class' do
it 'returns empty on false' do
expect(unfold_class(false)).to eq('')
end
it 'returns a class on true' do
expect(unfold_class(true)).to eq('unfold js-unfold')
end
end
describe '#diff_line_content' do
it 'should return non breaking space when line is empty' do
expect(diff_line_content(nil)).to eq(' &nbsp;')
......@@ -105,4 +85,56 @@ describe DiffHelper do
expect(marked_new_line).to be_html_safe
end
end
describe "#diff_match_line" do
let(:old_pos) { 40 }
let(:new_pos) { 50 }
let(:text) { 'some_text' }
it "should generate foldable top match line for inline view with empty text by default" do
output = diff_match_line old_pos, new_pos
expect(output).to be_html_safe
expect(output).to have_css "td:nth-child(1):not(.js-unfold-bottom).diff-line-num.unfold.js-unfold.old_line[data-linenumber='#{old_pos}']", text: '...'
expect(output).to have_css "td:nth-child(2):not(.js-unfold-bottom).diff-line-num.unfold.js-unfold.new_line[data-linenumber='#{new_pos}']", text: '...'
expect(output).to have_css 'td:nth-child(3):not(.parallel).line_content.match', text: ''
end
it "should allow to define text and bottom option" do
output = diff_match_line old_pos, new_pos, text: text, bottom: true
expect(output).to be_html_safe
expect(output).to have_css "td:nth-child(1).diff-line-num.unfold.js-unfold.js-unfold-bottom.old_line[data-linenumber='#{old_pos}']", text: '...'
expect(output).to have_css "td:nth-child(2).diff-line-num.unfold.js-unfold.js-unfold-bottom.new_line[data-linenumber='#{new_pos}']", text: '...'
expect(output).to have_css 'td:nth-child(3):not(.parallel).line_content.match', text: text
end
it "should generate match line for parallel view" do
output = diff_match_line old_pos, new_pos, text: text, view: :parallel
expect(output).to be_html_safe
expect(output).to have_css "td:nth-child(1):not(.js-unfold-bottom).diff-line-num.unfold.js-unfold.old_line[data-linenumber='#{old_pos}']", text: '...'
expect(output).to have_css 'td:nth-child(2).line_content.match.parallel', text: text
expect(output).to have_css "td:nth-child(3):not(.js-unfold-bottom).diff-line-num.unfold.js-unfold.new_line[data-linenumber='#{new_pos}']", text: '...'
expect(output).to have_css 'td:nth-child(4).line_content.match.parallel', text: text
end
it "should allow to generate only left match line for parallel view" do
output = diff_match_line old_pos, nil, text: text, view: :parallel
expect(output).to be_html_safe
expect(output).to have_css "td:nth-child(1):not(.js-unfold-bottom).diff-line-num.unfold.js-unfold.old_line[data-linenumber='#{old_pos}']", text: '...'
expect(output).to have_css 'td:nth-child(2).line_content.match.parallel', text: text
expect(output).not_to have_css 'td:nth-child(3)'
end
it "should allow to generate only right match line for parallel view" do
output = diff_match_line nil, new_pos, text: text, view: :parallel
expect(output).to be_html_safe
expect(output).to have_css "td:nth-child(1):not(.js-unfold-bottom).diff-line-num.unfold.js-unfold.new_line[data-linenumber='#{new_pos}']", text: '...'
expect(output).to have_css 'td:nth-child(2).line_content.match.parallel', text: text
expect(output).not_to have_css 'td:nth-child(3)'
end
end
end
......@@ -84,6 +84,11 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
end
it 'ignores absolute URLs with two leading slashes' do
doc = filter(link('//doc/api/README.md'))
expect(doc.at_css('a')['href']).to eq '//doc/api/README.md'
end
it 'rebuilds relative URL for a file in the repo' do
doc = filter(link('doc/api/README.md'))
expect(doc.at_css('a')['href']).
......
......@@ -3,10 +3,12 @@ require 'spec_helper'
describe Gitlab::ClosingIssueExtractor, lib: true do
let(:project) { create(:project) }
let(:project2) { create(:project) }
let(:forked_project) { Projects::ForkService.new(project, project.creator).execute }
let(:issue) { create(:issue, project: project) }
let(:issue2) { create(:issue, project: project2) }
let(:reference) { issue.to_reference }
let(:cross_reference) { issue2.to_reference(project) }
let(:fork_cross_reference) { issue.to_reference(forked_project) }
subject { described_class.new(project, project.creator) }
......@@ -278,6 +280,15 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
end
end
context "with a cross-project fork reference" do
subject { described_class.new(forked_project, forked_project.creator) }
it do
message = "Closes #{fork_cross_reference}"
expect(subject.closed_by_message(message)).to be_empty
end
end
context "with an invalid URL" do
it do
message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)}"
......
require 'spec_helper'
describe Gitlab::ImportExport::VersionChecker, services: true do
describe 'bundle a project Git repo' do
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: '') }
let(:version) { Gitlab::ImportExport.version }
before do
allow(File).to receive(:open).and_return(version)
end
it 'returns true if Import/Export have the same version' do
expect(described_class.check!(shared: shared)).to be true
end
context 'newer version' do
let(:version) { '900.0'}
it 'returns false if export version is newer' do
expect(described_class.check!(shared: shared)).to be false
end
it 'shows the correct error message' do
described_class.check!(shared: shared)
expect(shared.errors.first).to eq("Import version mismatch: Required <= #{Gitlab::ImportExport.version} but was #{version}")
end
end
end
end
......@@ -101,7 +101,7 @@ describe ProjectMember, models: true do
end
end
describe '.add_users_into_projects' do
describe '.add_users_to_projects' do
before do
@project_1 = create :project
@project_2 = create :project
......@@ -109,7 +109,7 @@ describe ProjectMember, models: true do
@user_1 = create :user
@user_2 = create :user
ProjectMember.add_users_into_projects(
ProjectMember.add_users_to_projects(
[@project_1.id, @project_2.id],
[@user_1.id, @user_2.id],
ProjectMember::MASTER
......
require 'spec_helper'
describe ImportExportCleanUpService, services: true do
describe '#execute' do
let(:service) { described_class.new }
let(:tmp_import_export_folder) { 'tmp/project_exports' }
context 'when the import/export directory does not exist' do
it 'does not remove any archives' do
path = '/invalid/path/'
stub_repository_downloads_path(path)
expect(File).to receive(:directory?).with(path + tmp_import_export_folder).and_return(false).at_least(:once)
expect(service).not_to receive(:clean_up_export_files)
service.execute
end
end
context 'when the import/export directory exists' do
it 'removes old files' do
in_directory_with_files(mtime: 2.days.ago) do |dir, files|
service.execute
files.each { |file| expect(File.exist?(file)).to eq false }
expect(File.directory?(dir)).to eq false
end
end
it 'does not remove new files' do
in_directory_with_files(mtime: 2.hours.ago) do |dir, files|
service.execute
files.each { |file| expect(File.exist?(file)).to eq true }
expect(File.directory?(dir)).to eq true
end
end
end
def in_directory_with_files(mtime:)
Dir.mktmpdir do |tmpdir|
stub_repository_downloads_path(tmpdir)
dir = File.join(tmpdir, tmp_import_export_folder, 'subfolder')
FileUtils.mkdir_p(dir)
files = FileUtils.touch(file_list(dir) + [dir], mtime: mtime.to_time)
yield(dir, files)
end
end
def stub_repository_downloads_path(path)
new_shared_settings = Settings.shared.merge('path' => path)
allow(Settings).to receive(:shared).and_return(new_shared_settings)
end
def file_list(dir)
Array.new(5) do |num|
File.join(dir, "random-#{num}.tar.gz")
end
end
end
end
......@@ -31,7 +31,7 @@ describe 'devise/shared/_signin_box' do
def enable_crowd
allow(view).to receive(:form_based_providers).and_return([:crowd])
allow(view).to receive(:crowd_enabled?).and_return(true)
allow(view).to receive(:user_omniauth_authorize_path).with('crowd').
allow(view).to receive(:omniauth_authorize_path).with(:user, :crowd).
and_return('/crowd')
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