Commit c454dec1 authored by Mike Greiling's avatar Mike Greiling

Merge branch 'master' into go-go-gadget-webpack

* master: (33 commits)
  Improved code style on the issue_sidebar_spec.rb
  add CHAGELOG.md entry for !8831
  remove assets:compile step from relative_url docs
  update scripts and docs to reference the newly namespaced rake task
  namespace assets rake tasks to gitlab:assets:*
  correct gzip files if they exist as well
  automatically correct CSS urls on assets:precompile
  remove hard-coded assets path for ace editor modules
  Fixed cancel button in the services form not redirecting back to the integrations settings view
  Fix search bar search param encoding
  Fix a transient failure in the `Explore::ProjectsController` spec
  Fix filtering with multiple words
  Fix project name label's for reference in project settings
  Fixed merge request tabs extra margin
  Don't call `#uniq` on a relation
  Move Gitlab::Shell and Gitlab::ShellAdapter files to lib/
  Move ApplicationSetting DEFAULTS to `.defaults` instead
  Move a begin/rescue clause to ApplicationSetting.expire
  Use badge partial as single source of truth instead of having 2 partials doing the same
  Changes after review
  ...
parents a5f79344 b78d06b7
......@@ -109,7 +109,7 @@ setup-test-env:
script:
- npm install
- bundle exec rake webpack:compile
- bundle exec rake assets:precompile 2>/dev/null
- bundle exec rake gitlab:assets:compile 2>/dev/null
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
artifacts:
expire_in: 7d
......
......@@ -280,6 +280,7 @@ group :development, :test do
gem 'rspec-retry', '~> 0.4.5'
gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2'
gem 'rspec_profiling'
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0'
......
......@@ -638,6 +638,11 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
rspec_profiling (0.0.4)
activerecord
pg
rails
sqlite3
rubocop (0.46.0)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
......@@ -733,6 +738,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.11)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
......@@ -953,6 +959,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rspec_profiling
rubocop (~> 0.46.0)
rubocop-rspec (~> 1.9.1)
ruby-fogbugz (~> 0.2.1)
......
......@@ -81,7 +81,6 @@ window.ES6Promise.polyfill();
var $sidebarGutterToggle = $('.js-sidebar-toggle');
var $flash = $('.flash-container');
var bootstrapBreakpoint = bp.getBreakpointSize();
var checkInitialSidebarSize;
var fitSidebarForSize;
// Set the default path for all cookies to GitLab's root directory
......@@ -243,19 +242,11 @@ window.ES6Promise.polyfill();
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
checkInitialSidebarSize = function () {
bootstrapBreakpoint = bp.getBreakpointSize();
if (bootstrapBreakpoint === 'xs' || 'sm') {
return $document.trigger('breakpoint:change', [bootstrapBreakpoint]);
}
};
$window.off('resize.app').on('resize.app', function () {
return fitSidebarForSize();
});
gl.awardsHandler = new AwardsHandler();
checkInitialSidebarSize();
new Aside();
// bind sidebar events
new gl.Sidebar();
});
......
......@@ -3,7 +3,7 @@
(function() {
this.CommitsList = (function() {
function CommitsList() {}
var CommitsList = {};
CommitsList.timer = null;
......@@ -20,6 +20,7 @@
});
this.content = $("#commits-list");
this.searchField = $("#commits-search");
this.lastSearch = this.searchField.val();
return this.initSearch();
};
......@@ -37,6 +38,7 @@
var commitsUrl, form, search;
form = $(".commits-search-form");
search = CommitsList.searchField.val();
if (search === CommitsList.lastSearch) return;
commitsUrl = form.attr("action") + '?' + form.serialize();
CommitsList.content.fadeTo('fast', 0.5);
return $.ajax({
......@@ -47,12 +49,16 @@
return CommitsList.content.fadeTo('fast', 1.0);
},
success: function(data) {
CommitsList.lastSearch = search;
CommitsList.content.html(data.html);
return history.replaceState({
page: commitsUrl
// Change url so if user reload a page - search results are saved
}, document.title, commitsUrl);
},
error: function() {
CommitsList.lastSearch = null;
},
dataType: "json"
});
};
......
......@@ -28,7 +28,12 @@
if (lastToken !== searchToken) {
const title = updatedItem.title.toLowerCase();
let value = lastToken.value.toLowerCase();
value = value.replace(/"(.*?)"/g, str => str.slice(1).slice(0, -1));
// Removes the first character if it is a quotation so that we can search
// with multiple words
if ((value[0] === '"' || value[0] === '\'') && title.indexOf(' ') !== -1) {
value = value.slice(1);
}
// Eg. filterSymbol = ~ for labels
const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1;
......@@ -83,8 +88,9 @@
const selectionStart = input.selectionStart;
let inputValue = input.value;
// Replace all spaces inside quote marks with underscores
// (will continue to match entire string until an end quote is found if any)
// This helps with matching the beginning & end of a token:key
inputValue = inputValue.replace(/("(.*?)"|:\s+)/g, str => str.replace(/\s/g, '_'));
inputValue = inputValue.replace(/(('[^']*'{0,1})|("[^"]*"{0,1})|:\s+)/g, str => str.replace(/\s/g, '_'));
// Get the right position for the word selected
// Regex matches first space
......
......@@ -196,7 +196,8 @@
});
if (searchToken) {
paths.push(`search=${encodeURIComponent(searchToken)}`);
const sanitized = searchToken.split(' ').map(t => encodeURIComponent(t)).join('+');
paths.push(`search=${sanitized}`);
}
Turbolinks.visit(`?scope=all&utf8=✓&${paths.join('&')}`);
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, max-len */
/* global UsersSelect */
/* global Cookies */
/* global bp */
(function() {
this.IssuableContext = (function() {
......@@ -37,6 +39,13 @@
}, 0);
}
});
window.addEventListener('beforeunload', function() {
// collapsed_gutter cookie hides the sidebar
var bpBreakpoint = bp.getBreakpointSize();
if (bpBreakpoint === 'xs' || bpBreakpoint === 'sm') {
Cookies.set('collapsed_gutter', true);
}
});
$(".right-sidebar").niceScroll();
}
......
/*= require ace-rails-ap */
/*= require ace/ace */
/*= require ace/ext-searchbox */
/*= require ./ace/ace_config_paths */
<%
ace_gem_path = Bundler.rubygems.find_name('ace-rails-ap').first.full_gem_path
ace_workers = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/worker-*.js'].sort.map do |file|
File.basename(file, '.js').sub(/^worker-/, '')
end
ace_modes = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/mode-*.js'].sort.map do |file|
File.basename(file, '.js').sub(/^mode-/, '')
end
%>
(function() {
window.gon = window.gon || {};
var basePath = (window.gon.relative_url_root || '').replace(/\/$/, '') + '/assets/ace';
ace.config.set('basePath', basePath);
// configure paths for all worker modules
<% ace_workers.each do |worker| %>
ace.config.setModuleUrl('ace/mode/<%= worker %>_worker', basePath + '/worker-<%= worker %>.js');
<% end %>
// configure paths for all mode modules
<% ace_modes.each do |mode| %>
ace.config.setModuleUrl('ace/mode/<%= mode %>', basePath + '/mode-<%= mode %>.js');
<% end %>
})();
......@@ -158,7 +158,7 @@
};
Calendar.prototype.renderMonths = function() {
return this.svg.append('g').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
return date.x;
}).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) {
return function(date) {
......
.calender-block {
padding-left: 0;
padding-right: 0;
direction: rtl;
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
overflow-x: scroll;
......
......@@ -71,7 +71,7 @@ header {
&:focus,
&:active {
background-color: $gray-light;
color: darken($gl-text-color-secondary, 30%);
color: $gl-text-color;
.todos-pending-count {
background: darken($todo-alert-blue, 10%);
......
......@@ -178,7 +178,7 @@ $count-arrow-border: #dce0e5;
$save-project-loader-color: #555;
$divergence-graph-bar-bg: #ccc;
$divergence-graph-separator-bg: #ccc;
$general-hover-transition-duration: 150ms;
$general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
......
......@@ -26,10 +26,6 @@
border: 0;
}
}
.container-fluid {
@extend .fixed-width-container;
}
}
}
......
......@@ -420,10 +420,6 @@
.merge-request-tabs-holder {
background-color: $white-light;
.container-limited {
max-width: $limited-layout-width;
}
&.affix {
top: 100px;
left: 0;
......@@ -433,10 +429,26 @@
@media (max-width: $screen-xs-max) {
right: 0;
}
.merge-request-tabs-container {
padding-left: $gl-padding;
padding-right: $gl-padding;
}
}
}
&:not(.affix) .container-fluid {
padding-left: 0;
padding-right: 0;
.limit-container-width {
.merge-request-tabs-container {
max-width: $limited-layout-width;
margin-left: auto;
margin-right: auto;
}
}
.limit-container-width:not(.container-limited) {
.merge-request-tabs-holder:not(.affix) {
.merge-request-tabs-container {
max-width: $limited-layout-width - ($gl-padding * 2);
}
}
}
......@@ -198,7 +198,7 @@
margin: 15px 5px 0 0;
input {
height: 27px;
height: 28px;
}
}
......@@ -523,7 +523,7 @@ a.deploy-project-label {
&:hover,
&:focus {
color: darken($notes-light-color, 15%);
color: $gl-text-color;
}
}
......
......@@ -18,15 +18,14 @@ class AutocompleteController < ApplicationController
if params[:search].blank?
# Include current user if available to filter by "Me"
if params[:current_user].present? && current_user
@users = @users.where.not(id: current_user.id)
@users = [current_user, *@users]
end
if params[:author_id].present?
author = User.find_by_id(params[:author_id])
@users = [author, *@users] if author
@users = [author, *@users].uniq if author
end
@users.uniq!
end
render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
......
......@@ -13,49 +13,6 @@ class ApplicationSetting < ActiveRecord::Base
[\r\n] # any number of newline characters
}x
DEFAULTS_CE = {
after_sign_up_text: nil,
akismet_enabled: false,
container_registry_token_expire_delay: 5,
default_branch_protection: Settings.gitlab['default_branch_protection'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
disabled_oauth_sign_in_sources: [],
domain_whitelist: Settings.gitlab['domain_whitelist'],
gravatar_enabled: Settings.gravatar['enabled'],
help_page_text: nil,
housekeeping_bitmaps_enabled: true,
housekeeping_enabled: true,
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
housekeeping_incremental_repack_period: 10,
import_sources: Gitlab::ImportSources.values,
koding_enabled: false,
koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
plantuml_enabled: false,
plantuml_url: nil,
recaptcha_enabled: false,
repository_checks_enabled: true,
repository_storages: ['default'],
require_two_factor_authentication: false,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
send_user_confirmation_email: false,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
shared_runners_text: nil,
sidekiq_throttling_enabled: false,
sign_in_text: nil,
signin_enabled: Settings.gitlab['signin_enabled'],
signup_enabled: Settings.gitlab['signup_enabled'],
two_factor_grace_period: 48,
user_default_external: false
}
DEFAULTS = DEFAULTS_CE
serialize :restricted_visibility_levels
serialize :import_sources
serialize :disabled_oauth_sign_in_sources, Array
......@@ -199,14 +156,64 @@ class ApplicationSetting < ActiveRecord::Base
def self.expire
Rails.cache.delete(CACHE_KEY)
rescue
# Gracefully handle when Redis is not available. For example,
# omnibus may fail here during gitlab:assets:compile.
end
def self.cached
Rails.cache.fetch(CACHE_KEY)
end
def self.defaults_ce
{
after_sign_up_text: nil,
akismet_enabled: false,
container_registry_token_expire_delay: 5,
default_branch_protection: Settings.gitlab['default_branch_protection'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
disabled_oauth_sign_in_sources: [],
domain_whitelist: Settings.gitlab['domain_whitelist'],
gravatar_enabled: Settings.gravatar['enabled'],
help_page_text: nil,
housekeeping_bitmaps_enabled: true,
housekeeping_enabled: true,
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
housekeeping_incremental_repack_period: 10,
import_sources: Gitlab::ImportSources.values,
koding_enabled: false,
koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
plantuml_enabled: false,
plantuml_url: nil,
recaptcha_enabled: false,
repository_checks_enabled: true,
repository_storages: ['default'],
require_two_factor_authentication: false,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
send_user_confirmation_email: false,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
shared_runners_text: nil,
sidekiq_throttling_enabled: false,
sign_in_text: nil,
signin_enabled: Settings.gitlab['signin_enabled'],
signup_enabled: Settings.gitlab['signup_enabled'],
two_factor_grace_period: 48,
user_default_external: false
}
end
def self.defaults
defaults_ce
end
def self.create_from_defaults
create(DEFAULTS)
create(defaults)
end
def home_page_url_column_exist
......
- status = local_assigns.fetch(:status)
- link = local_assigns.fetch(:link, true)
- css_classes = "ci-status ci-#{status.group}"
- if status.has_details?
- if link && status.has_details?
= link_to status.details_path, class: css_classes do
= custom_icon(status.icon)
= status.text
......
.content-block.build-header
.header-content
= render 'ci/status/badge', status: @build.detailed_status(current_user)
= render 'ci/status/badge', status: @build.detailed_status(current_user), link: false
Build
%strong ##{@build.id}
%strong.js-build-id ##{@build.id}
in pipeline
= link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id}
......
......@@ -9,7 +9,7 @@
%fieldset.append-bottom-0
.row
.form-group.col-md-9
= f.label :name, class: 'label-light' do
= f.label :name, class: 'label-light', for: 'project_name_edit' do
Project name
= f.text_field :name, class: "form-control", id: "project_name_edit"
......
......@@ -19,10 +19,8 @@
= render 'shared/issuable/nav', type: :issues
.nav-controls
- if current_user
= link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10' do
= link_to url_for(params.merge(format: :atom, private_token: current_user.private_token)), class: 'btn append-right-10 has-tooltip', title: 'Subscribe' do
= icon('rss')
%span.icon-label
Subscribe
- if can? current_user, :create_issue, @project
= link_to new_namespace_project_issue_path(@project.namespace,
@project,
......
......@@ -52,7 +52,7 @@
= render 'award_emoji/awards_block', awardable: @merge_request, inline: true
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
%div{ class: container_class }
.merge-request-tabs-container
%ul.merge-request-tabs.nav-links.no-top.no-bottom
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
......
......@@ -17,4 +17,4 @@
- disabled_title = @service.disabled_title
= link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service), class: "btn #{disabled_class}", title: disabled_title
= link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
= link_to "Cancel", namespace_project_settings_integrations_path(@project.namespace, @project), class: "btn btn-cancel"
---
title: Add link verification to badge partial in order to render a badge without a link
merge_request: 8740
author:
---
title: Fix filtering with multiple words
merge_request: 8830
author:
---
title: Fix project name label's for reference in project settings
merge_request: 8795
author:
---
title: contribution calendar scrolls from right to left
merge_request:
author:
---
title: Fixed services form cancel not redirecting back the integrations settings view
merge_request: 8843
author:
---
title: Fix search bar search param encoding
merge_request: 8753
author:
---
title: Fixed Issuable sidebar not closing on smaller/mobile sized screens
merge_request:
author:
---
title: Fixed merge requests tab extra margin when fixed to window
merge_request:
author:
---
title: allow relative url change without recompiling frontend assets
merge_request: 8831
author:
......@@ -419,10 +419,4 @@ if Rails.env.test?
end
# Force a refresh of application settings at startup
begin
ApplicationSetting.expire
Ci::ApplicationSetting.expire
rescue
# Gracefully handle when Redis is not available. For example,
# omnibus may fail here during assets:precompile.
end
ApplicationSetting.expire
# GIT over SSH
require_dependency Rails.root.join('lib/gitlab/backend/shell')
# GitLab shell adapter
require_dependency Rails.root.join('lib/gitlab/backend/shell_adapter')
required_version = Gitlab::VersionInfo.parse(Gitlab::Shell.version_required)
current_version = Gitlab::VersionInfo.parse(Gitlab::Shell.new.version)
......
module RspecProfilingConnection
def establish_connection
::RspecProfiling::Collectors::PSQL::Result.establish_connection(ENV['RSPEC_PROFILING_POSTGRES_URL'])
end
end
if Rails.env.test?
RspecProfiling.configure do |config|
if ENV['RSPEC_PROFILING_POSTGRES_URL']
RspecProfiling::Collectors::PSQL.prepend(RspecProfilingConnection)
config.collector = RspecProfiling::Collectors::PSQL
end
end
end
......@@ -172,14 +172,14 @@ Omnibus packages.
```
cd /home/git/gitlab
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production
```
For omnibus versions, the unoptimized assets (JavaScript, CSS) are frozen at
the release of upstream GitLab. The omnibus version includes optimized versions
of those assets. Unless you are modifying the JavaScript / CSS code on your
production machine after installing the package, there should be no reason to redo
rake assets:precompile on the production machine. If you suspect that assets
rake gitlab:assets:compile on the production machine. If you suspect that assets
have been corrupted, you should reinstall the omnibus package.
## Tracking Deployments
......
......@@ -245,7 +245,7 @@ Example response:
```json
[
{
"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
"diff": "--- a/doc/update/5.4-to-6.0.md\n+++ b/doc/update/5.4-to-6.0.md\n@@ -71,6 +71,8 @@\n sudo -u git -H bundle exec rake migrate_keys RAILS_ENV=production\n sudo -u git -H bundle exec rake migrate_inline_notes RAILS_ENV=production\n \n+sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production\n+\n ```\n \n ### 6. Update config files",
"new_path": "doc/update/5.4-to-6.0.md",
"old_path": "doc/update/5.4-to-6.0.md",
"a_mode": null,
......
......@@ -211,6 +211,41 @@ suite first. See the
[StackProf documentation](https://github.com/tmm1/stackprof/blob/master/README.md)
for details.
## RSpec profiling
GitLab's development environment also includes the
[rspec_profiling](https://github.com/foraker/rspec_profiling) gem, which is used
to collect data on spec execution times. This is useful for analyzing the
performance of the test suite itself, or seeing how the performance of a spec
may have changed over time.
To activate profiling in your local environment, run the following:
```
$ export RSPEC_PROFILING=yes
$ rake rspec_profiling:install
```
This creates an SQLite3 database in `tmp/rspec_profiling`, into which statistics
are saved every time you run specs with the `RSPEC_PROFILING` environment
variable set.
Ad-hoc investigation of the collected results can be performed in an interactive
shell:
```
$ rake rspec_profiling:console
irb(main):001:0> results.count
=> 231
irb(main):002:0> results.last.attributes.keys
=> ["id", "commit", "date", "file", "line_number", "description", "time", "status", "exception", "query_count", "query_time", "request_count", "request_time", "created_at", "updated_at"]
irb(main):003:0> results.where(status: "passed").average(:time).to_s
=> "0.211340155844156"
```
These results can also be placed into a PostgreSQL database by setting the
`RSPEC_PROFILING_POSTGRES_URL` variable. This is used to profile the test suite
when running in the CI environment.
## Importance of Changes
When working on performance improvements, it's important to always ask yourself
......
......@@ -448,7 +448,7 @@ Check if GitLab and its environment are configured correctly:
### Compile Assets
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production
### Start Your GitLab Instance
......
......@@ -113,14 +113,6 @@ Make sure to follow all steps below:
If you are using a custom init script, make sure to edit the above
gitlab-workhorse setting as needed.
1. After all the above changes recompile the assets. This is an important task
and will take some time to complete depending on the server resources:
```
cd /home/git/gitlab
sudo -u git -H bundle exec rake assets:clean assets:precompile RAILS_ENV=production
```
1. [Restart GitLab][] for the changes to take effect.
### Disable relative URL in GitLab
......
......@@ -57,7 +57,7 @@ sudo -u git -H bundle clean
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production
```
### 4. Update gitlab-workhorse to the corresponding version
......
......@@ -30,7 +30,7 @@ module Gitlab
end
def in_memory_application_settings
@in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting::DEFAULTS)
@in_memory_application_settings ||= ::ApplicationSetting.new(::ApplicationSetting.defaults)
# In case migrations the application_settings table is not created yet,
# we fallback to a simple OpenStruct
rescue ActiveRecord::StatementInvalid, ActiveRecord::UnknownAttributeError
......@@ -38,7 +38,7 @@ module Gitlab
end
def fake_application_settings
OpenStruct.new(::ApplicationSetting::DEFAULTS)
OpenStruct.new(::ApplicationSetting.defaults)
end
private
......
......@@ -61,7 +61,7 @@ module Gitlab
"Switch to new version" => %W(#{Gitlab.config.git.bin_path} checkout v#{latest_version}),
"Install gems" => %W(bundle),
"Migrate DB" => %W(bundle exec rake db:migrate),
"Recompile assets" => %W(bundle exec rake assets:clean assets:precompile),
"Recompile assets" => %W(bundle exec rake gitlab:assets:clean gitlab:assets:compile),
"Clear cache" => %W(bundle exec rake cache:clear)
}
end
......
......@@ -31,8 +31,8 @@ echo 'Deploy: Bundle and migrate'
sudo -u git -H bundle --without aws development test mysql --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:clean RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:assets:compile RAILS_ENV=production
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
# return stashed changes (if necessary)
......
namespace :gitlab do
namespace :assets do
desc 'GitLab | Assets | Compile all frontend assets'
task :compile do
Rake::Task['assets:precompile'].invoke
Rake::Task['gitlab:assets:fix_urls'].invoke
end
desc 'GitLab | Assets | Clean up old compiled frontend assets'
task :clean do
Rake::Task['assets:clean'].invoke
end
desc 'GitLab | Assets | Remove all compiled frontend assets'
task :purge do
Rake::Task['assets:clobber'].invoke
end
desc 'GitLab | Assets | Fix all absolute url references in CSS'
task :fix_urls do
css_files = Dir['public/assets/*.css']
css_files.each do | file |
# replace url(/assets/*) with url(./*)
puts "Fixing #{file}"
system "sed", "-i", "-e", 's/url(\([\"\']\?\)\/assets\//url(\1.\//g', file
# rewrite the corresponding gzip file (if it exists)
gzip = "#{file}.gz"
if File.exist?(gzip)
puts "Fixing #{gzip}"
FileUtils.rm(gzip)
mtime = File.stat(file).mtime
File.open(gzip, 'wb+') do |f|
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
gz.mtime = mtime
gz.write IO.binread(file)
gz.close
File.utime(mtime, mtime, f.path)
end
end
end
end
end
end
require 'spec_helper'
describe Explore::ProjectsController do
let(:user) { create(:user) }
let(:visibility) { :public }
describe 'GET #trending' do
let!(:project_1) { create(:project, visibility, ci_id: 1) }
let!(:project_2) { create(:project, visibility, ci_id: 2) }
let!(:trending_project_1) { create(:trending_project, project: project_1) }
let!(:trending_project_2) { create(:trending_project, project: project_2) }
context 'sorting by update date' do
let(:project1) { create(:empty_project, :public, updated_at: 3.days.ago) }
let(:project2) { create(:empty_project, :public, updated_at: 1.day.ago) }
before do
sign_in(user)
end
before do
create(:trending_project, project: project1)
create(:trending_project, project: project2)
end
context 'sorting by update date' do
it 'sorts by last updated' do
get :trending, sort: 'updated_desc'
expect(assigns(:projects)).to eq [project_2, project_1]
expect(assigns(:projects)).to eq [project2, project1]
end
it 'sorts by oldest updated' do
get :trending, sort: 'updated_asc'
expect(assigns(:projects)).to eq [project_1, project_2]
expect(assigns(:projects)).to eq [project1, project2]
end
end
end
......
......@@ -773,7 +773,7 @@ describe 'Filter issues', js: true, feature: true do
describe 'RSS feeds' do
it 'updates atom feed link for project issues' do
visit namespace_project_issues_path(project.namespace, project, milestone_title: milestone.title, assignee_id: user.id)
link = find('.nav-controls a', text: 'Subscribe')
link = find_link('Subscribe')
params = CGI.parse(URI.parse(link[:href]).query)
auto_discovery_link = find('link[type="application/atom+xml"]', visible: false)
auto_discovery_params = CGI.parse(URI.parse(auto_discovery_link[:href]).query)
......
......@@ -2,6 +2,7 @@ require 'rails_helper'
feature 'Issue Sidebar', feature: true do
include WaitForAjax
include MobileHelpers
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
......@@ -59,6 +60,23 @@ feature 'Issue Sidebar', feature: true do
end
end
context 'sidebar', js: true do
it 'changes size when the screen size is smaller' do
sidebar_selector = 'aside.right-sidebar.right-sidebar-collapsed'
# Resize the window
resize_screen_sm
# Make sure the sidebar is collapsed
expect(page).to have_css(sidebar_selector)
# Once is collapsed let's open the sidebard and reload
open_issue_sidebar
refresh
expect(page).to have_css(sidebar_selector)
# Restore the window size as it was including the sidebar
restore_window_size
open_issue_sidebar
end
end
context 'creating a new label', js: true do
it 'shows option to crate a new label is present' do
page.within('.block.labels') do
......@@ -109,4 +127,11 @@ feature 'Issue Sidebar', feature: true do
def visit_issue(project, issue)
visit namespace_project_issue_path(project.namespace, project, issue)
end
def open_issue_sidebar
page.within('aside.right-sidebar.right-sidebar-collapsed') do
find('.js-sidebar-toggle').click
sleep 1
end
end
end
......@@ -37,7 +37,7 @@ describe 'Edit Project Settings', feature: true do
it 'shows errors for invalid project path/name' do
visit edit_namespace_project_path(project.namespace, project)
fill_in 'Project name', with: 'foo&bar'
fill_in 'project_name', with: 'foo&bar'
fill_in 'Path', with: 'foo&bar'
click_button 'Rename project'
......@@ -53,7 +53,7 @@ describe 'Edit Project Settings', feature: true do
it 'shows error for invalid project name' do
visit edit_namespace_project_path(project.namespace, project)
fill_in 'Project name', with: '🚀 foo bar ☁️'
fill_in 'project_name', with: '🚀 foo bar ☁️'
click_button 'Rename project'
......
/* global CommitsList */
//= require jquery.endless-scroll
//= require pager
//= require commits
(() => {
describe('Commits List', () => {
beforeEach(() => {
setFixtures(`
<form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/master">
<input id="commits-search">
</form>
<ol id="commits-list"></ol>
`);
});
it('should be defined', () => {
expect(CommitsList).toBeDefined();
});
describe('on entering input', () => {
let ajaxSpy;
beforeEach(() => {
CommitsList.init(25);
CommitsList.searchField.val('');
spyOn(history, 'replaceState').and.stub();
ajaxSpy = spyOn(jQuery, 'ajax').and.callFake((req) => {
req.success({
data: '<li>Result</li>',
});
});
});
it('should save the last search string', () => {
CommitsList.searchField.val('GitLab');
CommitsList.filterResults();
expect(ajaxSpy).toHaveBeenCalled();
expect(CommitsList.lastSearch).toEqual('GitLab');
});
it('should not make ajax call if the input does not change', () => {
CommitsList.filterResults();
expect(ajaxSpy).not.toHaveBeenCalled();
expect(CommitsList.lastSearch).toEqual('');
});
});
});
})();
......@@ -64,6 +64,68 @@ require('~/filtered_search/filtered_search_dropdown_manager');
const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
expect(updatedItem.droplab_hidden).toBe(false);
});
describe('filters multiple word title', () => {
const multipleWordItem = {
title: 'Community Contributions',
};
it('should filter with double quote', () => {
input.value = 'label:"';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote and symbol', () => {
input.value = 'label:~"';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote and multiple words', () => {
input.value = 'label:"community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote, symbol and multiple words', () => {
input.value = 'label:~"community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote', () => {
input.value = 'label:\'';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and symbol', () => {
input.value = 'label:~\'';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and multiple words', () => {
input.value = 'label:\'community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote, symbol and multiple words', () => {
input.value = 'label:~\'community con';
const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
});
});
describe('filterHint', () => {
......@@ -130,5 +192,99 @@ require('~/filtered_search/filtered_search_dropdown_manager');
expect(result).toBe(false);
});
});
describe('getInputSelectionPosition', () => {
describe('word with trailing spaces', () => {
const value = 'label:none ';
it('should return selectionStart when cursor is at the trailing space', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 11,
value,
});
expect(left).toBe(11);
expect(right).toBe(11);
});
it('should return input when cursor is at the start of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 0,
value,
});
expect(left).toBe(0);
expect(right).toBe(10);
});
it('should return input when cursor is at the middle of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 7,
value,
});
expect(left).toBe(0);
expect(right).toBe(10);
});
it('should return input when cursor is at the end of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 10,
value,
});
expect(left).toBe(0);
expect(right).toBe(10);
});
});
describe('multiple words', () => {
const value = 'label:~"Community Contribution"';
it('should return input when cursor is after the first word', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 17,
value,
});
expect(left).toBe(0);
expect(right).toBe(31);
});
it('should return input when cursor is before the second word', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 18,
value,
});
expect(left).toBe(0);
expect(right).toBe(31);
});
});
describe('incomplete multiple words', () => {
const value = 'label:~"Community Contribution';
it('should return entire input when cursor is at the start of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 0,
value,
});
expect(left).toBe(0);
expect(right).toBe(30);
});
it('should return entire input when cursor is at the end of input', () => {
const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
selectionStart: 30,
value,
});
expect(left).toBe(0);
expect(right).toBe(30);
});
});
});
});
})();
/* global Turbolinks */
//= require turbolinks
//= require lib/utils/common_utils
//= require filtered_search/filtered_search_token_keys
//= require filtered_search/filtered_search_tokenizer
//= require filtered_search/filtered_search_dropdown_manager
//= require filtered_search/filtered_search_manager
(() => {
describe('Filtered Search Manager', () => {
describe('search', () => {
let manager;
const defaultParams = '?scope=all&utf8=✓&state=opened';
function getInput() {
return document.querySelector('.filtered-search');
}
beforeEach(() => {
setFixtures(`
<input type='text' class='filtered-search' />
`);
spyOn(gl.FilteredSearchManager.prototype, 'bindEvents').and.callFake(() => {});
spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
spyOn(gl.utils, 'getParameterByName').and.returnValue(null);
manager = new gl.FilteredSearchManager();
});
afterEach(() => {
getInput().outerHTML = '';
});
it('should search with a single word', () => {
getInput().value = 'searchTerm';
spyOn(Turbolinks, 'visit').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=searchTerm`);
});
manager.search();
});
it('should search with multiple words', () => {
getInput().value = 'awesome search terms';
spyOn(Turbolinks, 'visit').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
});
manager.search();
});
it('should search with special characters', () => {
getInput().value = '~!@#$%^&*()_+{}:<>,.?/';
spyOn(Turbolinks, 'visit').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
});
manager.search();
});
});
});
})();
......@@ -9,6 +9,10 @@ require 'rspec/rails'
require 'shoulda/matchers'
require 'rspec/retry'
if ENV['RSPEC_PROFILING_POSTGRES_URL'] || ENV['RSPEC_PROFILING']
require 'rspec_profiling/rspec'
end
if ENV['CI'] && !ENV['NO_KNAPSACK']
require 'knapsack'
Knapsack::Adapters::RSpecAdapter.bind
......
module MobileHelpers
def resize_screen_sm
resize_window(900, 768)
end
def restore_window_size
resize_window(1366, 768)
end
def resize_window(width, height)
page.driver.resize_window width, height
end
end
......@@ -15,6 +15,36 @@ describe 'projects/builds/show', :view do
allow(view).to receive(:can?).and_return(true)
end
describe 'build information in header' do
let(:build) do
create(:ci_build, :success, environment: 'staging')
end
before do
render
end
it 'shows status name' do
expect(rendered).to have_css('.ci-status.ci-success', text: 'passed')
end
it 'does not render a link to the build' do
expect(rendered).not_to have_link('passed')
end
it 'shows build id' do
expect(rendered).to have_css('.js-build-id', text: build.id)
end
it 'shows a link to the pipeline' do
expect(rendered).to have_link(build.pipeline.id)
end
it 'shows a link to the commit' do
expect(rendered).to have_link(build.pipeline.short_sha)
end
end
describe 'environment info in build view' do
context 'build with latest deployment' do
let(:build) do
......
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