Commit 356e9aa8 authored by Mike Greiling's avatar Mike Greiling

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
  ...
parents f682cc18 5a23af92
...@@ -105,7 +105,7 @@ gem 'fog-rackspace', '~> 0.1.1' ...@@ -105,7 +105,7 @@ gem 'fog-rackspace', '~> 0.1.1'
gem 'fog-aliyun', '~> 0.1.0' gem 'fog-aliyun', '~> 0.1.0'
# for Google storage # for Google storage
gem 'google-api-client', '~> 0.8.6' gem 'google-api-client', '~> 0.13.6'
# for aws storage # for aws storage
gem 'unf', '~> 0.1.4' gem 'unf', '~> 0.1.4'
...@@ -239,7 +239,7 @@ gem 'rack-proxy', '~> 0.6.0' ...@@ -239,7 +239,7 @@ gem 'rack-proxy', '~> 0.6.0'
gem 'sass-rails', '~> 5.0.6' gem 'sass-rails', '~> 5.0.6'
gem 'uglifier', '~> 2.7.2' gem 'uglifier', '~> 2.7.2'
gem 'addressable', '~> 2.3.8' gem 'addressable', '~> 2.5.2'
gem 'bootstrap-sass', '~> 3.3.0' gem 'bootstrap-sass', '~> 3.3.0'
gem 'font-awesome-rails', '~> 4.7' gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.3' gem 'gemojione', '~> 3.3'
...@@ -356,7 +356,7 @@ end ...@@ -356,7 +356,7 @@ end
group :test do group :test do
gem 'shoulda-matchers', '~> 3.1.2', require: false gem 'shoulda-matchers', '~> 3.1.2', require: false
gem 'email_spec', '~> 1.6.0' gem 'email_spec', '~> 1.6.0'
gem 'json-schema', '~> 2.6.2' gem 'json-schema', '~> 2.8.0'
gem 'webmock', '~> 2.3.2' gem 'webmock', '~> 2.3.2'
gem 'test_after_commit', '~> 1.1' gem 'test_after_commit', '~> 1.1'
gem 'sham_rack', '~> 1.3.6' gem 'sham_rack', '~> 1.3.6'
......
...@@ -45,7 +45,8 @@ GEM ...@@ -45,7 +45,8 @@ GEM
adamantium (0.2.0) adamantium (0.2.0)
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
memoizable (~> 0.4.0) memoizable (~> 0.4.0)
addressable (2.3.8) addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
akismet (2.0.0) akismet (2.0.0)
allocations (1.0.5) allocations (1.0.5)
arel (6.0.4) arel (6.0.4)
...@@ -62,10 +63,6 @@ GEM ...@@ -62,10 +63,6 @@ GEM
attr_encrypted (3.0.3) attr_encrypted (3.0.3)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.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) autoprefixer-rails (6.2.3)
execjs execjs
json json
...@@ -147,6 +144,8 @@ GEM ...@@ -147,6 +144,8 @@ GEM
debugger-ruby_core_source (1.3.8) debugger-ruby_core_source (1.3.8)
deckar01-task_list (2.0.0) deckar01-task_list (2.0.0)
html-pipeline html-pipeline
declarative (0.0.10)
declarative-option (0.1.0)
default_value_for (3.0.2) default_value_for (3.0.2)
activerecord (>= 3.2.0, < 5.1) activerecord (>= 3.2.0, < 5.1)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
...@@ -189,7 +188,6 @@ GEM ...@@ -189,7 +188,6 @@ GEM
excon (0.57.1) excon (0.57.1)
execjs (2.6.0) execjs (2.6.0)
expression_parser (0.9.0) expression_parser (0.9.0)
extlib (0.9.16)
factory_girl (4.7.0) factory_girl (4.7.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
factory_girl_rails (4.7.0) factory_girl_rails (4.7.0)
...@@ -289,10 +287,10 @@ GEM ...@@ -289,10 +287,10 @@ GEM
flowdock (~> 0.7) flowdock (~> 0.7)
gitlab-grit (>= 2.4.1) gitlab-grit (>= 2.4.1)
multi_json multi_json
gitlab-grit (2.8.1) gitlab-grit (2.8.2)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3) mime-types (>= 1.16)
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab-markup (1.6.2) gitlab-markup (1.6.2)
gitlab_omniauth-ldap (2.0.4) gitlab_omniauth-ldap (2.0.4)
...@@ -320,20 +318,16 @@ GEM ...@@ -320,20 +318,16 @@ GEM
json json
multi_json multi_json
request_store (>= 1.0) request_store (>= 1.0)
google-api-client (0.8.7) google-api-client (0.13.6)
activesupport (>= 3.2, < 5.0) addressable (~> 2.5, >= 2.5.1)
addressable (~> 2.3) googleauth (~> 0.5)
autoparse (~> 0.3) httpclient (>= 2.8.1, < 3.0)
extlib (~> 0.9) mime-types (~> 3.0)
faraday (~> 0.9) representable (~> 3.0)
googleauth (~> 0.3) retriable (>= 2.0, < 4.0)
launchy (~> 2.4)
multi_json (~> 1.10)
retriable (~> 1.4)
signet (~> 0.6)
google-protobuf (3.4.0.2) google-protobuf (3.4.0.2)
googleauth (0.5.1) googleauth (0.5.3)
faraday (~> 0.9) faraday (~> 0.12)
jwt (~> 1.4) jwt (~> 1.4)
logging (~> 2.0) logging (~> 2.0)
memoist (~> 0.12) memoist (~> 0.12)
...@@ -423,8 +417,8 @@ GEM ...@@ -423,8 +417,8 @@ GEM
multi_json (>= 1.3) multi_json (>= 1.3)
securecompare securecompare
url_safe_base64 url_safe_base64
json-schema (2.6.2) json-schema (2.8.0)
addressable (~> 2.3.8) addressable (>= 2.4)
jwt (1.5.6) jwt (1.5.6)
kaminari (1.0.1) kaminari (1.0.1)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
...@@ -476,18 +470,20 @@ GEM ...@@ -476,18 +470,20 @@ GEM
mail (2.6.6) mail (2.6.6)
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
mail_room (0.9.1) mail_room (0.9.1)
memoist (0.15.0) memoist (0.16.0)
memoizable (0.4.2) memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
method_source (0.8.2) 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) mimemagic (0.3.0)
mini_mime (0.1.4) mini_mime (0.1.4)
mini_portile2 (2.3.0) mini_portile2 (2.3.0)
minitest (5.7.0) minitest (5.7.0)
mmap2 (2.2.7) mmap2 (2.2.7)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
multi_json (1.12.1) multi_json (1.12.2)
multi_xml (0.6.0) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
mustermann (1.0.0) mustermann (1.0.0)
...@@ -631,6 +627,7 @@ GEM ...@@ -631,6 +627,7 @@ GEM
pry (~> 0.10) pry (~> 0.10)
pry-rails (0.3.5) pry-rails (0.3.5)
pry (>= 0.9.10) pry (>= 0.9.10)
public_suffix (3.0.0)
pyu-ruby-sasl (0.0.3.3) pyu-ruby-sasl (0.0.3.3)
rack (1.6.8) rack (1.6.8)
rack-accept (0.4.5) rack-accept (0.4.5)
...@@ -713,6 +710,10 @@ GEM ...@@ -713,6 +710,10 @@ GEM
redis-store (~> 1.2.0) redis-store (~> 1.2.0)
redis-store (1.2.0) redis-store (1.2.0)
redis (>= 2.2) 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) request_store (1.3.1)
responders (2.3.0) responders (2.3.0)
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
...@@ -720,7 +721,7 @@ GEM ...@@ -720,7 +721,7 @@ GEM
http-cookie (>= 1.0.2, < 2.0) http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0) mime-types (>= 1.16, < 4.0)
netrc (~> 0.8) netrc (~> 0.8)
retriable (1.4.1) retriable (3.1.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (2.2.1) rouge (2.2.1)
...@@ -902,6 +903,7 @@ GEM ...@@ -902,6 +903,7 @@ GEM
tzinfo (1.2.3) tzinfo (1.2.3)
thread_safe (~> 0.1) thread_safe (~> 0.1)
u2f (0.2.1) u2f (0.2.1)
uber (0.1.0)
uglifier (2.7.2) uglifier (2.7.2)
execjs (>= 0.3.0) execjs (>= 0.3.0)
json (>= 1.8.0) json (>= 1.8.0)
...@@ -959,7 +961,7 @@ DEPENDENCIES ...@@ -959,7 +961,7 @@ DEPENDENCIES
ace-rails-ap (~> 4.1.0) ace-rails-ap (~> 4.1.0)
activerecord_sane_schema_dumper (= 0.2) activerecord_sane_schema_dumper (= 0.2)
acts-as-taggable-on (~> 4.0) acts-as-taggable-on (~> 4.0)
addressable (~> 2.3.8) addressable (~> 2.5.2)
akismet (~> 2.0) akismet (~> 2.0)
allocations (~> 1.0) allocations (~> 1.0)
asana (~> 0.6.0) asana (~> 0.6.0)
...@@ -1029,7 +1031,7 @@ DEPENDENCIES ...@@ -1029,7 +1031,7 @@ DEPENDENCIES
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.4) gollum-rugged_adapter (~> 0.4.4)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.13.6)
gpgme gpgme
grape (~> 1.0) grape (~> 1.0)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
...@@ -1047,7 +1049,7 @@ DEPENDENCIES ...@@ -1047,7 +1049,7 @@ DEPENDENCIES
jira-ruby (~> 1.4) jira-ruby (~> 1.4)
jquery-atwho-rails (~> 1.3.2) jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 4.1.0) jquery-rails (~> 4.1.0)
json-schema (~> 2.6.2) json-schema (~> 2.8.0)
jwt (~> 1.5.6) jwt (~> 1.5.6)
kaminari (~> 1.0) kaminari (~> 1.0)
knapsack (~> 1.11.0) knapsack (~> 1.11.0)
......
...@@ -197,6 +197,11 @@ month. When we say 'the most recent monthly release', this can refer to either ...@@ -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 the version currently running on GitLab.com, or the most recent version
available in the package repositories. 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 ## Release retrospective and kickoff
- [Retrospective](https://about.gitlab.com/handbook/engineering/workflow/#retrospective) - [Retrospective](https://about.gitlab.com/handbook/engineering/workflow/#retrospective)
......
...@@ -548,6 +548,7 @@ GitLabDropdown = (function() { ...@@ -548,6 +548,7 @@ GitLabDropdown = (function() {
GitLabDropdown.prototype.positionMenuAbove = function() { GitLabDropdown.prototype.positionMenuAbove = function() {
var $menu = this.dropdown.find('.dropdown-menu'); var $menu = this.dropdown.find('.dropdown-menu');
$menu.addClass('dropdown-open-top');
$menu.css('top', 'initial'); $menu.css('top', 'initial');
$menu.css('bottom', '100%'); $menu.css('bottom', '100%');
}; };
......
import Jed from 'jed'; import Jed from 'jed';
import sprintf from './sprintf';
/** /**
This is required to require all the translation folders in the current directory 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 this saves us having to do this manually & keep up to date with new languages
...@@ -66,4 +68,5 @@ export { lang }; ...@@ -66,4 +68,5 @@ export { lang };
export { gettext as __ }; export { gettext as __ };
export { ngettext as n__ }; export { ngettext as n__ };
export { pgettext as s__ }; export { pgettext as s__ };
export { sprintf };
export default locale; export default locale;
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 { ...@@ -2,6 +2,7 @@ import {
__, __,
n__, n__,
s__, s__,
sprintf,
} from '../locale'; } from '../locale';
export default (Vue) => { export default (Vue) => {
...@@ -37,6 +38,7 @@ export default (Vue) => { ...@@ -37,6 +38,7 @@ export default (Vue) => {
@returns {String} Translated context based text @returns {String} Translated context based text
**/ **/
s__, s__,
sprintf,
}, },
}); });
}; };
...@@ -745,6 +745,10 @@ ...@@ -745,6 +745,10 @@
#{$selector}.dropdown-menu-nav { #{$selector}.dropdown-menu-nav {
margin-bottom: 24px; margin-bottom: 24px;
&.dropdown-open-top {
margin-bottom: $dropdown-vertical-offset;
}
li { li {
display: block; display: block;
padding: 0 1px; padding: 0 1px;
......
...@@ -295,7 +295,7 @@ header.navbar-gitlab-new { ...@@ -295,7 +295,7 @@ header.navbar-gitlab-new {
.header-user .dropdown-menu-nav, .header-user .dropdown-menu-nav,
.header-new .dropdown-menu-nav { .header-new .dropdown-menu-nav {
margin-top: 4px; margin-top: $dropdown-vertical-offset;
} }
.breadcrumbs { .breadcrumbs {
......
...@@ -327,6 +327,7 @@ $regular_font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-San ...@@ -327,6 +327,7 @@ $regular_font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-San
* Dropdowns * Dropdowns
*/ */
$dropdown-width: 300px; $dropdown-width: 300px;
$dropdown-vertical-offset: 4px;
$dropdown-link-color: #555; $dropdown-link-color: #555;
$dropdown-link-hover-bg: $row-hover; $dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, .04); $dropdown-empty-row-bg: rgba(#000, .04);
......
...@@ -362,7 +362,7 @@ ...@@ -362,7 +362,7 @@
.dropdown-menu { .dropdown-menu {
top: initial; top: initial;
bottom: 40px; bottom: 100%;
width: 298px; width: 298px;
} }
......
...@@ -18,16 +18,12 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -18,16 +18,12 @@ class Projects::WikisController < Projects::ApplicationController
response.headers['Content-Security-Policy'] = "default-src 'none'" response.headers['Content-Security-Policy'] = "default-src 'none'"
response.headers['X-Content-Security-Policy'] = "default-src 'none'" response.headers['X-Content-Security-Policy'] = "default-src 'none'"
if file.on_disk? send_data(
send_file file.on_disk_path, disposition: 'inline' file.raw_data,
else type: file.mime_type,
send_data( disposition: 'inline',
file.raw_data, filename: file.name
type: file.mime_type, )
disposition: 'inline',
filename: file.name
)
end
else else
return render('empty') unless can?(current_user, :create_wiki, @project) return render('empty') unless can?(current_user, :create_wiki, @project)
@page = WikiPage.new(@project_wiki) @page = WikiPage.new(@project_wiki)
......
...@@ -229,6 +229,10 @@ module Ci ...@@ -229,6 +229,10 @@ module Ci
variables variables
end end
def features
{ trace_sections: true }
end
def merge_request def merge_request
return @merge_request if defined?(@merge_request) return @merge_request if defined?(@merge_request)
......
...@@ -54,12 +54,15 @@ class ProjectWiki ...@@ -54,12 +54,15 @@ class ProjectWiki
[Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('') [Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('')
end end
# Returns the Gollum::Wiki object. # Returns the Gitlab::Git::Wiki object.
def wiki def wiki
@wiki ||= begin @wiki ||= begin
Gollum::Wiki.new(path_to_repo) gl_repository = Gitlab::GlRepository.gl_repository(project, true)
rescue Rugged::OSError raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository)
create_repo!
create_repo!(raw_repository) unless raw_repository.exists?
Gitlab::Git::Wiki.new(raw_repository)
end end
end end
...@@ -86,20 +89,14 @@ class ProjectWiki ...@@ -86,20 +89,14 @@ class ProjectWiki
# Returns an initialized WikiPage instance or nil # Returns an initialized WikiPage instance or nil
def find_page(title, version = nil) def find_page(title, version = nil)
page_title, page_dir = page_title_and_dir(title) 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) WikiPage.new(self, page, true)
else
nil
end end
end end
def find_file(name, version = nil, try_on_disk = true) def find_file(name, version = nil)
version = wiki.ref if version.nil? # Gollum::Wiki#file ? wiki.file(name, version)
if wiki_file = wiki.file(name, version, try_on_disk)
wiki_file
else
nil
end
end end
def create_page(title, content, format = :markdown, message = nil) def create_page(title, content, format = :markdown, message = nil)
...@@ -108,7 +105,7 @@ class ProjectWiki ...@@ -108,7 +105,7 @@ class ProjectWiki
wiki.write_page(title, format.to_sym, content, commit) wiki.write_page(title, format.to_sym, content, commit)
update_project_activity update_project_activity
rescue Gollum::DuplicatePageError => e rescue Gitlab::Git::Wiki::DuplicatePageError => e
@error_message = "Duplicate page: #{e.message}" @error_message = "Duplicate page: #{e.message}"
return false return false
end end
...@@ -116,13 +113,13 @@ class ProjectWiki ...@@ -116,13 +113,13 @@ class ProjectWiki
def update_page(page, content:, title: nil, format: :markdown, message: nil) def update_page(page, content:, title: nil, format: :markdown, message: nil)
commit = commit_details(:updated, message, page.title) 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 update_project_activity
end end
def delete_page(page, message = nil) 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 update_project_activity
end end
...@@ -145,20 +142,8 @@ class ProjectWiki ...@@ -145,20 +142,8 @@ class ProjectWiki
wiki.class.default_ref wiki.class.default_ref
end 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 def ensure_repository
create_repo! unless repository_exists? raise CouldNotCreateWikiError unless wiki.repository_exists?
end end
def hook_attrs def hook_attrs
...@@ -173,24 +158,24 @@ class ProjectWiki ...@@ -173,24 +158,24 @@ class ProjectWiki
private private
def init_repo(disk_path) def create_repo!(raw_repository)
gitlab_shell.add_repository(project.repository_storage, disk_path) gitlab_shell.add_repository(project.repository_storage, disk_path)
raise CouldNotCreateWikiError unless raw_repository.exists?
repository.after_create
end end
def commit_details(action, message = nil, title = nil) def commit_details(action, message = nil, title = nil)
commit_message = message || default_message(action, title) 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 end
def default_message(action, title) def default_message(action, title)
"#{@user.username} #{action} page: #{title}" "#{@user.username} #{action} page: #{title}"
end end
def path_to_repo
@path_to_repo ||= File.join(project.repository_storage_path, "#{disk_path}.git")
end
def update_project_activity def update_project_activity
@project.touch(:last_activity_at, :last_repository_updated_at) @project.touch(:last_activity_at, :last_repository_updated_at)
end end
......
...@@ -50,7 +50,7 @@ class WikiPage ...@@ -50,7 +50,7 @@ class WikiPage
# The Gitlab ProjectWiki instance. # The Gitlab ProjectWiki instance.
attr_reader :wiki attr_reader :wiki
# The raw Gollum::Page instance. # The raw Gitlab::Git::WikiPage instance.
attr_reader :page attr_reader :page
# The attributes Hash used for storing and validating # The attributes Hash used for storing and validating
...@@ -75,7 +75,7 @@ class WikiPage ...@@ -75,7 +75,7 @@ class WikiPage
if @attributes[:slug].present? if @attributes[:slug].present?
@attributes[:slug] @attributes[:slug]
else else
wiki.wiki.preview_page(title, '', format).url_path wiki.wiki.preview_slug(title, format)
end end
end end
...@@ -131,7 +131,7 @@ class WikiPage ...@@ -131,7 +131,7 @@ class WikiPage
def versions def versions
return [] unless persisted? return [] unless persisted?
@page.versions wiki.wiki.page_versions(@page.path)
end end
def commit def commit
...@@ -264,8 +264,8 @@ class WikiPage ...@@ -264,8 +264,8 @@ class WikiPage
end end
page_title, page_dir = wiki.page_title_and_dir(page_details) page_title, page_dir = wiki.page_title_and_dir(page_details)
gollum_wiki = wiki.wiki gitlab_git_wiki = wiki.wiki
@page = gollum_wiki.paged(page_title, page_dir) @page = gitlab_git_wiki.page(title: page_title, dir: page_dir)
set_attributes set_attributes
@persisted = errors.blank? @persisted = errors.blank?
......
...@@ -29,13 +29,13 @@ ...@@ -29,13 +29,13 @@
commit.id, index == 0) do commit.id, index == 0) do
= truncate_sha(commit.id) = truncate_sha(commit.id)
%td %td
= commit.author.name = commit.author_name
%td %td
= commit.message = commit.message
%td %td
#{time_ago_with_tooltip(version.authored_date)} #{time_ago_with_tooltip(version.authored_date)}
%td %td
%strong %strong
= @page.page.wiki.page(@page.page.name, commit.id).try(:format) = version.format
= render 'sidebar' = render 'sidebar'
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
.nav-text .nav-text
%h2.wiki-page-title= @page.title.capitalize %h2.wiki-page-title= @page.title.capitalize
%span.wiki-last-edit-by %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)} #{time_ago_with_tooltip(@page.commit.authored_date)}
.nav-controls .nav-controls
......
...@@ -7,7 +7,7 @@ ...@@ -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 = 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') = 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}" } } %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 %button.btn.btn-transparent
= icon('check', class: 'icon') = icon('check', class: 'icon')
......
---
title: Strip gitlab-runner section markers in build trace HTML view
merge_request: 14393
author:
type: added
---
title: Fix bottom spacing for dropdowns that open upwards
merge_request: 14535
author:
type: fixed
---
title: Change index on ci_builds to optimize Jobs Controller
merge_request:
author:
type: other
---
title: Bump google-api-client Gem from 0.8.6 to 0.13.6
merge_request:
author:
type: other
---
title: Add basic sprintf implementation to JavaScript
merge_request: 14506
author:
type: other
...@@ -16,7 +16,7 @@ Rails.application.configure do ...@@ -16,7 +16,7 @@ Rails.application.configure do
config.cache_classes = ENV['CACHE_CLASSES'] == 'true' config.cache_classes = ENV['CACHE_CLASSES'] == 'true'
# Configure static asset server for tests with Cache-Control for performance # 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.serve_static_files = true
config.static_cache_control = "public, max-age=3600" config.static_cache_control = "public, max-age=3600"
......
# 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 ...@@ -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", "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", "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", ["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", ["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", ["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 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`. ...@@ -183,13 +183,20 @@ aren't in the message with id `1 pipeline`.
### Interpolation ### Interpolation
- In Ruby/HAML: - In Ruby/HAML (see [sprintf]):
```ruby ```ruby
_("Hello %{name}") % { name: 'Joe' } _("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 ### Plurals
......
...@@ -39,6 +39,12 @@ When information is updating in place, a quick, subtle animation is needed. The ...@@ -39,6 +39,12 @@ When information is updating in place, a quick, subtle animation is needed. The
![Quick update animation](img/animation-quickupdate.gif) ![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 ### 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`. 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. ...@@ -51,7 +57,9 @@ View the [interactive example](http://codepen.io/awhildy/full/ALyKPE/) here.
![Reorder animation](img/animation-reorder.gif) ![Reorder animation](img/animation-reorder.gif)
#### Autoscroll the page #### Autoscroll the page
Another example of a moving transition is when you have to autoscroll the page to keep an active element visible. 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. View the [interactive example](http://codepen.io/awhildy/full/PbxgVo/) here.
![Autoscroll animation](img/animation-autoscroll.gif)
\ No newline at end of file ![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 ...@@ -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 ## Panels
> TODO: Catalog how we are currently using panels and rationalize how they relate to alerts > TODO: Catalog how we are currently using panels and rationalize how they relate to alerts
......
...@@ -1022,6 +1022,7 @@ module API ...@@ -1022,6 +1022,7 @@ module API
expose :cache, using: Cache expose :cache, using: Cache
expose :credentials, using: Credentials expose :credentials, using: Credentials
expose :dependencies, using: Dependency expose :dependencies, using: Dependency
expose :features
end end
end end
......
...@@ -73,8 +73,9 @@ module Banzai ...@@ -73,8 +73,9 @@ module Banzai
return unless node.has_attribute?('href') return unless node.has_attribute?('href')
begin begin
node['href'] = node['href'].strip
uri = Addressable::URI.parse(node['href']) 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) node.remove_attribute('href') if UNSAFE_PROTOCOLS.include?(uri.scheme)
rescue Addressable::URI::InvalidURIError rescue Addressable::URI::InvalidURIError
......
...@@ -155,7 +155,9 @@ module Gitlab ...@@ -155,7 +155,9 @@ module Gitlab
stream.each_line do |line| stream.each_line do |line|
s = StringScanner.new(line) s = StringScanner.new(line)
until s.eos? 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) handle_sequence(s)
elsif s.scan(/\e(([@-_])(.*?)?)?$/) elsif s.scan(/\e(([@-_])(.*?)?)?$/)
break break
...@@ -183,6 +185,15 @@ module Gitlab ...@@ -183,6 +185,15 @@ module Gitlab
) )
end 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) def handle_sequence(s)
indicator = s[1] indicator = s[1]
commands = s[2].split ';' commands = s[2].split ';'
......
...@@ -7,6 +7,11 @@ module Gitlab ...@@ -7,6 +7,11 @@ module Gitlab
new(gitlab_user.username, gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user)) new(gitlab_user.username, gitlab_user.name, gitlab_user.email, Gitlab::GlId.gl_id(gitlab_user))
end 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) def initialize(username, name, email, gl_id)
@username = username @username = username
@name = name @name = name
......
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
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
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
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
module Gitlab module Gitlab
class UrlSanitizer class UrlSanitizer
ALLOWED_SCHEMES = %w[http https ssh git].freeze
def self.sanitize(content) 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 } content.gsub(regexp) { |url| new(url).masked_url }
rescue Addressable::URI::InvalidURIError rescue Addressable::URI::InvalidURIError
...@@ -11,9 +13,9 @@ module Gitlab ...@@ -11,9 +13,9 @@ module Gitlab
def self.valid?(url) def self.valid?(url)
return false unless url.present? 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 rescue Addressable::URI::InvalidURIError
false false
end end
......
...@@ -5,8 +5,8 @@ module QA ...@@ -5,8 +5,8 @@ module QA
def choose_repository_clone_http def choose_repository_clone_http
find('#clone-dropdown').click find('#clone-dropdown').click
page.within('#clone-dropdown') do page.within('.clone-options-dropdown') do
find('span', text: 'HTTP').click click_link('HTTP')
end end
end end
......
...@@ -216,7 +216,7 @@ describe Projects::JobsController do ...@@ -216,7 +216,7 @@ describe Projects::JobsController do
expect(json_response['text']).to eq status.text expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label expect(json_response['label']).to eq status.label
expect(json_response['icon']).to eq status.icon 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
end end
......
...@@ -658,7 +658,7 @@ describe Projects::MergeRequestsController do ...@@ -658,7 +658,7 @@ describe Projects::MergeRequestsController do
expect(json_response['text']).to eq status.text expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label expect(json_response['label']).to eq status.label
expect(json_response['icon']).to eq status.icon 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
end end
......
...@@ -142,7 +142,7 @@ describe Projects::PipelinesController do ...@@ -142,7 +142,7 @@ describe Projects::PipelinesController do
expect(json_response['text']).to eq status.text expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label expect(json_response['label']).to eq status.label
expect(json_response['icon']).to eq status.icon 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
end end
......
...@@ -83,7 +83,7 @@ describe 'User views a wiki page' do ...@@ -83,7 +83,7 @@ describe 'User views a wiki page' do
it 'shows a file stored in a page' do it 'shows a file stored in a page' do
file = Gollum::File.new(project.wiki) 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') allow_any_instance_of(Gollum::File).to receive(:mime_type).and_return('image/jpeg')
expect(page).to have_xpath('//img[@data-src="image.jpg"]') expect(page).to have_xpath('//img[@data-src="image.jpg"]')
......
...@@ -17,7 +17,7 @@ describe GroupsHelper do ...@@ -17,7 +17,7 @@ describe GroupsHelper do
it 'gives default avatar_icon when no avatar is present' do it 'gives default avatar_icon when no avatar is present' do
group = create(:group) group = create(:group)
group.save! 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
end end
......
...@@ -54,7 +54,7 @@ describe PageLayoutHelper do ...@@ -54,7 +54,7 @@ describe PageLayoutHelper do
describe 'page_image' do describe 'page_image' do
it 'defaults to the GitLab logo' 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 end
%w(project user group).each do |type| %w(project user group).each do |type|
...@@ -70,13 +70,13 @@ describe PageLayoutHelper do ...@@ -70,13 +70,13 @@ describe PageLayoutHelper do
object = double(avatar_url: nil) object = double(avatar_url: nil)
assign(type, object) 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
end end
context "with no assignments" do context "with no assignments" do
it 'falls back to the default' 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 end
end end
......
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 &lt;script&gt;alert(&quot;malicious!&quot;)&lt;/script&gt;');
});
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 ...@@ -195,6 +195,32 @@ describe Gitlab::Ci::Ansi2html do
end end
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 describe "truncates" do
let(:text) { "Hello World" } let(:text) { "Hello World" }
let(:stream) { StringIO.new(text) } let(:stream) { StringIO.new(text) }
......
...@@ -8,6 +8,20 @@ describe Gitlab::Git::User do ...@@ -8,6 +8,20 @@ describe Gitlab::Git::User do
subject { described_class.new(username, name, email, gl_id) } 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 describe '#==' do
def eq_other(username, name, email, gl_id) def eq_other(username, name, email, gl_id)
eq(described_class.new(username, name, email, gl_id)) eq(described_class.new(username, name, email, gl_id))
......
...@@ -84,9 +84,9 @@ describe Gitlab::PathRegex do ...@@ -84,9 +84,9 @@ describe Gitlab::PathRegex do
let(:top_level_words) do let(:top_level_words) do
words = routes_not_starting_in_wildcard.map do |route| words = routes_not_starting_in_wildcard.map do |route|
route.split('/')[1] 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 end
let(:ee_top_level_words) do let(:ee_top_level_words) do
...@@ -95,10 +95,11 @@ describe Gitlab::PathRegex do ...@@ -95,10 +95,11 @@ describe Gitlab::PathRegex do
let(:files_in_public) do let(:files_in_public) do
git = Gitlab.config.git.bin_path git = Gitlab.config.git.bin_path
`cd #{Rails.root} && #{git} ls-files public` tracked = `cd #{Rails.root} && #{git} ls-files public`
.split("\n") .split("\n")
.map { |entry| entry.gsub('public/', '') } .map { |entry| entry.gsub('public/', '') }
.uniq .uniq
tracked + %w(assets uploads)
end end
# All routes that start with a namespaced path, that have 1 or more # All routes that start with a namespaced path, that have 1 or more
......
...@@ -14,7 +14,7 @@ describe 'Gitlab::Popen' do ...@@ -14,7 +14,7 @@ describe 'Gitlab::Popen' do
end end
it { expect(@status).to be_zero } it { expect(@status).to be_zero }
it { expect(@output).to include('cache') } it { expect(@output).to include('tests') }
end end
context 'non-zero status' do context 'non-zero status' do
......
...@@ -39,7 +39,8 @@ describe Gitlab::UrlSanitizer do ...@@ -39,7 +39,8 @@ describe Gitlab::UrlSanitizer do
false | nil false | nil
false | '' false | ''
false | '123://invalid:url' 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://:@example.com' true | 'ssh://:@example.com'
true | 'ssh://foo@example.com' true | 'ssh://foo@example.com'
...@@ -81,24 +82,6 @@ describe Gitlab::UrlSanitizer do ...@@ -81,24 +82,6 @@ describe Gitlab::UrlSanitizer do
describe '#credentials' do describe '#credentials' do
context 'credentials in hash' 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 it 'overrides URL-provided credentials' do
sanitizer = described_class.new('http://a:b@example.com', credentials: { user: 'c', password: 'd' }) sanitizer = described_class.new('http://a:b@example.com', credentials: { user: 'c', password: 'd' })
...@@ -116,10 +99,6 @@ describe Gitlab::UrlSanitizer do ...@@ -116,10 +99,6 @@ describe Gitlab::UrlSanitizer do
'http://@example.com' | { user: nil, password: nil } 'http://@example.com' | { user: nil, password: nil }
'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 # Other invalid URLs
nil | { user: nil, password: nil } nil | { user: nil, password: nil }
'' | { user: nil, password: nil } '' | { user: nil, password: nil }
......
...@@ -6,13 +6,10 @@ describe ProjectWiki do ...@@ -6,13 +6,10 @@ describe ProjectWiki do
let(:user) { project.owner } let(:user) { project.owner }
let(:gitlab_shell) { Gitlab::Shell.new } let(:gitlab_shell) { Gitlab::Shell.new }
let(:project_wiki) { described_class.new(project, user) } 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 } subject { project_wiki }
before do
project_wiki.wiki
end
describe "#path_with_namespace" do describe "#path_with_namespace" do
it "returns the project path with namespace with the .wiki extension" do it "returns the project path with namespace with the .wiki extension" do
expect(subject.path_with_namespace).to eq(project.full_path + '.wiki') expect(subject.path_with_namespace).to eq(project.full_path + '.wiki')
...@@ -61,8 +58,8 @@ describe ProjectWiki do ...@@ -61,8 +58,8 @@ describe ProjectWiki do
end end
describe "#wiki" do describe "#wiki" do
it "contains a Gollum::Wiki instance" do it "contains a Gitlab::Git::Wiki instance" do
expect(subject.wiki).to be_a Gollum::Wiki expect(subject.wiki).to be_a Gitlab::Git::Wiki
end end
it "creates a new wiki repo if one does not yet exist" do it "creates a new wiki repo if one does not yet exist" do
...@@ -70,20 +67,18 @@ describe ProjectWiki do ...@@ -70,20 +67,18 @@ describe ProjectWiki do
end end
it "raises CouldNotCreateWikiError if it can't create the wiki repository" do it "raises CouldNotCreateWikiError if it can't create the wiki repository" do
allow(project_wiki).to receive(:init_repo).and_return(false) # Create a fresh project which will not have a wiki
expect { project_wiki.send(:create_repo!) }.to raise_exception(ProjectWiki::CouldNotCreateWikiError) 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
end end
describe "#empty?" do describe "#empty?" do
context "when the wiki repository is 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 describe '#empty?' do
subject { super().empty? } subject { super().empty? }
it { is_expected.to be_truthy } it { is_expected.to be_truthy }
...@@ -154,13 +149,13 @@ describe ProjectWiki do ...@@ -154,13 +149,13 @@ describe ProjectWiki do
before do before do
file = Gollum::File.new(subject.wiki) file = Gollum::File.new(subject.wiki)
allow_any_instance_of(Gollum::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) .and_return(file)
allow_any_instance_of(Gollum::File) allow_any_instance_of(Gollum::File)
.to receive(:mime_type) .to receive(:mime_type)
.and_return('image/jpeg') .and_return('image/jpeg')
allow_any_instance_of(Gollum::Wiki) allow_any_instance_of(Gollum::Wiki)
.to receive(:file).with('non-existant', 'master', true) .to receive(:file).with('non-existant', 'master')
.and_return(nil) .and_return(nil)
end end
...@@ -178,9 +173,9 @@ describe ProjectWiki do ...@@ -178,9 +173,9 @@ describe ProjectWiki do
expect(subject.find_file('non-existant')).to eq(nil) expect(subject.find_file('non-existant')).to eq(nil)
end end
it 'returns a Gollum::File instance' do it 'returns a Gitlab::Git::WikiFile instance' do
file = subject.find_file('image.jpg') file = subject.find_file('image.jpg')
expect(file).to be_a Gollum::File expect(file).to be_a Gitlab::Git::WikiFile
end end
end end
...@@ -222,9 +217,9 @@ describe ProjectWiki do ...@@ -222,9 +217,9 @@ describe ProjectWiki do
describe "#update_page" do describe "#update_page" do
before do before do
create_page("update-page", "some content") 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( subject.update_page(
@gollum_page, @gitlab_git_wiki_page,
content: "some other content", content: "some other content",
format: :markdown, format: :markdown,
message: "updated page" message: "updated page"
...@@ -246,7 +241,7 @@ describe ProjectWiki do ...@@ -246,7 +241,7 @@ describe ProjectWiki do
it 'updates project activity' do it 'updates project activity' do
subject.update_page( subject.update_page(
@gollum_page, @gitlab_git_wiki_page,
content: 'Yet more content', content: 'Yet more content',
format: :markdown, format: :markdown,
message: 'Updated page again' message: 'Updated page again'
...@@ -262,7 +257,7 @@ describe ProjectWiki do ...@@ -262,7 +257,7 @@ describe ProjectWiki do
describe "#delete_page" do describe "#delete_page" do
before do before do
create_page("index", "some content") create_page("index", "some content")
@page = subject.wiki.paged("index") @page = subject.wiki.page(title: "index")
end end
it "deletes the page" do it "deletes the page" do
...@@ -282,27 +277,28 @@ describe ProjectWiki do ...@@ -282,27 +277,28 @@ describe ProjectWiki do
describe '#create_repo!' do describe '#create_repo!' do
it 'creates a repository' do it 'creates a repository' do
expect(subject).to receive(:init_repo) expect(raw_repository.exists?).to eq(false)
.with(subject.full_path)
.and_return(true)
expect(subject.repository).to receive(:after_create) 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
end end
describe '#ensure_repository' do describe '#ensure_repository' do
it 'creates the repository if it not exist' do it 'creates the repository if it not exist' do
allow(subject).to receive(:repository_exists?).and_return(false) expect(raw_repository.exists?).to eq(false)
expect(subject).to receive(:create_repo!)
expect(subject).to receive(:create_repo!).and_call_original
subject.ensure_repository subject.ensure_repository
expect(raw_repository.exists?).to eq(true)
end end
it 'does not create the repository if it exists' do 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!) expect(subject).not_to receive(:create_repo!)
...@@ -329,7 +325,7 @@ describe ProjectWiki do ...@@ -329,7 +325,7 @@ describe ProjectWiki do
end end
def commit_details def commit_details
{ name: user.name, email: user.email, message: "test commit" } Gitlab::Git::Wiki::CommitDetails.new(user.name, user.email, "test commit")
end end
def create_page(name, content) def create_page(name, content)
...@@ -337,6 +333,6 @@ describe ProjectWiki do ...@@ -337,6 +333,6 @@ describe ProjectWiki do
end end
def destroy_page(page) def destroy_page(page)
subject.wiki.delete_page(page, commit_details) subject.delete_page(page, commit_details)
end end
end end
...@@ -80,7 +80,7 @@ describe WikiPage do ...@@ -80,7 +80,7 @@ describe WikiPage do
context "when initialized with an existing gollum page" do context "when initialized with an existing gollum page" do
before do before do
create_page("test page", "test content") 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) @wiki_page = described_class.new(wiki, @page, true)
end end
...@@ -105,7 +105,7 @@ describe WikiPage do ...@@ -105,7 +105,7 @@ describe WikiPage do
end end
it "sets the version attribute" do 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 end
end end
...@@ -321,14 +321,14 @@ describe WikiPage do ...@@ -321,14 +321,14 @@ describe WikiPage do
end end
it 'returns true when requesting an old version' do 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) old_page = wiki.find_page('Update', old_version)
expect(old_page.historical?).to eq true expect(old_page.historical?).to eq true
end end
it 'returns false when requesting latest version' do 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) latest_page = wiki.find_page('Update', latest_version)
expect(latest_page.historical?).to eq false expect(latest_page.historical?).to eq false
...@@ -393,7 +393,7 @@ describe WikiPage do ...@@ -393,7 +393,7 @@ describe WikiPage do
end end
def commit_details def commit_details
{ name: user.name, email: user.email, message: "test commit" } Gitlab::Git::Wiki::CommitDetails.new(user.name, user.email, "test commit")
end end
def create_page(name, content) def create_page(name, content)
...@@ -401,8 +401,8 @@ describe WikiPage do ...@@ -401,8 +401,8 @@ describe WikiPage do
end end
def destroy_page(title) def destroy_page(title)
page = wiki.wiki.paged(title) page = wiki.wiki.page(title: title)
wiki.wiki.delete_page(page, commit_details) wiki.delete_page(page, commit_details)
end end
def get_slugs(page_or_dir) def get_slugs(page_or_dir)
......
...@@ -360,6 +360,8 @@ describe API::Runner do ...@@ -360,6 +360,8 @@ describe API::Runner do
'policy' => 'pull-push' }] 'policy' => 'pull-push' }]
end end
let(:expected_features) { { 'trace_sections' => true } }
it 'picks a job' do it 'picks a job' do
request_job info: { platform: :darwin } request_job info: { platform: :darwin }
...@@ -379,6 +381,7 @@ describe API::Runner do ...@@ -379,6 +381,7 @@ describe API::Runner do
expect(json_response['artifacts']).to eq(expected_artifacts) expect(json_response['artifacts']).to eq(expected_artifacts)
expect(json_response['cache']).to eq(expected_cache) expect(json_response['cache']).to eq(expected_cache)
expect(json_response['variables']).to include(*expected_variables) expect(json_response['variables']).to include(*expected_variables)
expect(json_response['features']).to eq(expected_features)
end end
context 'when job is made for tag' do context 'when job is made for tag' do
......
...@@ -38,7 +38,7 @@ describe BuildSerializer do ...@@ -38,7 +38,7 @@ describe BuildSerializer do
expect(subject[:text]).to eq(status.text) expect(subject[:text]).to eq(status.text)
expect(subject[:label]).to eq(status.label) expect(subject[:label]).to eq(status.label)
expect(subject[:icon]).to eq(status.icon) 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 end
end end
......
...@@ -168,7 +168,7 @@ describe PipelineSerializer do ...@@ -168,7 +168,7 @@ describe PipelineSerializer do
expect(subject[:text]).to eq(status.text) expect(subject[:text]).to eq(status.text)
expect(subject[:label]).to eq(status.label) expect(subject[:label]).to eq(status.label)
expect(subject[:icon]).to eq(status.icon) 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 end
end end
......
...@@ -18,12 +18,12 @@ describe StatusEntity do ...@@ -18,12 +18,12 @@ describe StatusEntity do
it 'contains status details' do it 'contains status details' do
expect(subject).to include :text, :icon, :favicon, :label, :group expect(subject).to include :text, :icon, :favicon, :label, :group
expect(subject).to include :has_details, :details_path 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 end
it 'contains a dev namespaced favicon if dev env' do it 'contains a dev namespaced favicon if dev env' do
allow(Rails.env).to receive(:development?) { true } 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 end
end end
...@@ -76,9 +76,8 @@ describe Projects::CreateService, '#execute' do ...@@ -76,9 +76,8 @@ describe Projects::CreateService, '#execute' do
context 'wiki_enabled true creates wiki repository directory' do context 'wiki_enabled true creates wiki repository directory' do
it do it do
project = create_project(user, opts) 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
end end
...@@ -86,11 +85,15 @@ describe Projects::CreateService, '#execute' do ...@@ -86,11 +85,15 @@ describe Projects::CreateService, '#execute' do
it do it do
opts[:wiki_enabled] = false opts[:wiki_enabled] = false
project = create_project(user, opts) 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
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 end
context 'builds_enabled global setting' do context 'builds_enabled global setting' do
......
...@@ -169,6 +169,24 @@ RSpec.configure do |config| ...@@ -169,6 +169,24 @@ RSpec.configure do |config|
end end
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 FactoryGirl::SyntaxRunner.class_eval do
include RSpec::Mocks::ExampleMethods include RSpec::Mocks::ExampleMethods
end end
......
...@@ -69,7 +69,12 @@ describe RepositoryCheck::SingleRepositoryWorker do ...@@ -69,7 +69,12 @@ describe RepositoryCheck::SingleRepositoryWorker do
end end
def break_wiki(project) 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 end
def wiki_path(project) def wiki_path(project)
......
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