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:
Mike Greiling 2017-10-03 16:54:06 -05:00
commit 356e9aa811
61 changed files with 645 additions and 187 deletions

View file

@ -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'

View file

@ -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)

View file

@ -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)

View file

@ -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%');
};

View file

@ -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;

View 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;
};

View file

@ -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,
},
});
};

View file

@ -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;

View file

@ -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 {

View file

@ -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);

View file

@ -362,7 +362,7 @@
.dropdown-menu {
top: initial;
bottom: 40px;
bottom: 100%;
width: 298px;
}

View file

@ -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)

View file

@ -229,6 +229,10 @@ module Ci
variables
end
def features
{ trace_sections: true }
end
def merge_request
return @merge_request if defined?(@merge_request)

View file

@ -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

View file

@ -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?

View file

@ -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'

View file

@ -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

View file

@ -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')

View file

@ -0,0 +1,5 @@
---
title: Strip gitlab-runner section markers in build trace HTML view
merge_request: 14393
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Fix bottom spacing for dropdowns that open upwards
merge_request: 14535
author:
type: fixed

View file

@ -0,0 +1,5 @@
---
title: Change index on ci_builds to optimize Jobs Controller
merge_request:
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Bump google-api-client Gem from 0.8.6 to 0.13.6
merge_request:
author:
type: other

View file

@ -0,0 +1,5 @@
---
title: Add basic sprintf implementation to JavaScript
merge_request: 14506
author:
type: other

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

View file

@ -1022,6 +1022,7 @@ module API
expose :cache, using: Cache
expose :credentials, using: Credentials
expose :dependencies, using: Dependency
expose :features
end
end

View file

@ -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

View file

@ -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 ';'

View file

@ -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
View 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

View 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

View 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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"]')

View file

@ -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

View file

@ -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

View 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 &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>');
});
});
});

View file

@ -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) }

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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 }

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)