Merge branch 'master' into sh-headless-chrome-support
* master: (21 commits) Fix bottom spacing for dropdowns that open upwards Fix linting errors in sprintf regression with product labels Hide Gollum inside Gitlab::Git::Wiki Restore User.from_gitaly Added skeleton loading paradigm to UX guide Remove those tests as they're not needed anymore Resolve "Precompiled assets with digest strings are ignored in CI" Make the `QA:Page::Project::Show` driver less brittle Change index on ci_builds to optimize Jobs Controller Add basic sprintf implementation to JavaScript Expose GitLab features to the CI runner Hide CI section markers from job trace Freeze the constant array Just allow the scheme we want! Adapt to the latest addressable behaviour Strip before passing to addressable, otherwise it's invalid Make sure we didn't blow up if URI is invalid Only update gems we need ok ...
This commit is contained in:
commit
356e9aa811
61 changed files with 645 additions and 187 deletions
6
Gemfile
6
Gemfile
|
@ -105,7 +105,7 @@ gem 'fog-rackspace', '~> 0.1.1'
|
|||
gem 'fog-aliyun', '~> 0.1.0'
|
||||
|
||||
# for Google storage
|
||||
gem 'google-api-client', '~> 0.8.6'
|
||||
gem 'google-api-client', '~> 0.13.6'
|
||||
|
||||
# for aws storage
|
||||
gem 'unf', '~> 0.1.4'
|
||||
|
@ -239,7 +239,7 @@ gem 'rack-proxy', '~> 0.6.0'
|
|||
gem 'sass-rails', '~> 5.0.6'
|
||||
gem 'uglifier', '~> 2.7.2'
|
||||
|
||||
gem 'addressable', '~> 2.3.8'
|
||||
gem 'addressable', '~> 2.5.2'
|
||||
gem 'bootstrap-sass', '~> 3.3.0'
|
||||
gem 'font-awesome-rails', '~> 4.7'
|
||||
gem 'gemojione', '~> 3.3'
|
||||
|
@ -356,7 +356,7 @@ end
|
|||
group :test do
|
||||
gem 'shoulda-matchers', '~> 3.1.2', require: false
|
||||
gem 'email_spec', '~> 1.6.0'
|
||||
gem 'json-schema', '~> 2.6.2'
|
||||
gem 'json-schema', '~> 2.8.0'
|
||||
gem 'webmock', '~> 2.3.2'
|
||||
gem 'test_after_commit', '~> 1.1'
|
||||
gem 'sham_rack', '~> 1.3.6'
|
||||
|
|
62
Gemfile.lock
62
Gemfile.lock
|
@ -45,7 +45,8 @@ GEM
|
|||
adamantium (0.2.0)
|
||||
ice_nine (~> 0.11.0)
|
||||
memoizable (~> 0.4.0)
|
||||
addressable (2.3.8)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
akismet (2.0.0)
|
||||
allocations (1.0.5)
|
||||
arel (6.0.4)
|
||||
|
@ -62,10 +63,6 @@ GEM
|
|||
attr_encrypted (3.0.3)
|
||||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.0)
|
||||
autoparse (0.3.3)
|
||||
addressable (>= 2.3.1)
|
||||
extlib (>= 0.9.15)
|
||||
multi_json (>= 1.0.0)
|
||||
autoprefixer-rails (6.2.3)
|
||||
execjs
|
||||
json
|
||||
|
@ -147,6 +144,8 @@ GEM
|
|||
debugger-ruby_core_source (1.3.8)
|
||||
deckar01-task_list (2.0.0)
|
||||
html-pipeline
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
default_value_for (3.0.2)
|
||||
activerecord (>= 3.2.0, < 5.1)
|
||||
descendants_tracker (0.0.4)
|
||||
|
@ -189,7 +188,6 @@ GEM
|
|||
excon (0.57.1)
|
||||
execjs (2.6.0)
|
||||
expression_parser (0.9.0)
|
||||
extlib (0.9.16)
|
||||
factory_girl (4.7.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.7.0)
|
||||
|
@ -289,10 +287,10 @@ GEM
|
|||
flowdock (~> 0.7)
|
||||
gitlab-grit (>= 2.4.1)
|
||||
multi_json
|
||||
gitlab-grit (2.8.1)
|
||||
gitlab-grit (2.8.2)
|
||||
charlock_holmes (~> 0.6)
|
||||
diff-lcs (~> 1.1)
|
||||
mime-types (>= 1.16, < 3)
|
||||
mime-types (>= 1.16)
|
||||
posix-spawn (~> 0.3)
|
||||
gitlab-markup (1.6.2)
|
||||
gitlab_omniauth-ldap (2.0.4)
|
||||
|
@ -320,20 +318,16 @@ GEM
|
|||
json
|
||||
multi_json
|
||||
request_store (>= 1.0)
|
||||
google-api-client (0.8.7)
|
||||
activesupport (>= 3.2, < 5.0)
|
||||
addressable (~> 2.3)
|
||||
autoparse (~> 0.3)
|
||||
extlib (~> 0.9)
|
||||
faraday (~> 0.9)
|
||||
googleauth (~> 0.3)
|
||||
launchy (~> 2.4)
|
||||
multi_json (~> 1.10)
|
||||
retriable (~> 1.4)
|
||||
signet (~> 0.6)
|
||||
google-api-client (0.13.6)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.5)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
google-protobuf (3.4.0.2)
|
||||
googleauth (0.5.1)
|
||||
faraday (~> 0.9)
|
||||
googleauth (0.5.3)
|
||||
faraday (~> 0.12)
|
||||
jwt (~> 1.4)
|
||||
logging (~> 2.0)
|
||||
memoist (~> 0.12)
|
||||
|
@ -423,8 +417,8 @@ GEM
|
|||
multi_json (>= 1.3)
|
||||
securecompare
|
||||
url_safe_base64
|
||||
json-schema (2.6.2)
|
||||
addressable (~> 2.3.8)
|
||||
json-schema (2.8.0)
|
||||
addressable (>= 2.4)
|
||||
jwt (1.5.6)
|
||||
kaminari (1.0.1)
|
||||
activesupport (>= 4.1.0)
|
||||
|
@ -476,18 +470,20 @@ GEM
|
|||
mail (2.6.6)
|
||||
mime-types (>= 1.16, < 4)
|
||||
mail_room (0.9.1)
|
||||
memoist (0.15.0)
|
||||
memoist (0.16.0)
|
||||
memoizable (0.4.2)
|
||||
thread_safe (~> 0.3, >= 0.3.1)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.99.3)
|
||||
mime-types (3.1)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mimemagic (0.3.0)
|
||||
mini_mime (0.1.4)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.7.0)
|
||||
mmap2 (2.2.7)
|
||||
mousetrap-rails (1.4.6)
|
||||
multi_json (1.12.1)
|
||||
multi_json (1.12.2)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mustermann (1.0.0)
|
||||
|
@ -631,6 +627,7 @@ GEM
|
|||
pry (~> 0.10)
|
||||
pry-rails (0.3.5)
|
||||
pry (>= 0.9.10)
|
||||
public_suffix (3.0.0)
|
||||
pyu-ruby-sasl (0.0.3.3)
|
||||
rack (1.6.8)
|
||||
rack-accept (0.4.5)
|
||||
|
@ -713,6 +710,10 @@ GEM
|
|||
redis-store (~> 1.2.0)
|
||||
redis-store (1.2.0)
|
||||
redis (>= 2.2)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
request_store (1.3.1)
|
||||
responders (2.3.0)
|
||||
railties (>= 4.2.0, < 5.1)
|
||||
|
@ -720,7 +721,7 @@ GEM
|
|||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 4.0)
|
||||
netrc (~> 0.8)
|
||||
retriable (1.4.1)
|
||||
retriable (3.1.1)
|
||||
rinku (2.0.0)
|
||||
rotp (2.1.2)
|
||||
rouge (2.2.1)
|
||||
|
@ -902,6 +903,7 @@ GEM
|
|||
tzinfo (1.2.3)
|
||||
thread_safe (~> 0.1)
|
||||
u2f (0.2.1)
|
||||
uber (0.1.0)
|
||||
uglifier (2.7.2)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
|
@ -959,7 +961,7 @@ DEPENDENCIES
|
|||
ace-rails-ap (~> 4.1.0)
|
||||
activerecord_sane_schema_dumper (= 0.2)
|
||||
acts-as-taggable-on (~> 4.0)
|
||||
addressable (~> 2.3.8)
|
||||
addressable (~> 2.5.2)
|
||||
akismet (~> 2.0)
|
||||
allocations (~> 1.0)
|
||||
asana (~> 0.6.0)
|
||||
|
@ -1029,7 +1031,7 @@ DEPENDENCIES
|
|||
gollum-lib (~> 4.2)
|
||||
gollum-rugged_adapter (~> 0.4.4)
|
||||
gon (~> 6.1.0)
|
||||
google-api-client (~> 0.8.6)
|
||||
google-api-client (~> 0.13.6)
|
||||
gpgme
|
||||
grape (~> 1.0)
|
||||
grape-entity (~> 0.6.0)
|
||||
|
@ -1047,7 +1049,7 @@ DEPENDENCIES
|
|||
jira-ruby (~> 1.4)
|
||||
jquery-atwho-rails (~> 1.3.2)
|
||||
jquery-rails (~> 4.1.0)
|
||||
json-schema (~> 2.6.2)
|
||||
json-schema (~> 2.8.0)
|
||||
jwt (~> 1.5.6)
|
||||
kaminari (~> 1.0)
|
||||
knapsack (~> 1.11.0)
|
||||
|
|
|
@ -197,6 +197,11 @@ month. When we say 'the most recent monthly release', this can refer to either
|
|||
the version currently running on GitLab.com, or the most recent version
|
||||
available in the package repositories.
|
||||
|
||||
A regression issue should be labeled with the appropriate [subject label](../CONTRIBUTING.md#subject-labels-wiki-container-registry-ldap-api-etc)
|
||||
and [team label](../CONTRIBUTING.md#team-labels-ci-discussion-edge-platform-etc),
|
||||
just like any other issue, to help GitLab team members focus on issues that are
|
||||
relevant to [their area of responsibility](https://about.gitlab.com/handbook/engineering/workflow/#choosing-something-to-work-on).
|
||||
|
||||
## Release retrospective and kickoff
|
||||
|
||||
- [Retrospective](https://about.gitlab.com/handbook/engineering/workflow/#retrospective)
|
||||
|
|
|
@ -548,6 +548,7 @@ GitLabDropdown = (function() {
|
|||
GitLabDropdown.prototype.positionMenuAbove = function() {
|
||||
var $menu = this.dropdown.find('.dropdown-menu');
|
||||
|
||||
$menu.addClass('dropdown-open-top');
|
||||
$menu.css('top', 'initial');
|
||||
$menu.css('bottom', '100%');
|
||||
};
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import Jed from 'jed';
|
||||
|
||||
import sprintf from './sprintf';
|
||||
|
||||
/**
|
||||
This is required to require all the translation folders in the current directory
|
||||
this saves us having to do this manually & keep up to date with new languages
|
||||
|
@ -66,4 +68,5 @@ export { lang };
|
|||
export { gettext as __ };
|
||||
export { ngettext as n__ };
|
||||
export { pgettext as s__ };
|
||||
export { sprintf };
|
||||
export default locale;
|
||||
|
|
26
app/assets/javascripts/locale/sprintf.js
Normal file
26
app/assets/javascripts/locale/sprintf.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import _ from 'underscore';
|
||||
|
||||
/**
|
||||
Very limited implementation of sprintf supporting only named parameters.
|
||||
|
||||
@param input (translated) text with parameters (e.g. '%{num_users} users use us')
|
||||
@param parameters object mapping parameter names to values (e.g. { num_users: 5 })
|
||||
@param escapeParameters whether parameter values should be escaped (see http://underscorejs.org/#escape)
|
||||
@returns {String} the text with parameters replaces (e.g. '5 users use us')
|
||||
|
||||
@see https://ruby-doc.org/core-2.3.3/Kernel.html#method-i-sprintf
|
||||
@see https://gitlab.com/gitlab-org/gitlab-ce/issues/37992
|
||||
**/
|
||||
export default (input, parameters, escapeParameters = true) => {
|
||||
let output = input;
|
||||
|
||||
if (parameters) {
|
||||
Object.keys(parameters).forEach((parameterName) => {
|
||||
const parameterValue = parameters[parameterName];
|
||||
const escapedParameterValue = escapeParameters ? _.escape(parameterValue) : parameterValue;
|
||||
output = output.replace(new RegExp(`%{${parameterName}}`, 'g'), escapedParameterValue);
|
||||
});
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
|
@ -2,6 +2,7 @@ import {
|
|||
__,
|
||||
n__,
|
||||
s__,
|
||||
sprintf,
|
||||
} from '../locale';
|
||||
|
||||
export default (Vue) => {
|
||||
|
@ -37,6 +38,7 @@ export default (Vue) => {
|
|||
@returns {String} Translated context based text
|
||||
**/
|
||||
s__,
|
||||
sprintf,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
@ -745,6 +745,10 @@
|
|||
#{$selector}.dropdown-menu-nav {
|
||||
margin-bottom: 24px;
|
||||
|
||||
&.dropdown-open-top {
|
||||
margin-bottom: $dropdown-vertical-offset;
|
||||
}
|
||||
|
||||
li {
|
||||
display: block;
|
||||
padding: 0 1px;
|
||||
|
|
|
@ -295,7 +295,7 @@ header.navbar-gitlab-new {
|
|||
|
||||
.header-user .dropdown-menu-nav,
|
||||
.header-new .dropdown-menu-nav {
|
||||
margin-top: 4px;
|
||||
margin-top: $dropdown-vertical-offset;
|
||||
}
|
||||
|
||||
.breadcrumbs {
|
||||
|
|
|
@ -327,6 +327,7 @@ $regular_font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-San
|
|||
* Dropdowns
|
||||
*/
|
||||
$dropdown-width: 300px;
|
||||
$dropdown-vertical-offset: 4px;
|
||||
$dropdown-link-color: #555;
|
||||
$dropdown-link-hover-bg: $row-hover;
|
||||
$dropdown-empty-row-bg: rgba(#000, .04);
|
||||
|
|
|
@ -362,7 +362,7 @@
|
|||
|
||||
.dropdown-menu {
|
||||
top: initial;
|
||||
bottom: 40px;
|
||||
bottom: 100%;
|
||||
width: 298px;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,16 +18,12 @@ class Projects::WikisController < Projects::ApplicationController
|
|||
response.headers['Content-Security-Policy'] = "default-src 'none'"
|
||||
response.headers['X-Content-Security-Policy'] = "default-src 'none'"
|
||||
|
||||
if file.on_disk?
|
||||
send_file file.on_disk_path, disposition: 'inline'
|
||||
else
|
||||
send_data(
|
||||
file.raw_data,
|
||||
type: file.mime_type,
|
||||
disposition: 'inline',
|
||||
filename: file.name
|
||||
)
|
||||
end
|
||||
send_data(
|
||||
file.raw_data,
|
||||
type: file.mime_type,
|
||||
disposition: 'inline',
|
||||
filename: file.name
|
||||
)
|
||||
else
|
||||
return render('empty') unless can?(current_user, :create_wiki, @project)
|
||||
@page = WikiPage.new(@project_wiki)
|
||||
|
|
|
@ -229,6 +229,10 @@ module Ci
|
|||
variables
|
||||
end
|
||||
|
||||
def features
|
||||
{ trace_sections: true }
|
||||
end
|
||||
|
||||
def merge_request
|
||||
return @merge_request if defined?(@merge_request)
|
||||
|
||||
|
|
|
@ -54,12 +54,15 @@ class ProjectWiki
|
|||
[Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('')
|
||||
end
|
||||
|
||||
# Returns the Gollum::Wiki object.
|
||||
# Returns the Gitlab::Git::Wiki object.
|
||||
def wiki
|
||||
@wiki ||= begin
|
||||
Gollum::Wiki.new(path_to_repo)
|
||||
rescue Rugged::OSError
|
||||
create_repo!
|
||||
gl_repository = Gitlab::GlRepository.gl_repository(project, true)
|
||||
raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository)
|
||||
|
||||
create_repo!(raw_repository) unless raw_repository.exists?
|
||||
|
||||
Gitlab::Git::Wiki.new(raw_repository)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -86,20 +89,14 @@ class ProjectWiki
|
|||
# Returns an initialized WikiPage instance or nil
|
||||
def find_page(title, version = nil)
|
||||
page_title, page_dir = page_title_and_dir(title)
|
||||
if page = wiki.page(page_title, version, page_dir)
|
||||
|
||||
if page = wiki.page(title: page_title, version: version, dir: page_dir)
|
||||
WikiPage.new(self, page, true)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def find_file(name, version = nil, try_on_disk = true)
|
||||
version = wiki.ref if version.nil? # Gollum::Wiki#file ?
|
||||
if wiki_file = wiki.file(name, version, try_on_disk)
|
||||
wiki_file
|
||||
else
|
||||
nil
|
||||
end
|
||||
def find_file(name, version = nil)
|
||||
wiki.file(name, version)
|
||||
end
|
||||
|
||||
def create_page(title, content, format = :markdown, message = nil)
|
||||
|
@ -108,7 +105,7 @@ class ProjectWiki
|
|||
wiki.write_page(title, format.to_sym, content, commit)
|
||||
|
||||
update_project_activity
|
||||
rescue Gollum::DuplicatePageError => e
|
||||
rescue Gitlab::Git::Wiki::DuplicatePageError => e
|
||||
@error_message = "Duplicate page: #{e.message}"
|
||||
return false
|
||||
end
|
||||
|
@ -116,13 +113,13 @@ class ProjectWiki
|
|||
def update_page(page, content:, title: nil, format: :markdown, message: nil)
|
||||
commit = commit_details(:updated, message, page.title)
|
||||
|
||||
wiki.update_page(page, title || page.name, format.to_sym, content, commit)
|
||||
wiki.update_page(page.path, title || page.name, format.to_sym, content, commit)
|
||||
|
||||
update_project_activity
|
||||
end
|
||||
|
||||
def delete_page(page, message = nil)
|
||||
wiki.delete_page(page, commit_details(:deleted, message, page.title))
|
||||
wiki.delete_page(page.path, commit_details(:deleted, message, page.title))
|
||||
|
||||
update_project_activity
|
||||
end
|
||||
|
@ -145,20 +142,8 @@ class ProjectWiki
|
|||
wiki.class.default_ref
|
||||
end
|
||||
|
||||
def create_repo!
|
||||
if init_repo(disk_path)
|
||||
wiki = Gollum::Wiki.new(path_to_repo)
|
||||
else
|
||||
raise CouldNotCreateWikiError
|
||||
end
|
||||
|
||||
repository.after_create
|
||||
|
||||
wiki
|
||||
end
|
||||
|
||||
def ensure_repository
|
||||
create_repo! unless repository_exists?
|
||||
raise CouldNotCreateWikiError unless wiki.repository_exists?
|
||||
end
|
||||
|
||||
def hook_attrs
|
||||
|
@ -173,24 +158,24 @@ class ProjectWiki
|
|||
|
||||
private
|
||||
|
||||
def init_repo(disk_path)
|
||||
def create_repo!(raw_repository)
|
||||
gitlab_shell.add_repository(project.repository_storage, disk_path)
|
||||
|
||||
raise CouldNotCreateWikiError unless raw_repository.exists?
|
||||
|
||||
repository.after_create
|
||||
end
|
||||
|
||||
def commit_details(action, message = nil, title = nil)
|
||||
commit_message = message || default_message(action, title)
|
||||
|
||||
{ email: @user.email, name: @user.name, message: commit_message }
|
||||
Gitlab::Git::Wiki::CommitDetails.new(@user.name, @user.email, commit_message)
|
||||
end
|
||||
|
||||
def default_message(action, title)
|
||||
"#{@user.username} #{action} page: #{title}"
|
||||
end
|
||||
|
||||
def path_to_repo
|
||||
@path_to_repo ||= File.join(project.repository_storage_path, "#{disk_path}.git")
|
||||
end
|
||||
|
||||
def update_project_activity
|
||||
@project.touch(:last_activity_at, :last_repository_updated_at)
|
||||
end
|
||||
|
|
|
@ -50,7 +50,7 @@ class WikiPage
|
|||
# The Gitlab ProjectWiki instance.
|
||||
attr_reader :wiki
|
||||
|
||||
# The raw Gollum::Page instance.
|
||||
# The raw Gitlab::Git::WikiPage instance.
|
||||
attr_reader :page
|
||||
|
||||
# The attributes Hash used for storing and validating
|
||||
|
@ -75,7 +75,7 @@ class WikiPage
|
|||
if @attributes[:slug].present?
|
||||
@attributes[:slug]
|
||||
else
|
||||
wiki.wiki.preview_page(title, '', format).url_path
|
||||
wiki.wiki.preview_slug(title, format)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -131,7 +131,7 @@ class WikiPage
|
|||
def versions
|
||||
return [] unless persisted?
|
||||
|
||||
@page.versions
|
||||
wiki.wiki.page_versions(@page.path)
|
||||
end
|
||||
|
||||
def commit
|
||||
|
@ -264,8 +264,8 @@ class WikiPage
|
|||
end
|
||||
|
||||
page_title, page_dir = wiki.page_title_and_dir(page_details)
|
||||
gollum_wiki = wiki.wiki
|
||||
@page = gollum_wiki.paged(page_title, page_dir)
|
||||
gitlab_git_wiki = wiki.wiki
|
||||
@page = gitlab_git_wiki.page(title: page_title, dir: page_dir)
|
||||
|
||||
set_attributes
|
||||
@persisted = errors.blank?
|
||||
|
|
|
@ -29,13 +29,13 @@
|
|||
commit.id, index == 0) do
|
||||
= truncate_sha(commit.id)
|
||||
%td
|
||||
= commit.author.name
|
||||
= commit.author_name
|
||||
%td
|
||||
= commit.message
|
||||
%td
|
||||
#{time_ago_with_tooltip(version.authored_date)}
|
||||
%td
|
||||
%strong
|
||||
= @page.page.wiki.page(@page.page.name, commit.id).try(:format)
|
||||
= version.format
|
||||
|
||||
= render 'sidebar'
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
.nav-text
|
||||
%h2.wiki-page-title= @page.title.capitalize
|
||||
%span.wiki-last-edit-by
|
||||
= (_("Last edited by %{name}") % { name: "<strong>#{@page.commit.author.name}</strong>" }).html_safe
|
||||
= (_("Last edited by %{name}") % { name: "<strong>#{@page.commit.author_name}</strong>" }).html_safe
|
||||
#{time_ago_with_tooltip(@page.commit.authored_date)}
|
||||
|
||||
.nav-controls
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
= button_tag type: 'button', class: 'btn btn-nr dropdown-toggle comment-btn js-note-new-discussion js-disable-on-submit', data: { 'dropdown-trigger' => '#resolvable-comment-menu' }, 'aria-label' => 'Open comment type dropdown' do
|
||||
= icon('caret-down', class: 'toggle-icon')
|
||||
|
||||
%ul#resolvable-comment-menu.dropdown-menu{ data: { dropdown: true } }
|
||||
%ul#resolvable-comment-menu.dropdown-menu.dropdown-open-top{ data: { dropdown: true } }
|
||||
%li#comment.droplab-item-selected{ data: { value: '', 'submit-text' => 'Comment', 'close-text' => "Comment & close #{noteable_name}", 'reopen-text' => "Comment & reopen #{noteable_name}" } }
|
||||
%button.btn.btn-transparent
|
||||
= icon('check', class: 'icon')
|
||||
|
|
5
changelogs/unreleased/37970-timestamped-ci.yml
Normal file
5
changelogs/unreleased/37970-timestamped-ci.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Strip gitlab-runner section markers in build trace HTML view
|
||||
merge_request: 14393
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix bottom spacing for dropdowns that open upwards
|
||||
merge_request: 14535
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Change index on ci_builds to optimize Jobs Controller
|
||||
merge_request:
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Bump google-api-client Gem from 0.8.6 to 0.13.6
|
||||
merge_request:
|
||||
author:
|
||||
type: other
|
5
changelogs/unreleased/winh-sprintf.yml
Normal file
5
changelogs/unreleased/winh-sprintf.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add basic sprintf implementation to JavaScript
|
||||
merge_request: 14506
|
||||
author:
|
||||
type: other
|
|
@ -16,7 +16,7 @@ Rails.application.configure do
|
|||
config.cache_classes = ENV['CACHE_CLASSES'] == 'true'
|
||||
|
||||
# Configure static asset server for tests with Cache-Control for performance
|
||||
config.assets.digest = false
|
||||
config.assets.compile = false if ENV['CI']
|
||||
config.serve_static_files = true
|
||||
config.static_cache_control = "public, max-age=3600"
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddCiBuildsIndexForJobscontroller < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
# Set this constant to true if this migration requires downtime.
|
||||
DOWNTIME = false
|
||||
|
||||
# When a migration requires downtime you **must** uncomment the following
|
||||
# constant and define a short and easy to understand explanation as to why the
|
||||
# migration requires downtime.
|
||||
# DOWNTIME_REASON = ''
|
||||
|
||||
# When using the methods "add_concurrent_index", "remove_concurrent_index" or
|
||||
# "add_column_with_default" you must disable the use of transactions
|
||||
# as these methods can not run in an existing transaction.
|
||||
# When using "add_concurrent_index" or "remove_concurrent_index" methods make sure
|
||||
# that either of them is the _only_ method called in the migration,
|
||||
# any other changes should go in a separate migration.
|
||||
# This ensures that upon failure _only_ the index creation or removing fails
|
||||
# and can be retried or reverted easily.
|
||||
#
|
||||
# To disable transactions uncomment the following line and remove these
|
||||
# comments:
|
||||
# disable_ddl_transaction!
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :ci_builds, [:project_id, :id] unless index_exists? :ci_builds, [:project_id, :id]
|
||||
remove_concurrent_index :ci_builds, :project_id if index_exists? :ci_builds, :project_id
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index :ci_builds, :project_id unless index_exists? :ci_builds, :project_id
|
||||
remove_concurrent_index :ci_builds, [:project_id, :id] if index_exists? :ci_builds, [:project_id, :id]
|
||||
end
|
||||
end
|
|
@ -256,7 +256,7 @@ ActiveRecord::Schema.define(version: 20170928100231) do
|
|||
add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
|
||||
add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
|
||||
add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
|
||||
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
|
||||
add_index "ci_builds", ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree
|
||||
add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree
|
||||
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
|
||||
add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
|
||||
|
|
|
@ -183,13 +183,20 @@ aren't in the message with id `1 pipeline`.
|
|||
|
||||
### Interpolation
|
||||
|
||||
- In Ruby/HAML:
|
||||
- In Ruby/HAML (see [sprintf]):
|
||||
|
||||
```ruby
|
||||
_("Hello %{name}") % { name: 'Joe' }
|
||||
```
|
||||
|
||||
- In JavaScript: Not supported at this moment.
|
||||
- In JavaScript: Only named parameters are supported (see also [#37992]):
|
||||
|
||||
```javascript
|
||||
__("Hello %{name}") % { name: 'Joe' }
|
||||
```
|
||||
|
||||
[sprintf]: http://ruby-doc.org/core/Kernel.html#method-i-sprintf
|
||||
[#37992]: https://gitlab.com/gitlab-org/gitlab-ce/issues/37992
|
||||
|
||||
### Plurals
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@ When information is updating in place, a quick, subtle animation is needed. The
|
|||
|
||||
![Quick update animation](img/animation-quickupdate.gif)
|
||||
|
||||
### Skeleton loading
|
||||
|
||||
Skeleton loading is explained in the [component section](components.html#skeleton-loading) of the UX guide. It includes a horizontally pulsating animation that shows motion as if it's growing. It's timing is a slower `linear 1s`.
|
||||
|
||||
![Skeleton loading animation](img/skeleton-loading.gif)
|
||||
|
||||
### Moving transitions
|
||||
|
||||
When elements move on screen, there should be a quick animation so it is clear to users what moved where. The timing of this animation differs based on the amount of movement and change. Consider animations between `200ms` and `400ms`.
|
||||
|
@ -51,7 +57,9 @@ View the [interactive example](http://codepen.io/awhildy/full/ALyKPE/) here.
|
|||
![Reorder animation](img/animation-reorder.gif)
|
||||
|
||||
#### Autoscroll the page
|
||||
|
||||
Another example of a moving transition is when you have to autoscroll the page to keep an active element visible.
|
||||
|
||||
View the [interactive example](http://codepen.io/awhildy/full/PbxgVo/) here.
|
||||
![Autoscroll animation](img/animation-autoscroll.gif)
|
||||
|
||||
![Autoscroll animation](img/animation-autoscroll.gif)
|
||||
|
|
|
@ -204,6 +204,25 @@ Cover blocks are generally used to create a heading element for a page, such as
|
|||
|
||||
---
|
||||
|
||||
## Skeleton loading
|
||||
|
||||
Skeleton loading is a way to convey to the user what kind of content is currently being loaded. It's a paradigm with which content can independently and asynchronously be loaded, while still adhering to the structure and look of the completely loaded view.
|
||||
|
||||
### Requirements
|
||||
|
||||
* A skeleton should represent an organism in a recognisable way
|
||||
* Atom elements within organisms (for reference see this article on [atomic design methodology](http://atomicdesign.bradfrost.com/chapter-2/)) may be represented in a maximum of 3 repetitions, if applicable.
|
||||
* Skeletons should only be presented in grayscale using the HEX colors: `#fafafa` or `#ffffff` (except for shadows)
|
||||
* Animate the grey atoms in a pulsating way to show motion, as if "loading". The pulse animation transitions colors horizontally from left to right, starting with `#f2f2f2` to `#fafafa`.
|
||||
|
||||
![Skeleton loading animation](img/skeleton-loading.gif)
|
||||
|
||||
### Usage
|
||||
|
||||
Skeleton loading can replace any existing UI elements for the period in which they are loaded and should aim for maintaining a similar structure visually.
|
||||
|
||||
---
|
||||
|
||||
## Panels
|
||||
|
||||
> TODO: Catalog how we are currently using panels and rationalize how they relate to alerts
|
||||
|
|
BIN
doc/development/ux_guide/img/skeleton-loading.gif
Normal file
BIN
doc/development/ux_guide/img/skeleton-loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1 MiB |
|
@ -1022,6 +1022,7 @@ module API
|
|||
expose :cache, using: Cache
|
||||
expose :credentials, using: Credentials
|
||||
expose :dependencies, using: Dependency
|
||||
expose :features
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -73,8 +73,9 @@ module Banzai
|
|||
return unless node.has_attribute?('href')
|
||||
|
||||
begin
|
||||
node['href'] = node['href'].strip
|
||||
uri = Addressable::URI.parse(node['href'])
|
||||
uri.scheme = uri.scheme.strip.downcase if uri.scheme
|
||||
uri.scheme = uri.scheme.downcase if uri.scheme
|
||||
|
||||
node.remove_attribute('href') if UNSAFE_PROTOCOLS.include?(uri.scheme)
|
||||
rescue Addressable::URI::InvalidURIError
|
||||
|
|
|
@ -155,7 +155,9 @@ module Gitlab
|
|||
stream.each_line do |line|
|
||||
s = StringScanner.new(line)
|
||||
until s.eos?
|
||||
if s.scan(/\e([@-_])(.*?)([@-~])/)
|
||||
if s.scan(/section_((?:start)|(?:end)):(\d+):([^\r]+)\r\033\[0K/)
|
||||
handle_section(s)
|
||||
elsif s.scan(/\e([@-_])(.*?)([@-~])/)
|
||||
handle_sequence(s)
|
||||
elsif s.scan(/\e(([@-_])(.*?)?)?$/)
|
||||
break
|
||||
|
@ -183,6 +185,15 @@ module Gitlab
|
|||
)
|
||||
end
|
||||
|
||||
def handle_section(s)
|
||||
action = s[1]
|
||||
timestamp = s[2]
|
||||
section = s[3]
|
||||
line = s.matched()[0...-5] # strips \r\033[0K
|
||||
|
||||
@out << %{<div class="hidden" data-action="#{action}" data-timestamp="#{timestamp}" data-section="#{section}">#{line}</div>}
|
||||
end
|
||||
|
||||
def handle_sequence(s)
|
||||
indicator = s[1]
|
||||
commands = s[2].split ';'
|
||||
|
|
|
@ -7,6 +7,11 @@ module Gitlab
|
|||
new(gitlab_user.username, gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user))
|
||||
end
|
||||
|
||||
# TODO support the username field in Gitaly https://gitlab.com/gitlab-org/gitaly/issues/628
|
||||
def self.from_gitaly(gitaly_user)
|
||||
new('', gitaly_user.name, gitaly_user.email, gitaly_user.gl_id)
|
||||
end
|
||||
|
||||
def initialize(username, name, email, gl_id)
|
||||
@username = username
|
||||
@name = name
|
||||
|
|
115
lib/gitlab/git/wiki.rb
Normal file
115
lib/gitlab/git/wiki.rb
Normal file
|
@ -0,0 +1,115 @@
|
|||
module Gitlab
|
||||
module Git
|
||||
class Wiki
|
||||
DuplicatePageError = Class.new(StandardError)
|
||||
|
||||
CommitDetails = Struct.new(:name, :email, :message) do
|
||||
def to_h
|
||||
{ name: name, email: email, message: message }
|
||||
end
|
||||
end
|
||||
|
||||
def self.default_ref
|
||||
'master'
|
||||
end
|
||||
|
||||
# Initialize with a Gitlab::Git::Repository instance
|
||||
def initialize(repository)
|
||||
@repository = repository
|
||||
end
|
||||
|
||||
def repository_exists?
|
||||
@repository.exists?
|
||||
end
|
||||
|
||||
def write_page(name, format, content, commit_details)
|
||||
assert_type!(format, Symbol)
|
||||
assert_type!(commit_details, CommitDetails)
|
||||
|
||||
gollum_wiki.write_page(name, format, content, commit_details.to_h)
|
||||
|
||||
nil
|
||||
rescue Gollum::DuplicatePageError => e
|
||||
raise Gitlab::Git::Wiki::DuplicatePageError, e.message
|
||||
end
|
||||
|
||||
def delete_page(page_path, commit_details)
|
||||
assert_type!(commit_details, CommitDetails)
|
||||
|
||||
gollum_wiki.delete_page(gollum_page_by_path(page_path), commit_details.to_h)
|
||||
nil
|
||||
end
|
||||
|
||||
def update_page(page_path, title, format, content, commit_details)
|
||||
assert_type!(format, Symbol)
|
||||
assert_type!(commit_details, CommitDetails)
|
||||
|
||||
gollum_wiki.update_page(gollum_page_by_path(page_path), title, format, content, commit_details.to_h)
|
||||
nil
|
||||
end
|
||||
|
||||
def pages
|
||||
gollum_wiki.pages.map { |gollum_page| new_page(gollum_page) }
|
||||
end
|
||||
|
||||
def page(title:, version: nil, dir: nil)
|
||||
if version
|
||||
version = Gitlab::Git::Commit.find(@repository, version).id
|
||||
end
|
||||
|
||||
gollum_page = gollum_wiki.page(title, version, dir)
|
||||
return unless gollum_page
|
||||
|
||||
new_page(gollum_page)
|
||||
end
|
||||
|
||||
def file(name, version)
|
||||
version ||= self.class.default_ref
|
||||
gollum_file = gollum_wiki.file(name, version)
|
||||
return unless gollum_file
|
||||
|
||||
Gitlab::Git::WikiFile.new(gollum_file)
|
||||
end
|
||||
|
||||
def page_versions(page_path)
|
||||
current_page = gollum_page_by_path(page_path)
|
||||
current_page.versions.map do |gollum_git_commit|
|
||||
gollum_page = gollum_wiki.page(current_page.title, gollum_git_commit.id)
|
||||
new_version(gollum_page, gollum_git_commit.id)
|
||||
end
|
||||
end
|
||||
|
||||
def preview_slug(title, format)
|
||||
gollum_wiki.preview_page(title, '', format).url_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gollum_wiki
|
||||
@gollum_wiki ||= Gollum::Wiki.new(@repository.path)
|
||||
end
|
||||
|
||||
def gollum_page_by_path(page_path)
|
||||
page_name = Gollum::Page.canonicalize_filename(page_path)
|
||||
page_dir = File.split(page_path).first
|
||||
|
||||
gollum_wiki.paged(page_name, page_dir)
|
||||
end
|
||||
|
||||
def new_page(gollum_page)
|
||||
Gitlab::Git::WikiPage.new(gollum_page, new_version(gollum_page, gollum_page.version.id))
|
||||
end
|
||||
|
||||
def new_version(gollum_page, commit_id)
|
||||
commit = Gitlab::Git::Commit.find(@repository, commit_id)
|
||||
Gitlab::Git::WikiPageVersion.new(commit, gollum_page&.format)
|
||||
end
|
||||
|
||||
def assert_type!(object, klass)
|
||||
unless object.is_a?(klass)
|
||||
raise ArgumentError, "expected a #{klass}, got #{object.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
19
lib/gitlab/git/wiki_file.rb
Normal file
19
lib/gitlab/git/wiki_file.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
module Gitlab
|
||||
module Git
|
||||
class WikiFile
|
||||
attr_reader :mime_type, :raw_data, :name
|
||||
|
||||
# This class is meant to be serializable so that it can be constructed
|
||||
# by Gitaly and sent over the network to GitLab.
|
||||
#
|
||||
# Because Gollum::File is not serializable we must get all the data from
|
||||
# 'gollum_file' during initialization, and NOT store it in an instance
|
||||
# variable.
|
||||
def initialize(gollum_file)
|
||||
@mime_type = gollum_file.mime_type
|
||||
@raw_data = gollum_file.raw_data
|
||||
@name = gollum_file.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
39
lib/gitlab/git/wiki_page.rb
Normal file
39
lib/gitlab/git/wiki_page.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
module Gitlab
|
||||
module Git
|
||||
class WikiPage
|
||||
attr_reader :url_path, :title, :format, :path, :version, :raw_data, :name, :text_data, :historical
|
||||
|
||||
# This class is meant to be serializable so that it can be constructed
|
||||
# by Gitaly and sent over the network to GitLab.
|
||||
#
|
||||
# Because Gollum::Page is not serializable we must get all the data from
|
||||
# 'gollum_page' during initialization, and NOT store it in an instance
|
||||
# variable.
|
||||
#
|
||||
# Note that 'version' is a WikiPageVersion instance which it itself
|
||||
# serializable. That means it's OK to store 'version' in an instance
|
||||
# variable.
|
||||
def initialize(gollum_page, version)
|
||||
@url_path = gollum_page.url_path
|
||||
@title = gollum_page.title
|
||||
@format = gollum_page.format
|
||||
@path = gollum_page.path
|
||||
@raw_data = gollum_page.raw_data
|
||||
@name = gollum_page.name
|
||||
@historical = gollum_page.historical?
|
||||
|
||||
@version = version
|
||||
end
|
||||
|
||||
def historical?
|
||||
@historical
|
||||
end
|
||||
|
||||
def text_data
|
||||
return @text_data if defined?(@text_data)
|
||||
|
||||
@text_data = @raw_data && Gitlab::EncodingHelper.encode!(@raw_data.dup)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
19
lib/gitlab/git/wiki_page_version.rb
Normal file
19
lib/gitlab/git/wiki_page_version.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
module Gitlab
|
||||
module Git
|
||||
class WikiPageVersion
|
||||
attr_reader :commit, :format
|
||||
|
||||
# This class is meant to be serializable so that it can be constructed
|
||||
# by Gitaly and sent over the network to GitLab.
|
||||
#
|
||||
# Both 'commit' (a Gitlab::Git::Commit) and 'format' (a string) are
|
||||
# serializable.
|
||||
def initialize(commit, format)
|
||||
@commit = commit
|
||||
@format = format
|
||||
end
|
||||
|
||||
delegate :message, :sha, :id, :author_name, :authored_date, to: :commit
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,9 @@
|
|||
module Gitlab
|
||||
class UrlSanitizer
|
||||
ALLOWED_SCHEMES = %w[http https ssh git].freeze
|
||||
|
||||
def self.sanitize(content)
|
||||
regexp = URI::Parser.new.make_regexp(%w(http https ssh git))
|
||||
regexp = URI::Parser.new.make_regexp(ALLOWED_SCHEMES)
|
||||
|
||||
content.gsub(regexp) { |url| new(url).masked_url }
|
||||
rescue Addressable::URI::InvalidURIError
|
||||
|
@ -11,9 +13,9 @@ module Gitlab
|
|||
def self.valid?(url)
|
||||
return false unless url.present?
|
||||
|
||||
Addressable::URI.parse(url.strip)
|
||||
uri = Addressable::URI.parse(url.strip)
|
||||
|
||||
true
|
||||
ALLOWED_SCHEMES.include?(uri.scheme)
|
||||
rescue Addressable::URI::InvalidURIError
|
||||
false
|
||||
end
|
||||
|
|
|
@ -5,8 +5,8 @@ module QA
|
|||
def choose_repository_clone_http
|
||||
find('#clone-dropdown').click
|
||||
|
||||
page.within('#clone-dropdown') do
|
||||
find('span', text: 'HTTP').click
|
||||
page.within('.clone-options-dropdown') do
|
||||
click_link('HTTP')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ describe Projects::JobsController do
|
|||
expect(json_response['text']).to eq status.text
|
||||
expect(json_response['label']).to eq status.label
|
||||
expect(json_response['icon']).to eq status.icon
|
||||
expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico"
|
||||
expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -658,7 +658,7 @@ describe Projects::MergeRequestsController do
|
|||
expect(json_response['text']).to eq status.text
|
||||
expect(json_response['label']).to eq status.label
|
||||
expect(json_response['icon']).to eq status.icon
|
||||
expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico"
|
||||
expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -142,7 +142,7 @@ describe Projects::PipelinesController do
|
|||
expect(json_response['text']).to eq status.text
|
||||
expect(json_response['label']).to eq status.label
|
||||
expect(json_response['icon']).to eq status.icon
|
||||
expect(json_response['favicon']).to eq "/assets/ci_favicons/#{status.favicon}.ico"
|
||||
expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ describe 'User views a wiki page' do
|
|||
it 'shows a file stored in a page' do
|
||||
file = Gollum::File.new(project.wiki)
|
||||
|
||||
allow_any_instance_of(Gollum::Wiki).to receive(:file).with('image.jpg', 'master', true).and_return(file)
|
||||
allow_any_instance_of(Gollum::Wiki).to receive(:file).with('image.jpg', 'master').and_return(file)
|
||||
allow_any_instance_of(Gollum::File).to receive(:mime_type).and_return('image/jpeg')
|
||||
|
||||
expect(page).to have_xpath('//img[@data-src="image.jpg"]')
|
||||
|
|
|
@ -17,7 +17,7 @@ describe GroupsHelper do
|
|||
it 'gives default avatar_icon when no avatar is present' do
|
||||
group = create(:group)
|
||||
group.save!
|
||||
expect(group_icon(group.path)).to match('group_avatar.png')
|
||||
expect(group_icon(group.path)).to match_asset_path('group_avatar.png')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ describe PageLayoutHelper do
|
|||
|
||||
describe 'page_image' do
|
||||
it 'defaults to the GitLab logo' do
|
||||
expect(helper.page_image).to end_with 'assets/gitlab_logo.png'
|
||||
expect(helper.page_image).to match_asset_path 'assets/gitlab_logo.png'
|
||||
end
|
||||
|
||||
%w(project user group).each do |type|
|
||||
|
@ -70,13 +70,13 @@ describe PageLayoutHelper do
|
|||
object = double(avatar_url: nil)
|
||||
assign(type, object)
|
||||
|
||||
expect(helper.page_image).to end_with 'assets/gitlab_logo.png'
|
||||
expect(helper.page_image).to match_asset_path 'assets/gitlab_logo.png'
|
||||
end
|
||||
end
|
||||
|
||||
context "with no assignments" do
|
||||
it 'falls back to the default' do
|
||||
expect(helper.page_image).to end_with 'assets/gitlab_logo.png'
|
||||
expect(helper.page_image).to match_asset_path 'assets/gitlab_logo.png'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
74
spec/javascripts/locale/sprintf_spec.js
Normal file
74
spec/javascripts/locale/sprintf_spec.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
import sprintf from '~/locale/sprintf';
|
||||
|
||||
describe('locale', () => {
|
||||
describe('sprintf', () => {
|
||||
it('does not modify string without parameters', () => {
|
||||
const input = 'No parameters';
|
||||
|
||||
const output = sprintf(input);
|
||||
|
||||
expect(output).toBe(input);
|
||||
});
|
||||
|
||||
it('ignores extraneous parameters', () => {
|
||||
const input = 'No parameters';
|
||||
|
||||
const output = sprintf(input, { ignore: 'this' });
|
||||
|
||||
expect(output).toBe(input);
|
||||
});
|
||||
|
||||
it('ignores extraneous placeholders', () => {
|
||||
const input = 'No %{parameters}';
|
||||
|
||||
const output = sprintf(input);
|
||||
|
||||
expect(output).toBe(input);
|
||||
});
|
||||
|
||||
it('replaces parameters', () => {
|
||||
const input = '%{name} has %{count} parameters';
|
||||
const parameters = {
|
||||
name: 'this',
|
||||
count: 2,
|
||||
};
|
||||
|
||||
const output = sprintf(input, parameters);
|
||||
|
||||
expect(output).toBe('this has 2 parameters');
|
||||
});
|
||||
|
||||
it('replaces multiple occurrences', () => {
|
||||
const input = 'to %{verb} or not to %{verb}';
|
||||
const parameters = {
|
||||
verb: 'be',
|
||||
};
|
||||
|
||||
const output = sprintf(input, parameters);
|
||||
|
||||
expect(output).toBe('to be or not to be');
|
||||
});
|
||||
|
||||
it('escapes parameters', () => {
|
||||
const input = 'contains %{userContent}';
|
||||
const parameters = {
|
||||
userContent: '<script>alert("malicious!")</script>',
|
||||
};
|
||||
|
||||
const output = sprintf(input, parameters);
|
||||
|
||||
expect(output).toBe('contains <script>alert("malicious!")</script>');
|
||||
});
|
||||
|
||||
it('does not escape parameters for escapeParameters = false', () => {
|
||||
const input = 'contains %{safeContent}';
|
||||
const parameters = {
|
||||
safeContent: '<strong>bold attempt</strong>',
|
||||
};
|
||||
|
||||
const output = sprintf(input, parameters, false);
|
||||
|
||||
expect(output).toBe('contains <strong>bold attempt</strong>');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -195,6 +195,32 @@ describe Gitlab::Ci::Ansi2html do
|
|||
end
|
||||
end
|
||||
|
||||
context "with section markers" do
|
||||
let(:section_name) { 'test_section' }
|
||||
let(:section_start_time) { Time.new(2017, 9, 20).utc }
|
||||
let(:section_duration) { 3.seconds }
|
||||
let(:section_end_time) { section_start_time + section_duration }
|
||||
let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
|
||||
let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
|
||||
let(:section_start_html) do
|
||||
'<div class="hidden" data-action="start"'\
|
||||
" data-timestamp=\"#{section_start_time.to_i}\" data-section=\"#{section_name}\">"\
|
||||
"#{section_start[0...-5]}</div>"
|
||||
end
|
||||
let(:section_end_html) do
|
||||
'<div class="hidden" data-action="end"'\
|
||||
" data-timestamp=\"#{section_end_time.to_i}\" data-section=\"#{section_name}\">"\
|
||||
"#{section_end[0...-5]}</div>"
|
||||
end
|
||||
|
||||
it "prints light red" do
|
||||
text = "#{section_start}\e[91mHello\e[0m\n#{section_end}"
|
||||
html = %{#{section_start_html}<span class="term-fg-l-red">Hello</span><br>#{section_end_html}}
|
||||
|
||||
expect(convert_html(text)).to eq(html)
|
||||
end
|
||||
end
|
||||
|
||||
describe "truncates" do
|
||||
let(:text) { "Hello World" }
|
||||
let(:stream) { StringIO.new(text) }
|
||||
|
|
|
@ -8,6 +8,20 @@ describe Gitlab::Git::User do
|
|||
|
||||
subject { described_class.new(username, name, email, gl_id) }
|
||||
|
||||
describe '.from_gitaly' do
|
||||
let(:gitaly_user) { Gitaly::User.new(name: name, email: email, gl_id: gl_id) }
|
||||
subject { described_class.from_gitaly(gitaly_user) }
|
||||
|
||||
it { expect(subject).to eq(described_class.new('', name, email, gl_id)) }
|
||||
end
|
||||
|
||||
describe '.from_gitlab' do
|
||||
let(:user) { build(:user) }
|
||||
subject { described_class.from_gitlab(user) }
|
||||
|
||||
it { expect(subject).to eq(described_class.new(user.username, user.name, user.email, 'user-')) }
|
||||
end
|
||||
|
||||
describe '#==' do
|
||||
def eq_other(username, name, email, gl_id)
|
||||
eq(described_class.new(username, name, email, gl_id))
|
||||
|
|
|
@ -84,9 +84,9 @@ describe Gitlab::PathRegex do
|
|||
let(:top_level_words) do
|
||||
words = routes_not_starting_in_wildcard.map do |route|
|
||||
route.split('/')[1]
|
||||
end.compact.uniq
|
||||
end.compact
|
||||
|
||||
words + ee_top_level_words + files_in_public + Array(API::API.prefix.to_s)
|
||||
(words + ee_top_level_words + files_in_public + Array(API::API.prefix.to_s)).uniq
|
||||
end
|
||||
|
||||
let(:ee_top_level_words) do
|
||||
|
@ -95,10 +95,11 @@ describe Gitlab::PathRegex do
|
|||
|
||||
let(:files_in_public) do
|
||||
git = Gitlab.config.git.bin_path
|
||||
`cd #{Rails.root} && #{git} ls-files public`
|
||||
tracked = `cd #{Rails.root} && #{git} ls-files public`
|
||||
.split("\n")
|
||||
.map { |entry| entry.gsub('public/', '') }
|
||||
.uniq
|
||||
tracked + %w(assets uploads)
|
||||
end
|
||||
|
||||
# All routes that start with a namespaced path, that have 1 or more
|
||||
|
|
|
@ -14,7 +14,7 @@ describe 'Gitlab::Popen' do
|
|||
end
|
||||
|
||||
it { expect(@status).to be_zero }
|
||||
it { expect(@output).to include('cache') }
|
||||
it { expect(@output).to include('tests') }
|
||||
end
|
||||
|
||||
context 'non-zero status' do
|
||||
|
|
|
@ -39,7 +39,8 @@ describe Gitlab::UrlSanitizer do
|
|||
false | nil
|
||||
false | ''
|
||||
false | '123://invalid:url'
|
||||
true | 'valid@project:url.git'
|
||||
false | 'valid@project:url.git'
|
||||
false | 'valid:pass@project:url.git'
|
||||
true | 'ssh://example.com'
|
||||
true | 'ssh://:@example.com'
|
||||
true | 'ssh://foo@example.com'
|
||||
|
@ -81,24 +82,6 @@ describe Gitlab::UrlSanitizer do
|
|||
|
||||
describe '#credentials' do
|
||||
context 'credentials in hash' do
|
||||
where(:input, :output) do
|
||||
{ user: 'foo', password: 'bar' } | { user: 'foo', password: 'bar' }
|
||||
{ user: 'foo', password: '' } | { user: 'foo', password: nil }
|
||||
{ user: 'foo', password: nil } | { user: 'foo', password: nil }
|
||||
{ user: '', password: 'bar' } | { user: nil, password: 'bar' }
|
||||
{ user: '', password: '' } | { user: nil, password: nil }
|
||||
{ user: '', password: nil } | { user: nil, password: nil }
|
||||
{ user: nil, password: 'bar' } | { user: nil, password: 'bar' }
|
||||
{ user: nil, password: '' } | { user: nil, password: nil }
|
||||
{ user: nil, password: nil } | { user: nil, password: nil }
|
||||
end
|
||||
|
||||
with_them do
|
||||
subject { described_class.new('user@example.com:path.git', credentials: input).credentials }
|
||||
|
||||
it { is_expected.to eq(output) }
|
||||
end
|
||||
|
||||
it 'overrides URL-provided credentials' do
|
||||
sanitizer = described_class.new('http://a:b@example.com', credentials: { user: 'c', password: 'd' })
|
||||
|
||||
|
@ -116,10 +99,6 @@ describe Gitlab::UrlSanitizer do
|
|||
'http://@example.com' | { user: nil, password: nil }
|
||||
'http://example.com' | { user: nil, password: nil }
|
||||
|
||||
# Credentials from SCP-style URLs are not supported at present
|
||||
'foo@example.com:path' | { user: nil, password: nil }
|
||||
'foo:bar@example.com:path' | { user: nil, password: nil }
|
||||
|
||||
# Other invalid URLs
|
||||
nil | { user: nil, password: nil }
|
||||
'' | { user: nil, password: nil }
|
||||
|
|
|
@ -6,13 +6,10 @@ describe ProjectWiki do
|
|||
let(:user) { project.owner }
|
||||
let(:gitlab_shell) { Gitlab::Shell.new }
|
||||
let(:project_wiki) { described_class.new(project, user) }
|
||||
let(:raw_repository) { Gitlab::Git::Repository.new(project.repository_storage, subject.disk_path + '.git', 'foo') }
|
||||
|
||||
subject { project_wiki }
|
||||
|
||||
before do
|
||||
project_wiki.wiki
|
||||
end
|
||||
|
||||
describe "#path_with_namespace" do
|
||||
it "returns the project path with namespace with the .wiki extension" do
|
||||
expect(subject.path_with_namespace).to eq(project.full_path + '.wiki')
|
||||
|
@ -61,8 +58,8 @@ describe ProjectWiki do
|
|||
end
|
||||
|
||||
describe "#wiki" do
|
||||
it "contains a Gollum::Wiki instance" do
|
||||
expect(subject.wiki).to be_a Gollum::Wiki
|
||||
it "contains a Gitlab::Git::Wiki instance" do
|
||||
expect(subject.wiki).to be_a Gitlab::Git::Wiki
|
||||
end
|
||||
|
||||
it "creates a new wiki repo if one does not yet exist" do
|
||||
|
@ -70,20 +67,18 @@ describe ProjectWiki do
|
|||
end
|
||||
|
||||
it "raises CouldNotCreateWikiError if it can't create the wiki repository" do
|
||||
allow(project_wiki).to receive(:init_repo).and_return(false)
|
||||
expect { project_wiki.send(:create_repo!) }.to raise_exception(ProjectWiki::CouldNotCreateWikiError)
|
||||
# Create a fresh project which will not have a wiki
|
||||
project_wiki = described_class.new(create(:project), user)
|
||||
gitlab_shell = double(:gitlab_shell)
|
||||
allow(gitlab_shell).to receive(:add_repository)
|
||||
allow(project_wiki).to receive(:gitlab_shell).and_return(gitlab_shell)
|
||||
|
||||
expect { project_wiki.send(:wiki) }.to raise_exception(ProjectWiki::CouldNotCreateWikiError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#empty?" do
|
||||
context "when the wiki repository is empty" do
|
||||
before do
|
||||
allow_any_instance_of(Gitlab::Shell).to receive(:add_repository) do
|
||||
create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git")
|
||||
end
|
||||
allow(project).to receive(:full_path).and_return("non-existant")
|
||||
end
|
||||
|
||||
describe '#empty?' do
|
||||
subject { super().empty? }
|
||||
it { is_expected.to be_truthy }
|
||||
|
@ -154,13 +149,13 @@ describe ProjectWiki do
|
|||
before do
|
||||
file = Gollum::File.new(subject.wiki)
|
||||
allow_any_instance_of(Gollum::Wiki)
|
||||
.to receive(:file).with('image.jpg', 'master', true)
|
||||
.to receive(:file).with('image.jpg', 'master')
|
||||
.and_return(file)
|
||||
allow_any_instance_of(Gollum::File)
|
||||
.to receive(:mime_type)
|
||||
.and_return('image/jpeg')
|
||||
allow_any_instance_of(Gollum::Wiki)
|
||||
.to receive(:file).with('non-existant', 'master', true)
|
||||
.to receive(:file).with('non-existant', 'master')
|
||||
.and_return(nil)
|
||||
end
|
||||
|
||||
|
@ -178,9 +173,9 @@ describe ProjectWiki do
|
|||
expect(subject.find_file('non-existant')).to eq(nil)
|
||||
end
|
||||
|
||||
it 'returns a Gollum::File instance' do
|
||||
it 'returns a Gitlab::Git::WikiFile instance' do
|
||||
file = subject.find_file('image.jpg')
|
||||
expect(file).to be_a Gollum::File
|
||||
expect(file).to be_a Gitlab::Git::WikiFile
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -222,9 +217,9 @@ describe ProjectWiki do
|
|||
describe "#update_page" do
|
||||
before do
|
||||
create_page("update-page", "some content")
|
||||
@gollum_page = subject.wiki.paged("update-page")
|
||||
@gitlab_git_wiki_page = subject.wiki.page(title: "update-page")
|
||||
subject.update_page(
|
||||
@gollum_page,
|
||||
@gitlab_git_wiki_page,
|
||||
content: "some other content",
|
||||
format: :markdown,
|
||||
message: "updated page"
|
||||
|
@ -246,7 +241,7 @@ describe ProjectWiki do
|
|||
|
||||
it 'updates project activity' do
|
||||
subject.update_page(
|
||||
@gollum_page,
|
||||
@gitlab_git_wiki_page,
|
||||
content: 'Yet more content',
|
||||
format: :markdown,
|
||||
message: 'Updated page again'
|
||||
|
@ -262,7 +257,7 @@ describe ProjectWiki do
|
|||
describe "#delete_page" do
|
||||
before do
|
||||
create_page("index", "some content")
|
||||
@page = subject.wiki.paged("index")
|
||||
@page = subject.wiki.page(title: "index")
|
||||
end
|
||||
|
||||
it "deletes the page" do
|
||||
|
@ -282,27 +277,28 @@ describe ProjectWiki do
|
|||
|
||||
describe '#create_repo!' do
|
||||
it 'creates a repository' do
|
||||
expect(subject).to receive(:init_repo)
|
||||
.with(subject.full_path)
|
||||
.and_return(true)
|
||||
|
||||
expect(raw_repository.exists?).to eq(false)
|
||||
expect(subject.repository).to receive(:after_create)
|
||||
|
||||
expect(subject.create_repo!).to be_an_instance_of(Gollum::Wiki)
|
||||
subject.send(:create_repo!, raw_repository)
|
||||
|
||||
expect(raw_repository.exists?).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ensure_repository' do
|
||||
it 'creates the repository if it not exist' do
|
||||
allow(subject).to receive(:repository_exists?).and_return(false)
|
||||
|
||||
expect(subject).to receive(:create_repo!)
|
||||
expect(raw_repository.exists?).to eq(false)
|
||||
|
||||
expect(subject).to receive(:create_repo!).and_call_original
|
||||
subject.ensure_repository
|
||||
|
||||
expect(raw_repository.exists?).to eq(true)
|
||||
end
|
||||
|
||||
it 'does not create the repository if it exists' do
|
||||
allow(subject).to receive(:repository_exists?).and_return(true)
|
||||
subject.wiki
|
||||
expect(raw_repository.exists?).to eq(true)
|
||||
|
||||
expect(subject).not_to receive(:create_repo!)
|
||||
|
||||
|
@ -329,7 +325,7 @@ describe ProjectWiki do
|
|||
end
|
||||
|
||||
def commit_details
|
||||
{ name: user.name, email: user.email, message: "test commit" }
|
||||
Gitlab::Git::Wiki::CommitDetails.new(user.name, user.email, "test commit")
|
||||
end
|
||||
|
||||
def create_page(name, content)
|
||||
|
@ -337,6 +333,6 @@ describe ProjectWiki do
|
|||
end
|
||||
|
||||
def destroy_page(page)
|
||||
subject.wiki.delete_page(page, commit_details)
|
||||
subject.delete_page(page, commit_details)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -80,7 +80,7 @@ describe WikiPage do
|
|||
context "when initialized with an existing gollum page" do
|
||||
before do
|
||||
create_page("test page", "test content")
|
||||
@page = wiki.wiki.paged("test page")
|
||||
@page = wiki.wiki.page(title: "test page")
|
||||
@wiki_page = described_class.new(wiki, @page, true)
|
||||
end
|
||||
|
||||
|
@ -105,7 +105,7 @@ describe WikiPage do
|
|||
end
|
||||
|
||||
it "sets the version attribute" do
|
||||
expect(@wiki_page.version).to be_a Gollum::Git::Commit
|
||||
expect(@wiki_page.version).to be_a Gitlab::Git::WikiPageVersion
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -321,14 +321,14 @@ describe WikiPage do
|
|||
end
|
||||
|
||||
it 'returns true when requesting an old version' do
|
||||
old_version = @page.versions.last.to_s
|
||||
old_version = @page.versions.last.id
|
||||
old_page = wiki.find_page('Update', old_version)
|
||||
|
||||
expect(old_page.historical?).to eq true
|
||||
end
|
||||
|
||||
it 'returns false when requesting latest version' do
|
||||
latest_version = @page.versions.first.to_s
|
||||
latest_version = @page.versions.first.id
|
||||
latest_page = wiki.find_page('Update', latest_version)
|
||||
|
||||
expect(latest_page.historical?).to eq false
|
||||
|
@ -393,7 +393,7 @@ describe WikiPage do
|
|||
end
|
||||
|
||||
def commit_details
|
||||
{ name: user.name, email: user.email, message: "test commit" }
|
||||
Gitlab::Git::Wiki::CommitDetails.new(user.name, user.email, "test commit")
|
||||
end
|
||||
|
||||
def create_page(name, content)
|
||||
|
@ -401,8 +401,8 @@ describe WikiPage do
|
|||
end
|
||||
|
||||
def destroy_page(title)
|
||||
page = wiki.wiki.paged(title)
|
||||
wiki.wiki.delete_page(page, commit_details)
|
||||
page = wiki.wiki.page(title: title)
|
||||
wiki.delete_page(page, commit_details)
|
||||
end
|
||||
|
||||
def get_slugs(page_or_dir)
|
||||
|
|
|
@ -360,6 +360,8 @@ describe API::Runner do
|
|||
'policy' => 'pull-push' }]
|
||||
end
|
||||
|
||||
let(:expected_features) { { 'trace_sections' => true } }
|
||||
|
||||
it 'picks a job' do
|
||||
request_job info: { platform: :darwin }
|
||||
|
||||
|
@ -379,6 +381,7 @@ describe API::Runner do
|
|||
expect(json_response['artifacts']).to eq(expected_artifacts)
|
||||
expect(json_response['cache']).to eq(expected_cache)
|
||||
expect(json_response['variables']).to include(*expected_variables)
|
||||
expect(json_response['features']).to eq(expected_features)
|
||||
end
|
||||
|
||||
context 'when job is made for tag' do
|
||||
|
|
|
@ -38,7 +38,7 @@ describe BuildSerializer do
|
|||
expect(subject[:text]).to eq(status.text)
|
||||
expect(subject[:label]).to eq(status.label)
|
||||
expect(subject[:icon]).to eq(status.icon)
|
||||
expect(subject[:favicon]).to eq("/assets/ci_favicons/#{status.favicon}.ico")
|
||||
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -168,7 +168,7 @@ describe PipelineSerializer do
|
|||
expect(subject[:text]).to eq(status.text)
|
||||
expect(subject[:label]).to eq(status.label)
|
||||
expect(subject[:icon]).to eq(status.icon)
|
||||
expect(subject[:favicon]).to eq("/assets/ci_favicons/#{status.favicon}.ico")
|
||||
expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,12 +18,12 @@ describe StatusEntity do
|
|||
it 'contains status details' do
|
||||
expect(subject).to include :text, :icon, :favicon, :label, :group
|
||||
expect(subject).to include :has_details, :details_path
|
||||
expect(subject[:favicon]).to eq('/assets/ci_favicons/favicon_status_success.ico')
|
||||
expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico')
|
||||
end
|
||||
|
||||
it 'contains a dev namespaced favicon if dev env' do
|
||||
allow(Rails.env).to receive(:development?) { true }
|
||||
expect(entity.as_json[:favicon]).to eq('/assets/ci_favicons/dev/favicon_status_success.ico')
|
||||
expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/dev/favicon_status_success.ico')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -76,9 +76,8 @@ describe Projects::CreateService, '#execute' do
|
|||
context 'wiki_enabled true creates wiki repository directory' do
|
||||
it do
|
||||
project = create_project(user, opts)
|
||||
path = ProjectWiki.new(project, user).send(:path_to_repo)
|
||||
|
||||
expect(File.exist?(path)).to be_truthy
|
||||
expect(wiki_repo(project).exists?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -86,11 +85,15 @@ describe Projects::CreateService, '#execute' do
|
|||
it do
|
||||
opts[:wiki_enabled] = false
|
||||
project = create_project(user, opts)
|
||||
path = ProjectWiki.new(project, user).send(:path_to_repo)
|
||||
|
||||
expect(File.exist?(path)).to be_falsey
|
||||
expect(wiki_repo(project).exists?).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
def wiki_repo(project)
|
||||
relative_path = ProjectWiki.new(project).disk_path + '.git'
|
||||
Gitlab::Git::Repository.new(project.repository_storage, relative_path, 'foobar')
|
||||
end
|
||||
end
|
||||
|
||||
context 'builds_enabled global setting' do
|
||||
|
|
|
@ -169,6 +169,24 @@ RSpec.configure do |config|
|
|||
end
|
||||
end
|
||||
|
||||
# add simpler way to match asset paths containing digest strings
|
||||
RSpec::Matchers.define :match_asset_path do |expected|
|
||||
match do |actual|
|
||||
path = Regexp.escape(expected)
|
||||
extname = Regexp.escape(File.extname(expected))
|
||||
digest_regex = Regexp.new(path.sub(extname, "(?:-\\h+)?#{extname}") << '$')
|
||||
digest_regex =~ actual
|
||||
end
|
||||
|
||||
failure_message do |actual|
|
||||
"expected that #{actual} would include an asset path for #{expected}"
|
||||
end
|
||||
|
||||
failure_message_when_negated do |actual|
|
||||
"expected that #{actual} would not include an asset path for #{expected}"
|
||||
end
|
||||
end
|
||||
|
||||
FactoryGirl::SyntaxRunner.class_eval do
|
||||
include RSpec::Mocks::ExampleMethods
|
||||
end
|
||||
|
|
|
@ -69,7 +69,12 @@ describe RepositoryCheck::SingleRepositoryWorker do
|
|||
end
|
||||
|
||||
def break_wiki(project)
|
||||
FileUtils.rm_rf(wiki_path(project) + '/objects')
|
||||
objects_dir = wiki_path(project) + '/objects'
|
||||
|
||||
# Replace the /objects directory with a file so that the repo is
|
||||
# invalid, _and_ 'git init' cannot fix it.
|
||||
FileUtils.rm_rf(objects_dir)
|
||||
FileUtils.touch(objects_dir) if File.directory?(wiki_path(project))
|
||||
end
|
||||
|
||||
def wiki_path(project)
|
||||
|
|
Loading…
Reference in a new issue