Commit b4523ebb authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee'

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>

Conflicts:
	app/views/profiles/accounts/show.html.haml
parents 00c3c4c5 a209e62e
Please view this file on the master branch, on stable branches it's out of date.
v 7.13.0 (unreleased)
- Support commenting on diffs in side-by-side mode (Stan Hu)
- Fix JavaScript error when clicking on the comment button on a diff line that has a comment already (Stan Hu)
- Remove project visibility icons from dashboard projects list
- Rename "Design" profile settings page to "Preferences".
- Allow users to customize their default Dashboard page.
- Update ssl_ciphers in Nginx example to remove DHE settings. This will deny forward secrecy for Android 2.3.7, Java 6 and OpenSSL 0.9.8
v 7.12.0 (unreleased)
- Fix post-receive errors on a push when an external issue tracker is configured (Stan Hu)
- Update oauth button logos for Twitter and Google to recommended assets
- Fix hooks for web based events with external issue references (Daniel Gerhardt)
- Update browser gem to version 0.8.0 for IE11 support (Stan Hu)
- Fix timeout when rendering file with thousands of lines.
- Add "Remember me" checkbox to LDAP signin form.
- Add session expiration delay configuration through UI application settings
- Fix external issue tracker hook/test for HTTPS URLs (Daniel Gerhardt)
- Don't notify users mentioned in code blocks or blockquotes.
- Omit link to generate labels if user does not have access to create them (Stan Hu)
- Show warning when a comment will add 10 or more people to the discussion.
......
source "https://rubygems.org"
gem "rails", "~> 4.1.0"
gem 'rails', '4.1.11'
# Default values for AR models
gem "default_value_for", "~> 3.0.0"
......@@ -95,7 +95,7 @@ gem "seed-fu"
gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '1.0.2', require: 'task_list/railtie'
gem 'github-markup'
gem 'redcarpet', '~> 3.2.3'
gem 'redcarpet', '~> 3.3.0'
gem 'RedCloth'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.12'
......@@ -194,7 +194,7 @@ gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.1'
gem 'gon', '~> 5.0.0'
gem 'jquery-atwho-rails', '~> 1.0.0'
gem 'jquery-rails', '3.1.2'
gem 'jquery-rails', '3.1.3'
gem 'jquery-scrollto-rails'
gem 'jquery-ui-rails'
gem 'nprogress-rails'
......
......@@ -4,31 +4,31 @@ GEM
CFPropertyList (2.3.1)
RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.1.9)
actionpack (= 4.1.9)
actionview (= 4.1.9)
actionmailer (4.1.11)
actionpack (= 4.1.11)
actionview (= 4.1.11)
mail (~> 2.5, >= 2.5.4)
actionpack (4.1.9)
actionview (= 4.1.9)
activesupport (= 4.1.9)
actionpack (4.1.11)
actionview (= 4.1.11)
activesupport (= 4.1.11)
rack (~> 1.5.2)
rack-test (~> 0.6.2)
actionview (4.1.9)
activesupport (= 4.1.9)
actionview (4.1.11)
activesupport (= 4.1.11)
builder (~> 3.1)
erubis (~> 2.7.0)
activemodel (4.1.9)
activesupport (= 4.1.9)
activemodel (4.1.11)
activesupport (= 4.1.11)
builder (~> 3.1)
activerecord (4.1.9)
activemodel (= 4.1.9)
activesupport (= 4.1.9)
activerecord (4.1.11)
activemodel (= 4.1.11)
activesupport (= 4.1.11)
arel (~> 5.0.0)
activeresource (4.0.0)
activemodel (~> 4.0)
activesupport (~> 4.0)
rails-observers (~> 0.1.1)
activesupport (4.1.9)
activesupport (4.1.11)
i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
......@@ -332,7 +332,7 @@ GEM
inflecto (0.0.2)
ipaddress (0.8.0)
jquery-atwho-rails (1.0.1)
jquery-rails (3.1.2)
jquery-rails (3.1.3)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
jquery-scrollto-rails (1.4.3)
......@@ -342,7 +342,7 @@ GEM
turbolinks
jquery-ui-rails (4.2.1)
railties (>= 3.2.16)
json (1.8.2)
json (1.8.3)
jwt (0.1.13)
multi_json (>= 1.5)
kaminari (0.15.1)
......@@ -367,7 +367,7 @@ GEM
mini_portile (0.6.2)
minitest (5.3.5)
mousetrap-rails (1.4.6)
multi_json (1.10.1)
multi_json (1.11.1)
multi_xml (0.5.5)
multipart-post (1.2.0)
mysql2 (0.3.16)
......@@ -450,7 +450,7 @@ GEM
quiet_assets (1.0.2)
railties (>= 3.1, < 5.0)
racc (1.4.10)
rack (1.5.3)
rack (1.5.4)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.3.0)
......@@ -470,21 +470,21 @@ GEM
rack
rack-test (0.6.3)
rack (>= 1.0)
rails (4.1.9)
actionmailer (= 4.1.9)
actionpack (= 4.1.9)
actionview (= 4.1.9)
activemodel (= 4.1.9)
activerecord (= 4.1.9)
activesupport (= 4.1.9)
rails (4.1.11)
actionmailer (= 4.1.11)
actionpack (= 4.1.11)
actionview (= 4.1.11)
activemodel (= 4.1.11)
activerecord (= 4.1.11)
activesupport (= 4.1.11)
bundler (>= 1.3.0, < 2.0)
railties (= 4.1.9)
railties (= 4.1.11)
sprockets-rails (~> 2.0)
rails-observers (0.1.2)
activemodel (~> 4.0)
railties (4.1.9)
actionpack (= 4.1.9)
activesupport (= 4.1.9)
railties (4.1.11)
actionpack (= 4.1.11)
activesupport (= 4.1.11)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.0.0)
......@@ -500,7 +500,7 @@ GEM
trollop
rdoc (3.12.2)
json (~> 1.4)
redcarpet (3.2.3)
redcarpet (3.3.1)
redis (3.1.0)
redis-actionpack (4.0.0)
actionpack (~> 4)
......@@ -634,7 +634,7 @@ GEM
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
sprockets-rails (2.2.4)
sprockets-rails (2.3.1)
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
......@@ -779,7 +779,7 @@ DEPENDENCIES
html-pipeline (~> 1.11.0)
httparty
jquery-atwho-rails (~> 1.0.0)
jquery-rails (= 3.1.2)
jquery-rails (= 3.1.3)
jquery-scrollto-rails
jquery-turbolinks
jquery-ui-rails
......@@ -810,10 +810,10 @@ DEPENDENCIES
rack-cors
rack-mini-profiler
rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0)
rails (= 4.1.11)
raphael-rails (~> 2.1.2)
rdoc (~> 3.6)
redcarpet (~> 3.2.3)
redcarpet (~> 3.3.0)
redis-rails
request_store
rerun (~> 0.10.0)
......
......@@ -55,7 +55,7 @@ class Dispatcher
when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation()
MergeRequests.init()
when 'dashboard:show'
when 'dashboard:show', 'root:show'
new Dashboard()
new Activities()
when 'dashboard:projects:starred'
......
......@@ -8,11 +8,12 @@
class @Notes
@interval: null
constructor: (notes_url, note_ids, last_fetched_at) ->
constructor: (notes_url, note_ids, last_fetched_at, view) ->
@notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids
@last_fetched_at = last_fetched_at
@view = view
@noteable_url = document.URL
@initRefresh()
@setupMainTargetNoteForm()
......@@ -131,6 +132,8 @@ class @Notes
isNewNote: (note) ->
$.inArray(note.id, @note_ids) == -1
isParallelView: ->
@view == 'parallel'
###
Render note in discussion area.
......@@ -391,6 +394,7 @@ class @Notes
setupDiscussionNoteForm: (dataHolder, form) =>
# setup note target
form.attr "rel", dataHolder.data("discussionId")
form.find("#line_type").val dataHolder.data("lineType")
form.find("#note_commit_id").val dataHolder.data("commitId")
form.find("#note_line_code").val dataHolder.data("lineCode")
form.find("#note_noteable_type").val dataHolder.data("noteableType")
......@@ -411,19 +415,40 @@ class @Notes
form = $(".js-new-note-form")
row = $(link).closest("tr")
nextRow = row.next()
# does it already have notes?
if nextRow.is(".notes_holder")
replyButton = nextRow.find(".js-discussion-reply-button")
if replyButton.length > 0
$.proxy(@replyToDiscussionNote, replyButton).call()
hasNotes = nextRow.is(".notes_holder")
addForm = false
targetContent = ".notes_content"
rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>"
# In parallel view, look inside the correct left/right pane
if @isParallelView()
lineType = $(link).data("lineType")
targetContent += "." + lineType
rowCssToAdd = "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\"></td><td class=\"notes_content parallel old\"></td><td class=\"notes_line\"></td><td class=\"notes_content parallel new\"></td></tr>"
if hasNotes
notesContent = nextRow.find(targetContent)
if notesContent.length
replyButton = notesContent.find(".js-discussion-reply-button:visible")
if replyButton.length
e.target = replyButton[0]
$.proxy(@replyToDiscussionNote, replyButton[0], e).call()
else
# In parallel view, the form may not be present in one of the panes
noteForm = notesContent.find(".js-discussion-note-form")
if noteForm.length == 0
addForm = true
else
# add a notes row and insert the form
row.after "<tr class=\"notes_holder js-temp-notes-holder\"><td class=\"notes_line\" colspan=\"2\"></td><td class=\"notes_content\"></td></tr>"
form.clone().appendTo row.next().find(".notes_content")
row.after rowCssToAdd
addForm = true
if addForm
newForm = form.clone()
newForm.appendTo row.next().find(targetContent)
# show the form
@setupDiscussionNoteForm $(link), row.next().find("form")
@setupDiscussionNoteForm $(link), newForm
###
Called in response to "cancel" on a diff note form.
......
class @Profile
constructor: ->
$('.edit_user .application-theme input, .edit_user .code-preview-theme input').click ->
# Submit the form
$('.edit_user').submit()
new Flash("Appearance settings saved", "notice")
# Automatically submit the Preferences form when any of its radio buttons change
$('.js-preferences-form').on 'change.preference', 'input[type=radio]', ->
$(this).parents('form').submit()
$('.update-username form').on 'ajax:before', ->
$('.loading-gif').show()
......@@ -18,7 +16,6 @@ class @Profile
$('.update-notifications').on 'ajax:complete', ->
$(this).find('.btn-save').enable()
$('.js-choose-user-avatar-button').bind "click", ->
form = $(this).closest("form")
form.find(".js-user-avatar-input").click()
......
......@@ -35,26 +35,26 @@
*/
@import "font-awesome";
/**
* UI themes:
*/
@import "themes/**/*";
/**
* Generic css (forms, nav etc):
*/
@import "generic/*";
@import "generic/**/*";
/**
* Page specific styles (issues, projects etc):
*/
@import "pages/*";
@import "pages/**/*";
/**
* Code highlight
*/
@import "highlight/*";
/**
* UI themes:
*/
@import "themes/*";
@import "highlight/**/*";
/**
* Styles for JS behaviors.
......
......@@ -98,16 +98,16 @@ header {
height: $header-height;
.title {
position: relative;
float: left;
margin: 0;
margin-left: 35px;
padding: 0 15px 0 35px;
overflow: hidden;
font-size: 18px;
line-height: $header-height;
font-weight: bold;
color: #444;
@include str-truncated(37%);
text-overflow: ellipsis;
vertical-align: top;
white-space: nowrap;
a {
color: #444;
......@@ -116,6 +116,10 @@ header {
}
}
}
.navbar-collapse {
float: right;
}
}
.search {
......@@ -167,10 +171,6 @@ header {
}
@media (max-width: $screen-md-max) {
header .container .title {
max-width: 43%;
}
.header-collapsed, .header-expanded {
@include collapsed-header;
}
......@@ -190,7 +190,6 @@ header {
font-size: 18px;
.title {
max-width: 70%;
}
.navbar-nav {
......
......@@ -61,8 +61,7 @@
}
.container .title {
margin-left: 15px !important;
max-width: 70% !important;
padding-left: 15px !important;
}
.issue-info, .merge-request-info {
......
......@@ -28,10 +28,6 @@
font-size: 14px;
line-height: 24px;
.str-truncated {
max-width: 76%;
}
a {
display: block;
padding: 8px 15px;
......
......@@ -17,67 +17,6 @@
}
}
/*
* Appearance settings
*
*/
.themes_opts {
label {
margin-right: 20px;
text-align: center;
.prev {
height: 80px;
width: 160px;
margin-bottom: 10px;
@include border-radius(4px);
&.classic {
background: #31363e;
}
&.default {
background: #888888;
}
&.modern {
background: #009871;
}
&.gray {
background: #373737;
}
&.violet {
background: #548;
}
&.blue {
background: #2980b9;
}
}
}
}
.code_highlight_opts {
margin-top: 10px;
label {
margin-right: 20px;
text-align: center;
.prev {
width: 160px;
margin-bottom: 10px;
img {
max-width: 100%;
@include border-radius(4px);
}
}
}
}
.oauth-buttons {
.btn-group {
margin-right: 10px;
......
.application-theme {
label {
margin-right: 20px;
text-align: center;
.preview {
@include border-radius(4px);
height: 80px;
margin-bottom: 10px;
width: 160px;
&.ui_blue {
background: $theme-blue;
}
&.ui_charcoal {
background: $theme-charcoal;
}
&.ui_graphite {
background: $theme-graphite;
}
&.ui_gray {
background: $theme-gray;
}
&.ui_green {
background: $theme-green;
}
&.ui_violet {
background: $theme-violet;
}
}
}
}
.syntax-theme {
label {
margin-right: 20px;
text-align: center;
.preview {
margin-bottom: 10px;
width: 160px;
img {
@include border-radius(4px);
max-width: 100%;
}
}
}
}
/**
* Styles the GitLab application with a specific color theme
*
* $color-light -
* $color -
* $color-darker -
* $color-dark -
*/
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
header {
&.navbar-gitlab {
......@@ -77,3 +85,36 @@
}
}
}
$theme-blue: #2980B9;
$theme-charcoal: #474D57;
$theme-graphite: #888888;
$theme-gray: #373737;
$theme-green: #019875;
$theme-violet: #554488;
body {
&.ui_blue {
@include gitlab-theme(#BECDE9, $theme-blue, #1970A9, #096099);
}
&.ui_charcoal {
@include gitlab-theme(#979DA7, $theme-charcoal, #373D47, #24272D);
}
&.ui_graphite {
@include gitlab-theme(#CCCCCC, $theme-graphite, #777777, #666666);
}
&.ui_gray {
@include gitlab-theme(#979797, $theme-gray, #272727, #222222);
}
&.ui_green {
@include gitlab-theme(#AADDCC, $theme-green, #018865, #017855);
}
&.ui_violet {
@include gitlab-theme(#9988CC, $theme-violet, #443366, #332255);
}
}
/**
* This file represent some UI that can be changed
* during web app restyle or theme select.
*
*/
.ui_basic {
@include gitlab-theme(#CCCCCC, #888888, #777777, #666666);
}
/**
* Blue GitLab UI theme
*/
.ui_blue {
@include gitlab-theme(#BECDE9, #2980b9, #1970a9, #096099);
}
/**
* Violet GitLab UI theme
*/
.ui_color {
@include gitlab-theme(#98C, #548, #436, #325);
}
/**
* Gray GitLab UI theme
*/
.ui_gray {
@include gitlab-theme(#979797, #373737, #272727, #222222);
}
/**
* Classic GitLab UI theme
*/
.ui_mars {
@include gitlab-theme(#979DA7, #474D57, #373D47, #24272D);
}
/**
* Modern GitLab UI theme
*/
.ui_modern {
@include gitlab-theme(#ADC, #019875, #018865, #017855);
}
......@@ -41,6 +41,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:help_text,
:after_sign_out_path,
:max_attachment_size,
:session_expire_delay,
:default_project_visibility,
:default_snippet_visibility,
:restricted_signup_domains_raw,
......
class Profiles::PreferencesController < Profiles::ApplicationController
before_action :user
def show
end
def update
begin
if @user.update_attributes(preferences_params)
flash[:notice] = 'Preferences saved.'
else
flash[:alert] = 'Failed to save preferences.'
end
rescue ArgumentError => e
# Raised when `dashboard` is given an invalid value.
flash[:alert] = "Failed to save preferences (#{e.message})."
end
respond_to do |format|
format.html { redirect_to profile_preferences_path }
format.js
end
end
private
def user
@user = current_user
end
def preferences_params
params.require(:user).permit(
:color_scheme_id,
:dashboard,
:theme_id
)
end
end
......@@ -8,9 +8,6 @@ class ProfilesController < Profiles::ApplicationController
def show
end
def design
end
def applications
@applications = current_user.oauth_applications
@authorized_tokens = current_user.oauth_authorized_tokens
......@@ -29,7 +26,6 @@ class ProfilesController < Profiles::ApplicationController
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
......@@ -65,10 +61,21 @@ class ProfilesController < Profiles::ApplicationController
def user_params
params.require(:user).permit(
:email, :password, :password_confirmation, :bio, :name,
:username, :skype, :linkedin, :twitter, :website_url,
:color_scheme_id, :theme_id, :avatar, :hide_no_ssh_key,
:hide_no_password, :location, :public_email
:avatar,
:bio,
:email,
:hide_no_password,
:hide_no_ssh_key,
:linkedin,
:location,
:name,
:password,
:password_confirmation,
:public_email,
:skype,
:twitter,
:username,
:website_url
)
end
end
......@@ -77,11 +77,24 @@ class Projects::NotesController < Projects::ApplicationController
end
def note_to_discussion_html(note)
if params[:view] == 'parallel'
template = "projects/notes/_diff_notes_with_reply_parallel"
locals =
if params[:line_type] == 'old'
{ notes_left: [note], notes_right: [] }
else
{ notes_left: [], notes_right: [note] }
end
else
template = "projects/notes/_diff_notes_with_reply"
locals = { notes: [note] }
end
render_to_string(
"projects/notes/_diff_notes_with_reply",
template,
layout: false,
formats: [:html],
locals: { notes: [note] }
locals: locals
)
end
......
# RootController
#
# This controller exists solely to handle requests to `root_url`. When a user is
# logged in and has customized their `dashboard` setting, they will be
# redirected to their preferred location.
#
# For users who haven't customized the setting, we simply delegate to
# `DashboardController#show`, which is the default.
class RootController < DashboardController
before_action :redirect_to_custom_dashboard, only: [:show]
def show
super
end
private
def redirect_to_custom_dashboard
return unless current_user
case current_user.dashboard
when 'stars'
redirect_to starred_dashboard_projects_path
else
return
end
end
end
......@@ -2,26 +2,6 @@ require 'digest/md5'
require 'uri'
module ApplicationHelper
COLOR_SCHEMES = {
1 => 'white',
2 => 'dark',
3 => 'solarized-light',
4 => 'solarized-dark',
5 => 'monokai',
}
COLOR_SCHEMES.default = 'white'
# Helper method to access the COLOR_SCHEMES
#
# The keys are the `color_scheme_ids`
# The values are the `name` of the scheme.
#
# The preview images are `name-scheme-preview.png`
# The stylesheets should use the css class `.name`
def color_schemes
COLOR_SCHEMES.freeze
end
# Check if a particular controller is the current one
#
# args - One or more controller names to check
......@@ -138,18 +118,6 @@ module ApplicationHelper
Emoji.names.to_s
end
def app_theme
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end
def theme_type
Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id))
end
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
# Define whenever show last push event
# with suggestion to create MR
def show_last_push_widget?(event)
......
......@@ -2,6 +2,7 @@ require 'nokogiri'
module GitlabMarkdownHelper
include Gitlab::Markdown
include PreferencesHelper
# Use this in places where you would normally use link_to(gfm(...), ...).
#
......
......@@ -47,7 +47,7 @@ module NotesHelper
}.to_json
end
def link_to_new_diff_note(line_code)
def link_to_new_diff_note(line_code, line_type = nil)
discussion_id = Note.build_discussion_id(
@comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id],
......@@ -59,7 +59,8 @@ module NotesHelper
noteable_id: @comments_target[:noteable_id],
commit_id: @comments_target[:commit_id],
line_code: line_code,
discussion_id: discussion_id
discussion_id: discussion_id,
line_type: line_type
}
button_tag(class: 'btn add-diff-note js-add-diff-note-button',
......@@ -69,7 +70,7 @@ module NotesHelper
end
end
def link_to_reply_diff(note)
def link_to_reply_diff(note, line_type = nil)
return unless current_user
data = {
......@@ -77,7 +78,8 @@ module NotesHelper
noteable_id: note.noteable_id,
commit_id: note.commit_id,
line_code: note.line_code,
discussion_id: note.discussion_id
discussion_id: note.discussion_id,
line_type: line_type
}
button_tag class: 'btn reply-btn js-discussion-reply-button',
......
# Helper methods for per-User preferences
module PreferencesHelper
COLOR_SCHEMES = {
1 => 'white',
2 => 'dark',
3 => 'solarized-light',
4 => 'solarized-dark',
5 => 'monokai',
}
COLOR_SCHEMES.default = 'white'
# Helper method to access the COLOR_SCHEMES
#
# The keys are the `color_scheme_ids`
# The values are the `name` of the scheme.
#
# The preview images are `name-scheme-preview.png`
# The stylesheets should use the css class `.name`
def color_schemes
COLOR_SCHEMES.freeze
end
# Maps `dashboard` values to more user-friendly option text
DASHBOARD_CHOICES = {
projects: 'Your Projects (default)',
stars: 'Starred Projects'
}.with_indifferent_access.freeze
# Returns an Array usable by a select field for more user-friendly option text
def dashboard_choices
defined = User.dashboards
if defined.size != DASHBOARD_CHOICES.size
# Ensure that anyone adding new options updates this method too
raise RuntimeError, "`User` defines #{defined.size} dashboard choices," +
" but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}."
else
defined.map do |key, _|
# Use `fetch` so `KeyError` gets raised when a key is missing
[DASHBOARD_CHOICES.fetch(key), key]
end
end
end
def user_application_theme
theme = Gitlab::Themes.by_id(current_user.try(:theme_id))
theme.css_class
end
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
end
......@@ -16,6 +16,7 @@
# help_text :text
# restricted_visibility_levels :text
# max_attachment_size :integer default(10), not null
# session_expire_delay :integer default(10080), not null
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
......@@ -28,6 +29,10 @@ class ApplicationSetting < ActiveRecord::Base
serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw
validates :session_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
validates :home_page_url,
allow_blank: true,
format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" },
......@@ -62,6 +67,7 @@ class ApplicationSetting < ActiveRecord::Base
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains']
......
......@@ -31,7 +31,7 @@ class Note < ActiveRecord::Base
participant :author, :mentioned_users
belongs_to :project
belongs_to :noteable, polymorphic: true
belongs_to :noteable, polymorphic: true, touch: true
belongs_to :author, class_name: "User"
delegate :name, to: :project, prefix: true
......
......@@ -752,4 +752,12 @@ class Project < ActiveRecord::Base
errors.add(:base, 'Failed create wiki')
false
end
def reference_issue_tracker?
default_issues_tracker? || jira_tracker_active?
end
def jira_tracker_active?
jira_tracker? && jira_service.active
end
end
......@@ -40,10 +40,14 @@ class GitlabCiService < CiService
def execute(data)
return unless supported_events.include?(data[:object_kind])
ci_yaml_file = ci_yaml_file(data)
sha = data[:checkout_sha]
if ci_yaml_file
data.merge!(ci_yaml_file: ci_yaml_file)
if sha.present?
file = ci_yaml_file(sha)
if file && file.data
data.merge!(ci_yaml_file: file.data)
end
end
service_hook.execute(data)
......@@ -129,15 +133,15 @@ class GitlabCiService < CiService
private
def ci_yaml_file(data)
ref = data[:checkout_sha]
repo = project.repository
commit = repo.commit(ref)
blob = Gitlab::Git::Blob.find(repo, commit.id, ".gitlab-ci.yml")
blob && blob.data
def ci_yaml_file(sha)
repository.blob_at(sha, '.gitlab-ci.yml')
end
def fork_registration_path
project_url.sub(/projects\/\d*/, "#{API_PREFIX}/forks")
end
def repository
project.repository
end
end
......@@ -85,18 +85,13 @@ class IssueTrackerService < Service
result = false
begin
url = URI.parse(self.project_url)
response = HTTParty.head(self.project_url, verify: true)
if url.host && url.port
http = Net::HTTP.start(url.host, url.port, { open_timeout: 5, read_timeout: 5 })
response = http.head("/")
if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
end
if response
message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
result = true
end
rescue Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error
rescue HTTParty::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error
message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
end
Rails.logger.info(message)
......
......@@ -50,12 +50,13 @@
# bitbucket_access_token :string(255)
# bitbucket_access_token_secret :string(255)
# location :string(255)
# public_email :string(255) default(""), not null
# encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean
# otp_backup_codes :text
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
#
require 'carrierwave/orm/activerecord'
......@@ -734,4 +735,8 @@ class User < ActiveRecord::Base
def can_be_removed?
!solo_owned_groups.present?
end
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars]
end
......@@ -92,18 +92,24 @@ class GitPushService
end
end
# Create cross-reference notes for any other references. Omit any issues that were referenced in an
# issue-closing phrase, or have already been mentioned from this commit (probably from this commit
# being pushed to a different branch).
refs = commit.references(project, user) - issues_to_close
refs.reject! { |r| commit.has_mentioned?(r) }
if project.reference_issue_tracker?
create_cross_reference_notes(commit, issues_to_close)
end
end
end
if refs.present?
author ||= commit_user(commit)
def create_cross_reference_notes(commit, issues_to_close)
# Create cross-reference notes for any other references. Omit any issues that were referenced in an
# issue-closing phrase, or have already been mentioned from this commit (probably from this commit
# being pushed to a different branch).
refs = commit.references(project, user) - issues_to_close
refs.reject! { |r| commit.has_mentioned?(r) }
refs.each do |r|
Note.create_cross_reference_note(r, commit, author)
end
if refs.present?
author ||= commit_user(commit)
refs.each do |r|
Note.create_cross_reference_note(r, commit, author)
end
end
end
......
module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
if issue.close
if project.default_issues_tracker? && issue.close
event_service.close_issue(issue, current_user)
create_note(issue, commit)
notification_service.close_issue(issue, current_user)
......
......@@ -216,13 +216,15 @@ class SystemNoteService
# Check if a cross-reference is disallowed
#
# This method prevents adding a "mentioned in !1" note on every single commit
# in a merge request.
# in a merge request. Additionally, it prevents the creation of references to
# external issues (which would fail).
#
# noteable - Noteable object being referenced
# mentioner - Mentionable object
#
# Returns Boolean
def self.cross_reference_disallowed?(noteable, mentioner)
return true if noteable.is_a?(ExternalIssue) && !noteable.project.jira_tracker_active?
return false unless mentioner.is_a?(MergeRequest)
return false unless noteable.is_a?(Commit)
......
......@@ -88,6 +88,11 @@
= f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :max_attachment_size, class: 'form-control', min: 0
.form-group
= f.label :session_expire_delay, 'Session duration (minutes)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :session_expire_delay, class: 'form-control'
%span.help-block#session_expire_delay_help_block GitLab restart is required to apply changes
.form-group
= f.label :restricted_signup_domains, 'Restricted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
......
......@@ -12,8 +12,7 @@
- if @group.new_record?
.form-group
.col-sm-2
.col-sm-10
.col-sm-offset-2.col-sm-10
.alert.alert-info
= render 'shared/group_tips'
.form-actions
......
......@@ -92,8 +92,7 @@
= namespace_select_tag :new_namespace_id, selected: params[:namespace_id], class: 'input-large'
.form-group
.col-sm-2
.col-sm-10
.col-sm-offset-2.col-sm-10
= f.submit 'Transfer', class: 'btn btn-primary'
.col-md-6
......
= form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f|
- if application.errors.any?
.alert.alert-danger{"data-alert" => ""}
%p Whoops! Check your form for possible errors
= content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do
= f.label :name, class: 'col-sm-2 control-label'
.alert.alert-danger
%ul
- application.errors.full_messages.each do |msg|
%li= msg
.form-group
= f.label :name, class: 'control-label'
.col-sm-10
= f.text_field :name, class: 'form-control'
= doorkeeper_errors_for application, :name
= content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do
= f.label :redirect_uri, class: 'col-sm-2 control-label'
= f.text_field :name, class: 'form-control', required: true
.form-group
= f.label :redirect_uri, class: 'control-label'
.col-sm-10
= f.text_area :redirect_uri, class: 'form-control'
= doorkeeper_errors_for application, :redirect_uri
= f.text_area :redirect_uri, class: 'form-control', required: true
%span.help-block
Use one line per URI
- if Doorkeeper.configuration.native_redirect_uri
......@@ -19,6 +24,7 @@
Use
%code= Doorkeeper.configuration.native_redirect_uri
for local tests
.form-actions
= f.submit 'Submit', class: "btn btn-primary wide"
= link_to "Cancel", applications_profile_path, class: "btn btn-default"
= f.submit 'Submit', class: "btn btn-create"
= link_to "Cancel", applications_profile_path, class: "btn btn-cancel"
%h3.page-title New application
- page_title "New Application"
%h3.page-title New Application
%hr
= render 'form', application: @application
\ No newline at end of file
......@@ -4,7 +4,7 @@
%span You pushed to
= link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do
%strong= event.ref_name
at
%span at
%strong= link_to_project event.project
#{time_ago_with_tooltip(event.created_at)}
......
......@@ -13,6 +13,3 @@
.public-projects
%ul.bordered-list
= render @trending_projects
.center.append-bottom-20
= link_to 'Show all projects', explore_projects_path, class: 'btn btn-primary'
......@@ -11,8 +11,7 @@
= render 'shared/group_form', f: f
.form-group
.col-sm-2
.col-sm-10
.col-sm-offset-2.col-sm-10
= image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
%p.light
- if @group.avatar?
......
......@@ -13,8 +13,7 @@
= render 'shared/choose_group_avatar_button', f: f
.form-group
.col-sm-2
.col-sm-10
.col-sm-offset-2.col-sm-10
= render 'shared/group_tips'
.form-actions
......
#modal-shortcuts.modal.hide{tabindex: -1}
#modal-shortcuts.modal{tabindex: -1}
.modal-dialog
.modal-content
.modal-header
......
!!! 5
%html{ lang: "en"}
= render "layouts/head"
%body{class: "#{app_theme}", :'data-page' => body_data_page}
/ Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body.
%body{class: "#{user_application_theme}", 'data-page' => body_data_page}
-# Ideally this would be inside the head, but turbolinks only evaluates page-specific JS in the body.
= yield :scripts_body_top
- if current_user
= render "layouts/header/default", title: header_title
- else
......
!!! 5
%html{ lang: "en"}
= render "layouts/head"
%body.ui_mars.login-page.application
%body.ui_charcoal.login-page.application
= render "layouts/header/empty"
= render "layouts/broadcast"
.container.navless-container
......
!!! 5
%html{ lang: "en"}
= render "layouts/head"
%body{class: "#{app_theme} application"}
%body{class: "#{user_application_theme} application"}
= render "layouts/header/empty"
.container.navless-container
= render "layouts/flash"
......
......@@ -5,10 +5,7 @@
= brand_header_logo
%h3 GitLab
.header-content
%h1.title
= title
%button.navbar-toggle
%button.navbar-toggle{type: 'button'}
%span.sr-only Toggle navigation
= icon('bars')
......@@ -43,4 +40,6 @@
= link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do
= icon('sign-out')
%h1.title= title
= render 'shared/outdated_browser'
......@@ -5,10 +5,10 @@
= brand_header_logo
%h3 GitLab
.header-content
%h1.title= title
- unless current_controller?('sessions')
.pull-right
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success btn-sm'
%h1.title= title
= render 'shared/outdated_browser'
%ul.nav.nav-sidebar
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to root_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
= nav_link(path: ['dashboard#show', 'root#show'], html_options: {class: 'home'}) do
= link_to dashboard_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
= icon('dashboard fw')
%span
Your Projects
......
......@@ -9,7 +9,7 @@
= icon('gear fw')
%span
Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new', 'applications#create']) do
= link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do
= icon('cloud fw')
%span
......@@ -38,11 +38,12 @@
%span
SSH Keys
%span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do
= link_to design_profile_path, title: 'Design', data: {placement: 'right'} do
= nav_link(controller: :preferences) do
= link_to profile_preferences_path, title: 'Preferences', data: {placement: 'right'} do
-# TODO (rspeicher): Better icon?
= icon('image fw')
%span
Design
Preferences
= nav_link(path: 'profiles#history') do
= link_to history_profile_path, title: 'History', data: {placement: 'right'} do
= icon('history fw')
......
- page_title "Account"
%h3.page-title
= page_title
%p.light
Change your username and basic account settings.
%hr
- if current_user.ldap_user?
.alert.alert-info
Some options are unavailable for LDAP accounts
.account-page
.panel.panel-default.update-token
.panel-heading
Reset Private token
.panel-body
= form_for @user, url: reset_private_token_profile_path, method: :put do |f|
.data
%p
Your private token is used to access application resources without authentication.
%br
It can be used for atom feeds or the API.
%span.cred
Keep it secret!
%p.cgray
- if current_user.private_token
= text_field_tag "token", current_user.private_token, class: "form-control"
%div
= f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token"
- else
%span You don`t have one yet. Click generate to fix it.
= f.submit 'Generate', class: "btn btn-default btn-build-token"
.panel.panel-default
.panel-heading
Two-factor Authentication
.panel-body
- if current_user.otp_required_for_login
.pull-right
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
data: { confirm: 'Are you sure?' }
%p.text-success
%strong
Two-factor Authentication is enabled
%p
If you lose your recovery codes you can
%strong
= succeed ',' do
= link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
invalidating all previous codes.
- else
%p
Increase your account's security by enabling two-factor authentication (2FA).
%p
Each time you log in you’ll be required to provide your username and
password as usual, plus a randomly-generated code from your phone.
%div
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if show_profile_social_tab?
.panel.panel-default
.panel-heading
Connected Accounts
.panel-body
.oauth-buttons.append-bottom-10
%p Click on icon to activate signin with one of the following services
- enabled_social_providers.each do |provider|
.btn-group
= link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider),
method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
- if oauth_active?(provider)
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
= icon('close')
- if show_profile_username_tab?
.panel.panel-warning.update-username
.panel-heading
Change Username
.panel-body
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
%p
Changing your username will change path to all personal projects!
%div
= f.text_field :username, required: true, class: 'form-control'
&nbsp;
.loading-gif.hide
%p
= icon('spinner spin')
Saving new username
%p.light
= user_url(@user)
%div
= f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab?
.panel.panel-danger.remove-account
.panel-heading
Remove account
.panel-body
- if @user.can_be_removed?
%p Deleting an account has the following effects:
%ul
%li All user content like authored issues, snippets, comments will be removed
- rp = current_user.personal_projects.count
- unless rp.zero?
%li #{pluralize rp, 'personal project'} will be removed and cannot be restored
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
- else
- if @user.solo_owned_groups.present?
%p
Your account is currently an owner in these groups:
%strong #{@user.solo_owned_groups.map(&:name).join(', ')}
%p
You must transfer ownership or delete these groups before you can delete your account.
\ No newline at end of file
- page_title "Account"
%h3.page-title
= page_title
%p.light
Change your username and basic account settings.
%hr
- if current_user.ldap_user?
.alert.alert-info
Some options are unavailable for LDAP accounts
.account-page
.panel.panel-default.update-token
.panel-heading
Reset Private token
.panel-body
= form_for @user, url: reset_private_token_profile_path, method: :put do |f|
.data
%p
Your private token is used to access application resources without authentication.
%br
It can be used for atom feeds or the API.
%span.cred
Keep it secret!
%p.cgray
- if current_user.private_token
= text_field_tag "token", current_user.private_token, class: "form-control"
%div
= f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token"
- else
%span You don`t have one yet. Click generate to fix it.
= f.submit 'Generate', class: "btn btn-default btn-build-token"
.panel.panel-default
.panel-heading
Two-factor Authentication
.panel-body
- if current_user.otp_required_for_login
.pull-right
= link_to 'Disable Two-factor Authentication', profile_two_factor_auth_path, method: :delete, class: 'btn btn-close btn-sm',
data: { confirm: 'Are you sure?' }
%p.text-success
%strong
Two-factor Authentication is enabled
%p
If you lose your recovery codes you can
%strong
= succeed ',' do
= link_to 'generate new ones', codes_profile_two_factor_auth_path, method: :post, data: { confirm: 'Are you sure?' }
invalidating all previous codes.
- else
%p
Increase your account's security by enabling two-factor authentication (2FA).
%p
Each time you log in you’ll be required to provide your username and
password as usual, plus a randomly-generated code from your phone.
%div
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if show_profile_social_tab?
.panel.panel-default
.panel-heading
Connected Accounts
.panel-body
.oauth-buttons.append-bottom-10
%p Click on icon to activate signin with one of the following services
- enabled_social_providers.each do |provider|
.btn-group
= link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider),
method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
- if oauth_active?(provider)
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
= icon('close')
- if show_profile_username_tab?
.panel.panel-warning.update-username
.panel-heading
Change Username
.panel-body
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
%p
Changing your username will change path to all personal projects!
%div
= f.text_field :username, required: true, class: 'form-control'
&nbsp;
.loading-gif.hide
%p
= icon('spinner spin')
Saving new username
%p.light
= user_url(@user)
%div
= f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab?
.panel.panel-danger.remove-account
.panel-heading
Remove account
.panel-body
- if @user.can_be_removed?
%p Deleting an account has the following effects:
%ul
%li All user content like authored issues, snippets, comments will be removed
- rp = current_user.personal_projects.count
- unless rp.zero?
%li #{pluralize rp, 'personal project'} will be removed and cannot be restored
= link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
- else
- if @user.solo_owned_groups.present?
%p
Your account is currently an owner in these groups:
%strong #{@user.solo_owned_groups.map(&:name).join(', ')}
%p
You must transfer ownership or delete these groups before you can delete your account.
- page_title "Design"
%h3.page-title
= page_title
%p.light
Appearance settings will be saved to your profile and made available across all devices.
%hr
= form_for @user, url: profile_path, remote: true, method: :put do |f|
.panel.panel-default.application-theme
.panel-heading
Application theme
.panel-body
.themes_opts
= label_tag do
.prev.default
= f.radio_button :theme_id, 1
Graphite
= label_tag do
.prev.classic
= f.radio_button :theme_id, 2
Charcoal
= label_tag do
.prev.modern
= f.radio_button :theme_id, 3
Green
= label_tag do
.prev.gray
= f.radio_button :theme_id, 4
Gray
= label_tag do
.prev.violet
= f.radio_button :theme_id, 5
Violet
= label_tag do
.prev.blue
= f.radio_button :theme_id, 6
Blue
%br
.clearfix
.panel.panel-default.code-preview-theme
.panel-heading
Code preview theme
.panel-body
.code_highlight_opts
- color_schemes.each do |color_scheme_id, color_scheme|
= label_tag do
.prev
= image_tag "#{color_scheme}-scheme-preview.png"
= f.radio_button :color_scheme_id, color_scheme_id
= color_scheme.gsub(/[-_]+/, ' ').humanize
- page_title 'Preferences'
%h3.page-title
= page_title
%p.light
These settings allow you to customize the appearance and behavior of the site.
They are saved with your account and will persist to any device you use to
access the site.
%hr
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'js-preferences-form form-horizontal'} do |f|
.panel.panel-default.application-theme
.panel-heading
Application theme
.panel-body
- Gitlab::Themes.each do |theme|
= label_tag do
.preview{class: theme.css_class}
= f.radio_button :theme_id, theme.id
= theme.name
.panel.panel-default.syntax-theme
.panel-heading
Syntax highlighting theme
.panel-body
- color_schemes.each do |color_scheme_id, color_scheme|
= label_tag do
.preview= image_tag "#{color_scheme}-scheme-preview.png"
= f.radio_button :color_scheme_id, color_scheme_id
= color_scheme.tr('-_', ' ').titleize
.panel.panel-default
.panel-heading
Behavior
.panel-body
.form-group
= f.label :dashboard, class: 'control-label' do
Default Dashboard
= link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank')
.col-sm-10
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
.panel-footer
= f.submit 'Save', class: 'btn btn-save'
// Remove body class for any previous theme, re-add current one
$('body').removeClass('<%= Gitlab::Themes.body_classes %>')
$('body').addClass('<%= user_application_theme %>')
// Re-enable the "Save" button
$('input[type=submit]').enable()
// Show the notice flash message
new Flash('<%= flash.discard(:notice) %>', 'notice')
......@@ -109,6 +109,5 @@
.row
.col-md-7
.form-group
.col-sm-2 &nbsp;
.col-sm-10
.col-sm-offset-2.col-sm-10
= f.submit 'Save changes', class: "btn btn-success"
// Remove body class for any previous theme, re-add current one
$('body').removeClass('<%= Gitlab::Theme.body_classes %>')
$('body').addClass('<%= app_theme %> <%= theme_type %>')
%div#bitbucket_import_modal.modal.hide
%div#bitbucket_import_modal.modal
.modal-dialog
.modal-content
.modal-header
......
%div#github_import_modal.modal.hide
%div#github_import_modal.modal
.modal-dialog
.modal-content
.modal-header
......
%div#gitlab_import_modal.modal.hide
%div#gitlab_import_modal.modal
.modal-dialog
.modal-content
.modal-header
......
- if issuable.errors.any?
.row
.col-sm-10.col-sm-offset-2
.col-sm-offset-2.col-sm-10
.alert.alert-danger
- issuable.errors.full_messages.each do |msg|
%span= msg
......
#modal-remove-blob.modal.hide
#modal-remove-blob.modal
.modal-dialog
.modal-content
.modal-header
......@@ -13,8 +13,7 @@
= render 'shared/commit_message_container', params: params,
placeholder: 'Removed this file because...'
.form-group
.col-sm-2
.col-sm-10
.col-sm-offset-2.col-sm-10
= button_tag 'Remove file', class: 'btn btn-remove btn-remove-file'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
......
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "commit_box"
= render "projects/diffs/diffs", diffs: @diffs, project: @project
= render "projects/notes/notes_with_form"
= render "projects/notes/notes_with_form", view: params[:view]
......@@ -18,6 +18,8 @@
- elsif type_left == 'old' || type_left.nil?
%td.old_line{id: line_code_left, class: "#{type_left}"}
= link_to raw(line_number_left), "##{line_code_left}", id: line_code_left
- if @comments_allowed && can?(current_user, :write_note, @project)
= link_to_new_diff_note(line_code_left, 'old')
%td.line_content{class: "parallel noteable_line #{type_left} #{line_code_left}", "line_code" => line_code_left }= raw line_content_left
- if type_right == 'new'
......@@ -29,12 +31,14 @@
%td.new_line{id: new_line_code, class: "#{new_line_class}", data: { linenumber: line_number_right }}
= link_to raw(line_number_right), "##{new_line_code}", id: new_line_code
- if @comments_allowed && can?(current_user, :write_note, @project)
= link_to_new_diff_note(line_code_right, 'new')
%td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", "line_code" => new_line_code}= raw line_content_right
- if @reply_allowed
- comments_left, comments_right = organize_comments(type_left, type_right, line_code_left, line_code_right)
- if comments_left.present? || comments_right.present?
= render "projects/notes/diff_notes_with_reply_parallel", notes1: comments_left, notes2: comments_right
= render "projects/notes/diff_notes_with_reply_parallel", notes_left: comments_left, notes_right: comments_right
- if diff_file.diff.diff.blank? && diff_file.mode_changed?
.file-mode-changed
......
......@@ -82,8 +82,7 @@
%legend
Project avatar:
.form-group
.col-sm-2
.col-sm-10
.col-sm-offset-2.col-sm-10
- if @project.avatar?
= project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
%p.light
......
= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form' } do |f|
-if @label.errors.any?
.row
.col-sm-10.col-sm-offset-2
.col-sm-offset-2.col-sm-10
.alert.alert-danger
- @label.errors.full_messages.each do |msg|
%span= msg
......
%div#modal_merge_info.modal.hide
%div#modal_merge_info.modal
.modal-dialog
.modal-content
.modal-header
......
- note1 = notes1.present? ? notes1.first : nil
- note2 = notes2.present? ? notes2.first : nil
- note1 = notes_left.present? ? notes_left.first : nil
- note2 = notes_right.present? ? notes_right.first : nil
%tr.notes_holder
- if note1
%td.notes_line
%td.notes_line.old
%span.btn.disabled
%i.fa.fa-comment
= notes1.count
%td.notes_content.parallel
= notes_left.count
%td.notes_content.parallel.old
%ul.notes{ rel: note1.discussion_id }
= render notes1
= render notes_left
.discussion-reply-holder
= link_to_reply_diff(note1)
= link_to_reply_diff(note1, 'old')
- else
%td= ""
%td= ""
%td.notes_line.old= ""
%td.notes_content.parallel.old= ""
- if note2
%td.notes_line
%td.notes_line.new
%span.btn.disabled
%i.fa.fa-comment
= notes2.count
%td.notes_content.parallel
= notes_right.count
%td.notes_content.parallel.new
%ul.notes{ rel: note2.discussion_id }
= render notes2
= render notes_right
.discussion-reply-holder
= link_to_reply_diff(note2)
= link_to_reply_diff(note2, 'new')
- else
%td= ""
%td= ""
%td.notes_line.new= ""
%td.notes_content.parallel.new= ""
= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
= hidden_field_tag :view, params[:view]
= hidden_field_tag :line_type
= note_target_fields(@note)
= f.hidden_field :commit_id
= f.hidden_field :line_code
......
......@@ -4,7 +4,7 @@
.js-main-target-form
- if can? current_user, :write_note, @project
= render "projects/notes/form"
= render "projects/notes/form", view: params[:view]
:javascript
new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i})
new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}")
......@@ -12,8 +12,7 @@
= f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control"
.row
.col-sm-2
.col-sm-10
.col-sm-offset-2.col-sm-10
%p.cgray
To link to a (new) page you can just type
%code [Link Title](page-slug)
......
%div#modal-new-wiki.modal.hide
%div#modal-new-wiki.modal
.modal-dialog
.modal-content
.modal-header
......
......@@ -3,6 +3,7 @@
.input-group-addon.git-protocols
.input-group-btn
%button{ |
type: 'button', |
class: "btn btn-sm #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
:"data-clone" => project.ssh_url_to_repo, |
:"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH",
......@@ -11,6 +12,7 @@
SSH
.input-group-btn
%button{ |
type: 'button', |
class: "btn btn-sm #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
:"data-clone" => project.http_url_to_repo, |
:"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}",
......
#modal-confirm-danger.modal.hide{tabindex: -1}
#modal-confirm-danger.modal{tabindex: -1}
.modal-dialog
.modal-content
.modal-header
......
......@@ -3,8 +3,6 @@
- if avatar
.dash-project-avatar
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
%span.str-truncated
%span.namespace-name
- if project.namespace
......
......@@ -12,8 +12,8 @@
= render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet
.form-group
.file-editor
.file-editor
.form-group
= f.label :file_name, "File", class: 'control-label'
.col-sm-10
.file-holder.snippet
......
......@@ -62,12 +62,13 @@ production: &base
# default_can_create_group: false # default: true
# username_changing_enabled: false # default: true - User can change her username/namespace
## Default theme
## BASIC = 1
## MARS = 2
## MODERN = 3
## GRAY = 4
## COLOR = 5
## Default theme ID
## 1 - Graphite
## 2 - Charcoal
## 3 - Green
## 4 - Gray
## 5 - Violet
## 6 - Blue
# default_theme: 2 # default: 2
## Automatic issue closing
......
......@@ -136,7 +136,7 @@ Settings['gitlab'] ||= Settingslogic.new({})
Settings.gitlab['default_projects_limit'] ||= 10
Settings.gitlab['default_branch_protection'] ||= 2
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Theme::MARS if Settings.gitlab['default_theme'].nil?
Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?
Settings.gitlab['host'] ||= 'localhost'
Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
......@@ -164,6 +164,7 @@ Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
Settings.gitlab['session_expire_delay'] ||= 10080
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
......
......@@ -20,3 +20,11 @@ OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_s
OmniAuth.config.before_request_phase do |env|
OmniAuth::RequestForgeryProtection.new(env).call
end
if Gitlab.config.omniauth.enabled
Gitlab.config.omniauth.providers.each do |provider|
if provider['name'] == 'kerberos'
require 'omniauth-kerberos'
end
end
end
# Be sure to restart your server when you modify this file.
require 'gitlab/current_settings'
include Gitlab::CurrentSettings
Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay
Gitlab::Application.config.session_store(
:redis_store, # Using the cookie_store would enable session replay attacks.
servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store
key: '_gitlab_session',
secure: Gitlab.config.gitlab.https,
httponly: true,
expire_after: 1.week,
expire_after: Settings.gitlab['session_expire_delay'] * 60,
path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root
)
......@@ -223,7 +223,6 @@ Gitlab::Application.routes.draw do
resource :profile, only: [:show, :update] do
member do
get :history
get :design
get :applications
put :reset_private_token
......@@ -242,6 +241,7 @@ Gitlab::Application.routes.draw do
put :reset
end
end
resource :preferences, only: [:show, :update]
resources :keys
resources :emails, only: [:index, :create, :destroy]
resource :avatar, only: [:destroy]
......@@ -330,7 +330,7 @@ Gitlab::Application.routes.draw do
get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
end
root to: "dashboard#show"
root to: "root#show"
#
# Project Area
......
......@@ -12,7 +12,7 @@ admin = User.create(
username: 'root',
password: password,
password_expires_at: expire_time,
theme_id: Gitlab::Theme::MARS
theme_id: Gitlab::Themes::APPLICATION_DEFAULT
)
......
class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :session_expire_delay, :integer, default: 10080, null: false
end
end
\ No newline at end of file
class AddDashboardToUsers < ActiveRecord::Migration
def up
add_column :users, :dashboard, :integer, default: 0
end
def down
remove_column :users, :dashboard
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150609125332) do
ActiveRecord::Schema.define(version: 20150610065936) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -40,13 +40,14 @@ ActiveRecord::Schema.define(version: 20150609125332) do
t.boolean "twitter_sharing_enabled", default: true
t.text "help_text"
t.text "restricted_visibility_levels"
t.integer "max_attachment_size", default: 10, null: false
t.boolean "version_check_enabled", default: true
t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility"
t.integer "default_snippet_visibility"
t.text "restricted_signup_domains"
t.boolean "version_check_enabled", default: true
t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false
end
create_table "approvals", force: true do |t|
......@@ -579,12 +580,13 @@ ActiveRecord::Schema.define(version: 20150609125332) do
t.string "bitbucket_access_token"
t.string "bitbucket_access_token_secret"
t.string "location"
t.string "public_email", default: "", null: false
t.string "encrypted_otp_secret"
t.string "encrypted_otp_secret_iv"
t.string "encrypted_otp_secret_salt"
t.boolean "otp_required_for_login"
t.text "otp_backup_codes"
t.string "public_email", default: "", null: false
t.integer "dashboard", default: 0
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
......
......@@ -3,35 +3,36 @@
## User documentation
- [API](api/README.md) Automate GitLab via a simple and powerful API.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
- [Importing to GitLab](workflow/importing/README.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
- [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Web hooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
## Administrator documentation
- [Install](install/README.md) Requirements, directory structures and installation from source.
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
- [Update](update/README.md) Update guides to upgrade your installation.
- [Audit Events](administration/audit_events.md) Check how user access changed in projects and groups.
- [Changing the appearance of the login page](customization/branded_login_page.md) Make the login page branded for your GitLab instance.
- [Help message](customization/help_message.md) Set information about administrators of your GitLab instance.
- [Git Hooks](git_hooks/git_hooks.md) Advanced push rules for your project.
- [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
- [Email](tools/email.md) Email GitLab users from GitLab
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
- [Git Hooks](git_hooks/git_hooks.md) Advanced push rules for your project.
- [Help message](customization/help_message.md) Set information about administrators of your GitLab instance.
- [Install](install/README.md) Requirements, directory structures and installation from source.
- [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
- [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
- [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
- [Log system](logs/logs.md) Log system.
- [Operations](operations/README.md) Keeping GitLab up and running
- [Log system](logs/logs.md) Log system
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects.
- [Security](security/README.md) Learn what you can do to further secure your GitLab instance.
- [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed.
- [Update](update/README.md) Update guides to upgrade your installation.
- [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
## Contributor documentation
......
......@@ -4,6 +4,9 @@ Sometimes it is useful to import the database from a production environment
into a staging environment for testing. The procedure below assumes you have
SSH+sudo access to both the production environment and the staging VM.
**Destroy your staging VM** when you are done with it. It is important to avoid
data leaks.
On the staging VM, add the following line to `/etc/gitlab/gitlab.rb` to speed up
large database imports.
......@@ -12,6 +15,8 @@ large database imports.
echo "postgresql['checkpoint_segments'] = 64" | sudo tee -a /etc/gitlab/gitlab.rb
sudo touch /etc/gitlab/skip-auto-migrations
sudo gitlab-ctl reconfigure
sudo gitlab-ctl stop unicorn
sudo gitlab-ctl stop sidekiq
```
Next, we let the production environment stream a compressed SQL dump to our
......
......@@ -177,3 +177,33 @@ File.open(full_path) do # Etc.
```
A check like this could have avoided CVE-2013-4583.
## Properly anchor regular expressions to the start and end of strings
When using regular expressions to validate user input that is passed as an argument to a shell command, make sure to use the `\A` and `\z` anchors that designate the start and end of the string, rather than `^` and `$`, or no anchors at all.
If you don't, an attacker could use this to execute commands with potentially harmful effect.
For example, when a project's `import_url` is validated like below, the user could trick GitLab into cloning from a Git repository on the local filesystem.
```ruby
validates :import_url, format: { with: URI.regexp(%w(ssh git http https)) }
# URI.regexp(%w(ssh git http https)) roughly evaluates to /(ssh|git|http|https):(something_that_looks_like_a_url)/
```
Suppose the user submits the following as their import URL:
```
file://git:/tmp/lol
```
Since there are no anchors in the used regular expression, the `git:/tmp/lol` in the value would match, and the validation would pass.
When importing, GitLab would execute the following command, passing the `import_url` as an argument:
```sh
git clone file://git:/tmp/lol
```
Git would simply ignore the `git:` part, interpret the path as `file:///tmp/lol` and import the repository into the new project, in turn potentially giving the attacker access to any repository in the system, whether private or not.
......@@ -88,8 +88,8 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source
cd /tmp
curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.1.2.tar.gz | tar xz
cd git-2.1.2/
curl -L --progress https://www.kernel.org/pub/software/scm/git/git-2.4.3.tar.gz | tar xz
cd git-2.4.3/
./configure
make prefix=/usr/local all
......@@ -195,9 +195,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
### Clone the Source
# Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-11-stable gitlab
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-12-stable gitlab
**Note:** You can change `7-11-stable-ee` to `master` if you want the *bleeding edge* version, but never install master on a production server!
**Note:** You can change `7-12-stable-ee` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
......
# Profile Settings
- [Preferences](preferences.md)
- [Two-factor Authentication (2FA)](two_factor_authentication.md)
# Profile Preferences
Settings in the **Profile > Preferences** page allow the user to customize
various aspects of the site to their liking.
## Application theme
Changing this setting allows the user to customize the color scheme used for the
navigation bar on the left side of the screen.
The default is **Charcoal**.
## Syntax highlighting theme
Changing this setting allows the user to customize the theme used when viewing
syntax highlighted code on the site.
The default is **White**.
## Behavior
### Default Dashboard
For users who have access to a large number of projects but only keep up with a
select few, the amount of activity on the default Dashboard page can be
overwhelming.
Changing this setting allows the user to redefine what their default dashboard
will be. Setting it to **Starred Projects** will make that Dashboard view the
default when signing in or clicking the application logo in the upper left.
The default is **Your Projects**.
......@@ -4,7 +4,7 @@ GitLab provides a way to push update messages to an Irker server. When
configured, pushes to a project will trigger the service to send data directly
to the Irker server.
See the project homepage for further info: http://www.catb.org/esr/irker/
See the project homepage for further info: https://gitlab.com/esr/irker
## Needed setup
......
......@@ -30,9 +30,6 @@ All steps from issue template are explained below
```
Xth: (7 working days before the 22nd)
- [ ] Update the CE changelog (#LINK)
- [ ] Update the EE changelog (#LINK)
- [ ] Update the CI changelog (#LINK)
- [ ] Triage the omnibus-gitlab milestone
Xth: (6 working days before the 22nd)
......@@ -41,6 +38,7 @@ Xth: (6 working days before the 22nd)
- [ ] Determine QA person and notify this person
- [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary
- [ ] Create CE, EE, CI RC1 versions (#LINK)
- [ ] Build RC1 packages (EE first) (#LINK)
Xth: (5 working days before the 22nd)
......@@ -142,7 +140,8 @@ Tweet about the RC release:
## Prepare the blog post
1. Start with a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) and fill it out.
1. The blog post template for this release should already exist and might have comments that were added during the month.
1. Fill out as much of the blog post template as you can.
1. Make sure the blog post contains information about the GitLab CI release.
1. Check the changelog of CE and EE for important changes.
1. Also check the CI changelog
......@@ -155,6 +154,7 @@ Tweet about the RC release:
1. Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master)
1. Assign to one reviewer who will fix spelling issues by editing the branch (either with a git client or by using the online editor)
1. Comment to the reviewer: '@person Please mention the whole team as soon as you are done (3 workdays before release at the latest)'
1. Create a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) for the release after this.
## Create CE, EE, CI stable versions
......@@ -212,4 +212,4 @@ Consider creating a post on Hacker News.
## Create a WIP blogpost for the next release
Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md).
Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md).
\ No newline at end of file
# From 6.x or 7.x to 7.11
*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.11.md) for the most up to date instructions.*
# From 6.x or 7.x to 7.12
*Make sure you view this [upgrade guide from the `master` branch](../../../master/doc/update/6.x-or-7.x-to-7.12.md) for the most up to date instructions.*
This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.11.
This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.12.
## Global issue numbers
......@@ -71,7 +71,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut
For GitLab Community Edition:
```bash
sudo -u git -H git checkout 7-11-stable
sudo -u git -H git checkout 7-12-stable
```
OR
......@@ -79,7 +79,7 @@ OR
For GitLab Enterprise Edition:
```bash
sudo -u git -H git checkout 7-11-stable-ee
sudo -u git -H git checkout 7-12-stable-ee
```
## 4. Install additional packages
......@@ -164,11 +164,11 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
TIP: to see what changed in `gitlab.yml.example` in this release use next command:
```
git diff 6-0-stable:config/gitlab.yml.example 7-11-stable-ee:config/gitlab.yml.example
git diff 6-0-stable:config/gitlab.yml.example 7-12-stable-ee:config/gitlab.yml.example
```
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.6.0/config.yml.example but with your settings.
* Copy rack attack middleware config
......@@ -184,10 +184,15 @@ sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
### Change Nginx settings
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-11-stable-ee/lib/support/nginx/gitlab-ssl but with your settings.
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ee/blob/7-12-stable-ee/lib/support/nginx/gitlab-ssl but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
### Check the version of /usr/local/bin/git
If you installed Git from source into /usr/local/bin/git then please [check
your version](7.11-to-7.12.md).
## 9. Start application
sudo service gitlab start
......
# From 7.11 to 7.12
### 0. Double-check your Git version
**This notice applies only to /usr/local/bin/git**
If you compiled Git from source on your GitLab server then please double-check
that you are using a version that protects against CVE-2014-9390. For six
months after this vulnerability became known the GitLab installation guide
still contained instructions that would install the outdated, 'vulnerable' Git
version 2.1.2.
Run the following command to get your current Git version.
```
/usr/local/bin/git --version
```
If you see 'No such file or directory' then you did not install Git according
to the outdated instructions from the GitLab installation guide and you can go
to the next step 'Stop server' below.
If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4,
v2.2.1 or newer. You can use the [instructions in the GitLab source
installation
guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
to install a newer version of Git.
### 1. Stop server
sudo service gitlab stop
### 2. Backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 3. Get latest code
```bash
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
```
For GitLab Community Edition:
```bash
sudo -u git -H git checkout 7-12-stable
```
OR
For GitLab Enterprise Edition:
```bash
sudo -u git -H git checkout 7-12-stable-ee
```
### 4. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v2.6.3
```
### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without ... postgres')
sudo -u git -H bundle install --without development test postgres --deployment
# PostgreSQL installations (note: the line below states '--without ... mysql')
sudo -u git -H bundle install --without development test mysql --deployment
# Run database migrations
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
# Update init.d script
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
```
### 6. Update config files
#### New configuration options for `gitlab.yml`
There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`.
```
git diff origin/7-11-stable:config/gitlab.yml.example origin/7-12-stable:config/gitlab.yml.example
``````
### 7. Start application
sudo service gitlab start
sudo service nginx restart
### 8. Check application status
Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
To make sure you didn't miss anything run a more thorough check with:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
If all items are green, then congratulations, the upgrade is complete!
## Things went south? Revert to previous version (7.11)
### 1. Revert the code to the previous version
Follow the [upgrade guide from 7.10 to 7.11](7.10-to-7.11.md), except for the database migration
(The backup is already migrated to the previous version)
### 2. Restore from the backup:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above.
# Workflow
- [Feature branch workflow](workflow.md)
- [Project forking workflow](forking_workflow.md)
- [Project Features](project_features.md)
- [Authorization for merge requests](authorization_for_merge_requests.md)
- [Approval for merge requests](approval_for_merge_requests.md)
- [Rebase before merge](rebase_before_merge.md)
- [Authorization for merge requests](authorization_for_merge_requests.md)
- [Change your time zone](timezone.md)
- [Feature branch workflow](workflow.md)
- [GitLab Flow](gitlab_flow.md)
- [Groups](groups.md)
- [Share projects with other groups](share_projects_with_other_groups.md)
- [Importing to GitLab](doc/importing/README.md)
- [Keyboard shortcuts](shortcuts.md)
- [Labels](labels.md)
- [GitLab Flow](gitlab_flow.md)
- [Manage large binaries with git annex](git_annex.md)
- [Notifications](notifications.md)
- [Importing to GitLab](doc/importing/README.md)
- [Two-factor Authentication (2FA)](two_factor_authentication.md)
- [Project Features](project_features.md)
- [Project forking workflow](forking_workflow.md)
- [Protected branches](protected_branches.md)
- [Rebase before merge](rebase_before_merge.md)
- [Sharing a project with a group](share_with_group.md)
- [Change your time zone](timezone.md)
- [Keyboard shortcuts](shortcuts.md)
- [Share projects with other groups](share_projects_with_other_groups.md)
- [Two-factor Authentication (2FA)](two_factor_authentication.md)
- [Web Editor](web_editor.md)
- [Manage large binaries with git annex](git_annex.md)
- ["Work In Progress" Merge Requests](wip_merge_requests.md)
......@@ -4,3 +4,6 @@
2. [GitHub](import_projects_from_github.md)
3. [GitLab.com](import_projects_from_gitlab_com.md)
4. [SVN](migrating_from_svn.md)
### Note
* If you'd like to migrate from a self-hosted GitLab instance to GitLab.com, you can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported.
\ No newline at end of file
......@@ -18,9 +18,9 @@ Feature: Profile Active Tab
Then the active main tab should be SSH Keys
And no other main tabs should be active
Scenario: On Profile Design
Given I visit profile design page
Then the active main tab should be Design
Scenario: On Profile Preferences
Given I visit profile preferences page
Then the active main tab should be Preferences
And no other main tabs should be active
Scenario: On Profile History
......
......@@ -84,16 +84,3 @@ Feature: Profile
Then I visit profile applications page
And I click to remove application
Then I see that application is removed
@javascript
Scenario: I change my application theme
Given I visit profile design page
When I change my application theme
Then I should see the theme change immediately
And I should receive feedback that the changes were saved
@javascript
Scenario: I change my code preview theme
Given I visit profile design page
When I change my code preview theme
Then I should receive feedback that the changes were saved
......@@ -77,3 +77,17 @@ Feature: Project Commits Diff Comments
And I submit the diff comment
Then I should not see the diff comment form
And I should see a discussion reply button
@javascript
Scenario: I can add a comment on a side-by-side commit diff (left side)
Given I open a diff comment form
And I click side-by-side diff button
When I leave a diff comment in a parallel view on the left side like "Old comment"
Then I should see a diff comment on the left side saying "Old comment"
@javascript
Scenario: I can add a comment on a side-by-side commit diff (right side)
Given I open a diff comment form
And I click side-by-side diff button
When I leave a diff comment in a parallel view on the right side like "New comment"
Then I should see a diff comment on the right side saying "New comment"
......@@ -15,8 +15,8 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
ensure_active_main_tab('SSH Keys')
end
step 'the active main tab should be Design' do
ensure_active_main_tab('Design')
step 'the active main tab should be Preferences' do
ensure_active_main_tab('Preferences')
end
step 'the active main tab should be History' do
......
......@@ -114,27 +114,6 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
expect(page).to have_content "#{current_user.name} closed issue"
end
step "I change my application theme" do
page.within '.application-theme' do
choose "Violet"
end
end
step "I change my code preview theme" do
page.within '.code-preview-theme' do
choose "Solarized dark"
end
end
step "I should see the theme change immediately" do
expect(page).to have_selector('body.ui_color')
expect(page).not_to have_selector('body.ui_basic')
end
step "I should receive feedback that the changes were saved" do
expect(page).to have_content("saved")
end
step 'my password is expired' do
current_user.update_attributes(password_expires_at: Time.now - 1.hour)
end
......@@ -197,7 +176,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see application form' do
expect(page).to have_content "New application"
expect(page).to have_content "New Application"
end
step 'I fill application form out and submit' do
......
......@@ -2,6 +2,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedPaths
include SharedDiffNote
include RepoHelpers
step 'I see project commits' do
......@@ -88,14 +89,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(links[1]['href']).to match %r{blob/#{sample_image_commit.new_blob_id}}
end
step 'I click side-by-side diff button' do
click_link "Side-by-side"
end
step 'I see side-by-side diff button' do
expect(page).to have_content "Side-by-side"
end
step 'I see inline diff button' do
expect(page).to have_content "Inline"
end
......
......@@ -28,6 +28,22 @@ module SharedDiffNote
end
end
step 'I leave a diff comment in a parallel view on the left side like "Old comment"' do
click_parallel_diff_line(sample_commit.line_code, 'old')
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "Old comment"
find(".js-comment-button").trigger("click")
end
end
step 'I leave a diff comment in a parallel view on the right side like "New comment"' do
click_parallel_diff_line(sample_commit.line_code, 'new')
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
fill_in "note[note]", with: "New comment"
find(".js-comment-button").trigger("click")
end
end
step 'I preview a diff comment text like "Should fix it :smile:"' do
click_diff_line(sample_commit.line_code)
page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do
......@@ -102,6 +118,18 @@ module SharedDiffNote
end
end
step 'I should see a diff comment on the left side saying "Old comment"' do
page.within("#{diff_file_selector} .notes_content.parallel.old") do
expect(page).to have_content("Old comment")
end
end
step 'I should see a diff comment on the right side saying "New comment"' do
page.within("#{diff_file_selector} .notes_content.parallel.new") do
expect(page).to have_content("New comment")
end
end
step 'I should see a discussion reply button' do
page.within(diff_file_selector) do
expect(page).to have_button('Reply')
......@@ -157,6 +185,14 @@ module SharedDiffNote
end
end
step 'I click side-by-side diff button' do
click_link "Side-by-side"
end
step 'I see side-by-side diff button' do
expect(page).to have_content "Side-by-side"
end
def diff_file_selector
".diff-file:nth-of-type(1)"
end
......@@ -164,4 +200,8 @@ module SharedDiffNote
def click_diff_line(code)
find("button[data-line-code='#{code}']").click
end
def click_parallel_diff_line(code, line_type)
find("button[data-line-code='#{code}'][data-line-type='#{line_type}']").trigger('click')
end
end
......@@ -135,8 +135,8 @@ module SharedPaths
visit profile_keys_path
end
step 'I visit profile design page' do
visit design_profile_path
step 'I visit profile preferences page' do
visit profile_preferences_path
end
step 'I visit profile history page' do
......
......@@ -21,7 +21,8 @@ module Gitlab
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size']
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay']
)
end
end
......
......@@ -27,7 +27,7 @@ module Gitlab
# Get latest 20 commits ASC
commits_limited = commits.last(20)
# For performance purposes maximum 20 latest commits
# will be passed as post receive hook data.
commit_attrs = commits_limited.map(&:hook_attrs)
......@@ -70,8 +70,11 @@ module Gitlab
end
def checkout_sha(repository, newrev, ref)
# Checkout sha is nil when we remove branch or tag
return if Gitlab::Git.blank_ref?(newrev)
# Find sha for tag, except when it was deleted.
if Gitlab::Git.tag_ref?(ref) && !Gitlab::Git.blank_ref?(newrev)
if Gitlab::Git.tag_ref?(ref)
tag_name = Gitlab::Git.ref_name(ref)
tag = repository.find_tag(tag_name)
......
module Gitlab
class Theme
BASIC = 1 unless const_defined?(:BASIC)
MARS = 2 unless const_defined?(:MARS)
MODERN = 3 unless const_defined?(:MODERN)
GRAY = 4 unless const_defined?(:GRAY)
COLOR = 5 unless const_defined?(:COLOR)
BLUE = 6 unless const_defined?(:BLUE)
def self.classes
@classes ||= {
BASIC => 'ui_basic',
MARS => 'ui_mars',
MODERN => 'ui_modern',
GRAY => 'ui_gray',
COLOR => 'ui_color',
BLUE => 'ui_blue'
}
end
def self.css_class_by_id(id)
id ||= Gitlab.config.gitlab.default_theme
classes[id]
end
def self.types
@types ||= {
BASIC => 'light_theme',
MARS => 'dark_theme',
MODERN => 'dark_theme',
GRAY => 'dark_theme',
COLOR => 'dark_theme',
BLUE => 'light_theme'
}
end
def self.type_css_class_by_id(id)
id ||= Gitlab.config.gitlab.default_theme
types[id]
end
# Convenience method to get a space-separated String of all the theme
# classes that might be applied to the `body` element
#
# Returns a String
def self.body_classes
(classes.values + types.values).uniq.join(' ')
end
end
end
module Gitlab
# Module containing GitLab's application theme definitions and helper methods
# for accessing them.
module Themes
# Theme ID used when no `default_theme` configuration setting is provided.
APPLICATION_DEFAULT = 2
# Struct class representing a single Theme
Theme = Struct.new(:id, :name, :css_class)
# All available Themes
THEMES = [
Theme.new(1, 'Graphite', 'ui_graphite'),
Theme.new(2, 'Charcoal', 'ui_charcoal'),
Theme.new(3, 'Green', 'ui_green'),
Theme.new(4, 'Gray', 'ui_gray'),
Theme.new(5, 'Violet', 'ui_violet'),
Theme.new(6, 'Blue', 'ui_blue')
].freeze
# Convenience method to get a space-separated String of all the theme
# classes that might be applied to the `body` element
#
# Returns a String
def self.body_classes
THEMES.collect(&:css_class).uniq.join(' ')
end
# Get a Theme by its ID
#
# If the ID is invalid, returns the default Theme.
#
# id - Integer ID
#
# Returns a Theme
def self.by_id(id)
THEMES.detect { |t| t.id == id } || default
end
# Get the default Theme
#
# Returns a Theme
def self.default
by_id(default_id)
end
# Iterate through each Theme
#
# Yields the Theme object
def self.each(&block)
THEMES.each(&block)
end
private
def self.default_id
id = Gitlab.config.gitlab.default_theme.to_i
# Prevent an invalid configuration setting from causing an infinite loop
if id < THEMES.first.id || id > THEMES.last.id
APPLICATION_DEFAULT
else
id
end
end
end
end
......@@ -73,7 +73,7 @@ server {
ssl_certificate_key /etc/nginx/ssl/gitlab.key;
# GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
......
......@@ -15,9 +15,9 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(1) }
it { body.first["username"].should == user.username }
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq(1) }
it { expect(body.first["username"]).to eq user.username }
end
context 'group members' do
......@@ -32,9 +32,9 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(1) }
it { body.first["username"].should == user.username }
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq(1) }
it { expect(body.first["username"]).to eq user.username }
end
context 'all users' do
......@@ -45,7 +45,7 @@ describe AutocompleteController do
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(User.count) }
it { expect(body).to be_kind_of(Array) }
it { expect(body.size).to eq(User.count) }
end
end
require 'spec_helper'
describe Profiles::PreferencesController do
let(:user) { create(:user) }
before do
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
end
describe 'GET show' do
it 'renders' do
get :show
expect(response).to render_template :show
end
it 'assigns user' do
get :show
expect(assigns[:user]).to eq user
end
end
describe 'PATCH update' do
def go(params: {}, format: :js)
params.reverse_merge!(
color_scheme_id: '1',
dashboard: 'stars',
theme_id: '1'
)
patch :update, user: params, format: format
end
context 'on successful update' do
it 'sets the flash' do
go
expect(flash[:notice]).to eq 'Preferences saved.'
end
it "changes the user's preferences" do
prefs = {
color_scheme_id: '1',
dashboard: 'stars',
theme_id: '2'
}.with_indifferent_access
expect(user).to receive(:update_attributes).with(prefs)
go params: prefs
end
end
context 'on failed update' do
it 'sets the flash' do
expect(user).to receive(:update_attributes).and_return(false)
go
expect(flash[:alert]).to eq('Failed to save preferences.')
end
end
context 'on invalid dashboard setting' do
it 'sets the flash' do
prefs = {dashboard: 'invalid'}
go params: prefs
expect(flash[:alert]).to match(/\AFailed to save preferences \(.+\)\.\z/)
end
end
context 'as js' do
it 'renders' do
go
expect(response).to render_template :update
end
end
context 'as html' do
it 'redirects' do
go format: :html
expect(response).to redirect_to(profile_preferences_path)
end
end
end
end
require 'spec_helper'
describe RootController do
describe 'GET show' do
context 'with a user' do
let(:user) { create(:user) }
before do
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
end
context 'who has customized their dashboard setting' do
before do
user.update_attribute(:dashboard, 'stars')
end
it 'redirects to their specified dashboard' do
get :show
expect(response).to redirect_to starred_dashboard_projects_path
end
end
context 'who uses the default dashboard setting' do
it 'renders the default dashboard' do
get :show
expect(response).to render_template 'dashboard/show'
end
end
end
end
end
......@@ -12,7 +12,7 @@ describe "Admin::Hooks", feature: true do
describe "GET /admin/hooks" do
it "should be ok" do
visit admin_root_path
within ".sidebar-wrapper" do
page.within ".sidebar-wrapper" do
click_on "Hooks"
end
expect(current_path).to eq(admin_hooks_path)
......
......@@ -311,10 +311,10 @@ describe 'Issues', feature: true do
end
def first_issue
all('ul.issues-list li').first.text
page.all('ul.issues-list li').first.text
end
def last_issue
all('ul.issues-list li').last.text
page.all('ul.issues-list li').last.text
end
end
......@@ -22,20 +22,20 @@ describe 'Comments' do
is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
expect(find('.js-main-target-form input[type=submit]').value).
to eq('Add Comment')
within('.js-main-target-form') do
page.within('.js-main-target-form') do
expect(page).not_to have_link('Cancel')
end
end
describe 'with text' do
before do
within('.js-main-target-form') do
page.within('.js-main-target-form') do
fill_in 'note[note]', with: 'This is awesome'
end
end
it 'should have enable submit button and preview button' do
within('.js-main-target-form') do
page.within('.js-main-target-form') do
expect(page).not_to have_css('.js-comment-button[disabled]')
expect(page).to have_css('.js-md-preview-button', visible: true)
end
......@@ -45,7 +45,7 @@ describe 'Comments' do
describe 'when posting a note' do
before do
within('.js-main-target-form') do
page.within('.js-main-target-form') do
fill_in 'note[note]', with: 'This is awsome!'
find('.js-md-preview-button').click
click_button 'Add Comment'
......@@ -54,11 +54,11 @@ describe 'Comments' do
it 'should be added and form reset' do
is_expected.to have_content('This is awsome!')
within('.js-main-target-form') do
page.within('.js-main-target-form') do
expect(page).to have_no_field('note[note]', with: 'This is awesome!')
expect(page).to have_css('.js-md-preview', visible: :hidden)
end
within('.js-main-target-form') do
page.within('.js-main-target-form') do
is_expected.to have_css('.js-note-text', visible: true)
end
end
......@@ -66,7 +66,7 @@ describe 'Comments' do
describe 'when editing a note', js: true do
it 'should contain the hidden edit form' do
within("#note_#{note.id}") do
page.within("#note_#{note.id}") do
is_expected.to have_css('.note-edit-form', visible: false)
end
end
......@@ -78,7 +78,7 @@ describe 'Comments' do
end
it 'should show the note edit form and hide the note body' do
within("#note_#{note.id}") do
page.within("#note_#{note.id}") do
expect(find('.current-note-edit-form', visible: true)).to be_visible
expect(find('.note-edit-form', visible: true)).to be_visible
expect(find(:css, '.note-body > .note-text', visible: false)).not_to be_visible
......@@ -90,17 +90,17 @@ describe 'Comments' do
#within(".current-note-edit-form") do
#fill_in "note[note]", with: "Some new content"
#find(".btn-cancel").click
#find(".js-note-text", visible: false).text.should == note.note
#expect(find(".js-note-text", visible: false).text).to eq note.note
#end
#end
it 'appends the edited at time to the note' do
within('.current-note-edit-form') do
page.within('.current-note-edit-form') do
fill_in 'note[note]', with: 'Some new content'
find('.btn-save').click
end
within("#note_#{note.id}") do
page.within("#note_#{note.id}") do
is_expected.to have_css('.note_edited_ago')
expect(find('.note_edited_ago').text).
to match(/less than a minute ago/)
......@@ -115,7 +115,7 @@ describe 'Comments' do
end
it 'shows the delete link' do
within('.note-attachment') do
page.within('.note-attachment') do
is_expected.to have_css('.js-note-attachment-delete')
end
end
......@@ -150,7 +150,7 @@ describe 'Comments' do
it { is_expected.to have_css('.js-temp-notes-holder') }
it 'has .new_note css class' do
within('.js-temp-notes-holder') do
page.within('.js-temp-notes-holder') do
expect(subject).to have_css('.new_note')
end
end
......@@ -166,7 +166,7 @@ describe 'Comments' do
end
it 'should be removed when canceled' do
within(".diff-file form[rel$='#{line_code}']") do
page.within(".diff-file form[rel$='#{line_code}']") do
find('.js-close-discussion-note-form').trigger('click')
end
......@@ -186,11 +186,11 @@ describe 'Comments' do
describe 'previewing them separately' do
before do
# add two separate texts and trigger previews on both
within("tr[id='#{line_code}'] + .js-temp-notes-holder") do
page.within("tr[id='#{line_code}'] + .js-temp-notes-holder") do
fill_in 'note[note]', with: 'One comment on line 7'
find('.js-md-preview-button').click
end
within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
page.within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
fill_in 'note[note]', with: 'Another comment on line 10'
find('.js-md-preview-button').click
end
......@@ -199,7 +199,7 @@ describe 'Comments' do
describe 'posting a note' do
before do
within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
page.within("tr[id='#{line_code_2}'] + .js-temp-notes-holder") do
fill_in 'note[note]', with: 'Another comment on line 10'
click_button('Add Comment')
end
......
require 'spec_helper'
describe 'Profile > Preferences' do
let(:user) { create(:user) }
before do
login_as(user)
visit profile_preferences_path
end
describe 'User changes their application theme', js: true do
let(:default) { Gitlab::Themes.default }
let(:theme) { Gitlab::Themes.by_id(5) }
it 'creates a flash message' do
choose "user_theme_id_#{theme.id}"
expect_preferences_saved_message
end
it 'updates their preference' do
choose "user_theme_id_#{theme.id}"
allowing_for_delay do
visit page.current_path
expect(page).to have_checked_field("user_theme_id_#{theme.id}")
end
end
it 'reflects the changes immediately' do
expect(page).to have_selector("body.#{default.css_class}")
choose "user_theme_id_#{theme.id}"
expect(page).not_to have_selector("body.#{default.css_class}")
expect(page).to have_selector("body.#{theme.css_class}")
end
end
describe 'User changes their syntax highlighting theme', js: true do
it 'creates a flash message' do
choose 'user_color_scheme_id_5'
expect_preferences_saved_message
end
it 'updates their preference' do
choose 'user_color_scheme_id_5'
allowing_for_delay do
visit page.current_path
expect(page).to have_checked_field('user_color_scheme_id_5')
end
end
end
describe 'User changes their default dashboard' do
it 'creates a flash message' do
select 'Starred Projects', from: 'user_dashboard'
click_button 'Save'
expect_preferences_saved_message
end
it 'updates their preference' do
select 'Starred Projects', from: 'user_dashboard'
click_button 'Save'
click_link 'Dashboard'
expect(page.current_path).to eq starred_dashboard_projects_path
click_link 'Your Projects'
expect(page.current_path).to eq dashboard_path
end
end
def expect_preferences_saved_message
within('.flash-container') do
expect(page).to have_content('Preferences saved.')
end
end
end
......@@ -7,7 +7,7 @@ describe "Search", feature: true do
@project.team << [@user, :reporter]
visit search_path
within '.search-holder' do
page.within '.search-holder' do
fill_in "search", with: @project.name[0..3]
click_button "Search"
end
......
......@@ -36,8 +36,8 @@ describe "Profile access", feature: true do
it { is_expected.to be_denied_for :visitor }
end
describe "GET /profile/design" do
subject { design_profile_path }
describe "GET /profile/preferences" do
subject { profile_preferences_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin }
......
......@@ -185,27 +185,6 @@ describe ApplicationHelper do
end
end
describe 'user_color_scheme_class' do
context 'with current_user is nil' do
it 'should return a string' do
allow(self).to receive(:current_user).and_return(nil)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
context 'with a current_user' do
(1..5).each do |color_scheme_id|
context "with color_scheme_id == #{color_scheme_id}" do
it 'should return a string' do
current_user = double(:color_scheme_id => color_scheme_id)
allow(self).to receive(:current_user).and_return(current_user)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
end
end
end
describe 'simple_sanitize' do
let(:a_tag) { '<a href="#">Foo</a>' }
......
require 'spec_helper'
describe PreferencesHelper do
describe 'user_application_theme' do
context 'with a user' do
it "returns user's theme's css_class" do
user = double('user', theme_id: 3)
allow(self).to receive(:current_user).and_return(user)
expect(user_application_theme).to eq 'ui_green'
end
it 'returns the default when id is invalid' do
user = double('user', theme_id: Gitlab::Themes::THEMES.size + 5)
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2)
allow(self).to receive(:current_user).and_return(user)
expect(user_application_theme).to eq 'ui_charcoal'
end
end
context 'without a user' do
before do
allow(self).to receive(:current_user).and_return(nil)
end
it 'returns the default theme' do
expect(user_application_theme).to eq Gitlab::Themes.default.css_class
end
end
end
describe 'dashboard_choices' do
it 'raises an exception when defined choices may be missing' do
expect(User).to receive(:dashboards).and_return(foo: 'foo')
expect { dashboard_choices }.to raise_error(RuntimeError)
end
it 'raises an exception when defined choices may be using the wrong key' do
expect(User).to receive(:dashboards).and_return(foo: 'foo', bar: 'bar')
expect { dashboard_choices }.to raise_error(KeyError)
end
it 'provides better option descriptions' do
expect(dashboard_choices).to match_array [
['Your Projects (default)', 'projects'],
['Starred Projects', 'stars']
]
end
end
describe 'user_color_scheme_class' do
context 'with current_user is nil' do
it 'should return a string' do
allow(self).to receive(:current_user).and_return(nil)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
context 'with a current_user' do
(1..5).each do |color_scheme_id|
context "with color_scheme_id == #{color_scheme_id}" do
it 'should return a string' do
current_user = double(:color_scheme_id => color_scheme_id)
allow(self).to receive(:current_user).and_return(current_user)
expect(user_color_scheme_class).to be_kind_of(String)
end
end
end
end
end
end
......@@ -23,7 +23,7 @@ describe Gitlab::LDAP::Access do
it "should block user in GitLab" do
access.allowed?
user.should be_blocked
expect(user).to be_blocked
end
end
......@@ -44,7 +44,7 @@ describe Gitlab::LDAP::Access do
it "does not unblock user in GitLab" do
access.allowed?
user.should be_blocked
expect(user).to be_blocked
end
end
......@@ -56,7 +56,7 @@ describe Gitlab::LDAP::Access do
it "should unblock user in GitLab" do
access.allowed?
user.should_not be_blocked
expect(user).not_to be_blocked
end
end
end
......
......@@ -36,6 +36,7 @@ describe 'Gitlab::NoteDataBuilder' do
let(:note) { create(:note_on_issue, noteable_id: issue.id) }
it 'returns the note and issue-specific data' do
data[:issue]["updated_at"] = fixed_time
expect(data).to have_key(:issue)
expect(data[:issue]).to eq(issue.hook_attrs)
end
......@@ -46,6 +47,7 @@ describe 'Gitlab::NoteDataBuilder' do
let(:note) { create(:note_on_merge_request, noteable_id: merge_request.id) }
it 'returns the note and merge request data' do
data[:merge_request]["updated_at"] = fixed_time
expect(data).to have_key(:merge_request)
expect(data[:merge_request]).to eq(merge_request.hook_attrs)
end
......@@ -56,6 +58,7 @@ describe 'Gitlab::NoteDataBuilder' do
let(:note) { create(:note_on_merge_request_diff, noteable_id: merge_request.id) }
it 'returns the note and merge request diff data' do
data[:merge_request]["updated_at"] = fixed_time
expect(data).to have_key(:merge_request)
expect(data[:merge_request]).to eq(merge_request.hook_attrs)
end
......@@ -66,6 +69,7 @@ describe 'Gitlab::NoteDataBuilder' do
let!(:note) { create(:note_on_project_snippet, noteable_id: snippet.id) }
it 'returns the note and project snippet data' do
data[:snippet]["updated_at"] = fixed_time
expect(data).to have_key(:snippet)
expect(data[:snippet]).to eq(snippet.hook_attrs)
end
......
......@@ -80,31 +80,31 @@ describe Gitlab::OAuth::AuthHash do
context 'auth_hash constructed with ASCII-8BIT encoding' do
it 'forces utf8 encoding on uid' do
auth_hash.uid.encoding.should eql Encoding::UTF_8
expect(auth_hash.uid.encoding).to eql Encoding::UTF_8
end
it 'forces utf8 encoding on provider' do
auth_hash.provider.encoding.should eql Encoding::UTF_8
expect(auth_hash.provider.encoding).to eql Encoding::UTF_8
end
it 'forces utf8 encoding on name' do
auth_hash.name.encoding.should eql Encoding::UTF_8
expect(auth_hash.name.encoding).to eql Encoding::UTF_8
end
it 'forces utf8 encoding on full_name' do
auth_hash.full_name.encoding.should eql Encoding::UTF_8
expect(auth_hash.full_name.encoding).to eql Encoding::UTF_8
end
it 'forces utf8 encoding on username' do
auth_hash.username.encoding.should eql Encoding::UTF_8
expect(auth_hash.username.encoding).to eql Encoding::UTF_8
end
it 'forces utf8 encoding on email' do
auth_hash.email.encoding.should eql Encoding::UTF_8
expect(auth_hash.email.encoding).to eql Encoding::UTF_8
end
it 'forces utf8 encoding on password' do
auth_hash.password.encoding.should eql Encoding::UTF_8
expect(auth_hash.password.encoding).to eql Encoding::UTF_8
end
end
end
require 'spec_helper'
describe Gitlab::Themes do
describe '.body_classes' do
it 'returns a space-separated list of class names' do
css = described_class.body_classes
expect(css).to include('ui_graphite')
expect(css).to include(' ui_charcoal ')
expect(css).to include(' ui_blue')
end
end
describe '.by_id' do
it 'returns a Theme by its ID' do
expect(described_class.by_id(1).name).to eq 'Graphite'
expect(described_class.by_id(6).name).to eq 'Blue'
end
end
describe '.default' do
it 'returns the default application theme' do
allow(described_class).to receive(:default_id).and_return(2)
expect(described_class.default.id).to eq 2
end
it 'prevents an infinite loop when configuration default is invalid' do
default = described_class::APPLICATION_DEFAULT
themes = described_class::THEMES
config = double(default_theme: 0).as_null_object
allow(Gitlab).to receive(:config).and_return(config)
expect(described_class.default.id).to eq default
config = double(default_theme: themes.size + 5).as_null_object
allow(Gitlab).to receive(:config).and_return(config)
expect(described_class.default.id).to eq default
end
end
describe '.each' do
it 'passes the block to the THEMES Array' do
ids = []
described_class.each { |theme| ids << theme.id }
expect(ids).not_to be_empty
# TODO (rspeicher): RSpec 3.x
# expect(described_class.each).to yield_with_arg(described_class::Theme)
end
end
end
......@@ -15,6 +15,7 @@
# twitter_sharing_enabled :boolean default(TRUE)
# restricted_visibility_levels :text
# max_attachment_size :integer default(10), not null
# session_expire_delay :integer default(10080), not null
# default_project_visibility :integer
# default_snippet_visibility :integer
# restricted_signup_domains :text
......
......@@ -52,7 +52,7 @@ describe ExternalWikiService do
it 'should replace the wiki url' do
wiki_path = get_project_wiki_path(project)
wiki_path.should match('https://gitlab.com')
expect(wiki_path).to match('https://gitlab.com')
end
end
end
......
......@@ -22,7 +22,7 @@ require 'spec_helper'
describe Note do
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:noteable) }
it { is_expected.to belong_to(:noteable).touch(true) }
it { is_expected.to belong_to(:author).class_name('User') }
end
......
......@@ -58,7 +58,7 @@ describe GitlabCiService do
service_hook = double
service_hook.should_receive(:execute)
@service.should_receive(:service_hook).and_return(service_hook)
@service.should_receive(:ci_yaml_file).with(push_sample_data)
@service.should_receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
@service.execute(push_sample_data)
end
......
......@@ -43,7 +43,7 @@ describe IrkerService do
let(:_recipients) { 'a b c d' }
it 'should add an error if there is too many recipients' do
subject.send :check_recipients_count
subject.errors.should_not be_blank
expect(subject.errors).not_to be_blank
end
end
......@@ -51,7 +51,7 @@ describe IrkerService do
let(:_recipients) { 'a b c' }
it 'should not add an error if there is 3 recipients' do
subject.send :check_recipients_count
subject.errors.should be_blank
expect(subject.errors).to be_blank
end
end
end
......@@ -96,11 +96,11 @@ describe IrkerService do
conn = @irker_server.accept
conn.readlines.each do |line|
msg = JSON.load(line.chomp("\n"))
msg.keys.should match_array(['to', 'privmsg'])
expect(msg.keys).to match_array(['to', 'privmsg'])
if msg['to'].is_a?(String)
msg['to'].should == 'irc://chat.freenode.net/#commits'
expect(msg['to']).to eq 'irc://chat.freenode.net/#commits'
else
msg['to'].should match_array(['irc://chat.freenode.net/#commits'])
expect(msg['to']).to match_array(['irc://chat.freenode.net/#commits'])
end
end
conn.close
......
......@@ -50,12 +50,13 @@
# bitbucket_access_token :string(255)
# bitbucket_access_token_secret :string(255)
# location :string(255)
# public_email :string(255) default(""), not null
# encrypted_otp_secret :string(255)
# encrypted_otp_secret_iv :string(255)
# encrypted_otp_secret_salt :string(255)
# otp_required_for_login :boolean
# otp_backup_codes :text
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
#
require 'spec_helper'
......@@ -342,12 +343,12 @@ describe User do
end
describe 'with default overrides' do
let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true, theme_id: Gitlab::Theme::BASIC) }
let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true, theme_id: 1) }
it "should apply defaults to user" do
expect(user.projects_limit).to eq(123)
expect(user.can_create_group).to be_falsey
expect(user.theme_id).to eq(Gitlab::Theme::BASIC)
expect(user.theme_id).to eq(1)
end
end
end
......
......@@ -196,10 +196,10 @@ describe API::API, api: true do
it 'should return a project issue by iid' do
get api("/projects/#{project.id}/issues?iid=#{issue.iid}", user)
response.status.should == 200
json_response.first['title'].should == issue.title
json_response.first['id'].should == issue.id
json_response.first['iid'].should == issue.iid
expect(response.status).to eq 200
expect(json_response.first['title']).to eq issue.title
expect(json_response.first['id']).to eq issue.id
expect(json_response.first['iid']).to eq issue.iid
end
it "should return 404 if issue id not found" do
......
......@@ -118,9 +118,9 @@ describe API::API, api: true do
it 'should return merge_request by iid' do
url = "/projects/#{project.id}/merge_requests?iid=#{merge_request.iid}"
get api(url, user)
response.status.should == 200
json_response.first['title'].should == merge_request.title
json_response.first['id'].should == merge_request.id
expect(response.status).to eq 200
expect(json_response.first['title']).to eq merge_request.title
expect(json_response.first['id']).to eq merge_request.id
end
it "should return a 404 error if merge_request_id not found" do
......
......@@ -32,9 +32,9 @@ describe API::API, api: true do
it 'should return a project milestone by iid' do
get api("/projects/#{project.id}/milestones?iid=#{milestone.iid}", user)
response.status.should == 200
json_response.first['title'].should == milestone.title
json_response.first['id'].should == milestone.id
expect(response.status).to eq 200
expect(json_response.first['title']).to eq milestone.title
expect(json_response.first['id']).to eq milestone.id
end
it 'should return 401 error if user not authenticated' do
......
......@@ -60,9 +60,9 @@ describe API::API, api: true do
it 'should include the project labels as the tag_list' do
get api('/projects', user)
response.status.should == 200
json_response.should be_an Array
json_response.first.keys.should include('tag_list')
expect(response.status).to eq 200
expect(json_response).to be_an Array
expect(json_response.first.keys).to include('tag_list')
end
context 'and using search' do
......
......@@ -102,7 +102,6 @@ end
# profile_token GET /profile/token(.:format) profile#token
# profile_reset_private_token PUT /profile/reset_private_token(.:format) profile#reset_private_token
# profile GET /profile(.:format) profile#show
# profile_design GET /profile/design(.:format) profile#design
# profile_update PUT /profile/update(.:format) profile#update
describe ProfilesController, "routing" do
it "to #account" do
......@@ -120,9 +119,19 @@ describe ProfilesController, "routing" do
it "to #show" do
expect(get("/profile")).to route_to('profiles#show')
end
end
it "to #design" do
expect(get("/profile/design")).to route_to('profiles#design')
# profile_preferences GET /profile/preferences(.:format) profiles/preferences#show
# PATCH /profile/preferences(.:format) profiles/preferences#update
# PUT /profile/preferences(.:format) profiles/preferences#update
describe Profiles::PreferencesController, 'routing' do
it 'to #show' do
expect(get('/profile/preferences')).to route_to('profiles/preferences#show')
end
it 'to #update' do
expect(put('/profile/preferences')).to route_to('profiles/preferences#update')
expect(patch('/profile/preferences')).to route_to('profiles/preferences#update')
end
end
......@@ -195,11 +204,9 @@ end
# dashboard GET /dashboard(.:format) dashboard#show
# dashboard_issues GET /dashboard/issues(.:format) dashboard#issues
# dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests
# root / dashboard#show
describe DashboardController, "routing" do
it "to #index" do
expect(get("/dashboard")).to route_to('dashboard#show')
expect(get("/")).to route_to('dashboard#show')
end
it "to #issues" do
......@@ -211,6 +218,14 @@ describe DashboardController, "routing" do
end
end
# root / root#show
describe RootController, 'routing' do
it 'to #show' do
expect(get('/')).to route_to('root#show')
end
end
# new_user_session GET /users/sign_in(.:format) devise/sessions#new
# user_session POST /users/sign_in(.:format) devise/sessions#create
# destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy
......
......@@ -12,8 +12,8 @@ describe DestroyGroupService do
destroy_group(group, user)
end
it { Group.all.should_not include(group) }
it { Project.all.should_not include(project) }
it { expect(Group.all).not_to include(group) }
it { expect(Project.all).not_to include(project) }
end
context 'file system' do
......@@ -23,8 +23,8 @@ describe DestroyGroupService do
Sidekiq::Testing.inline! { destroy_group(group, user) }
end
it { gitlab_shell.exists?(group.path).should be_falsey }
it { gitlab_shell.exists?(remove_path).should be_falsey }
it { expect(gitlab_shell.exists?(group.path)).to be_falsey }
it { expect(gitlab_shell.exists?(remove_path)).to be_falsey }
end
context 'Sidekiq fake' do
......@@ -33,8 +33,8 @@ describe DestroyGroupService do
Sidekiq::Testing.fake! { destroy_group(group, user) }
end
it { gitlab_shell.exists?(group.path).should be_falsey }
it { gitlab_shell.exists?(remove_path).should be_truthy }
it { expect(gitlab_shell.exists?(group.path)).to be_falsey }
it { expect(gitlab_shell.exists?(remove_path)).to be_truthy }
end
end
......
......@@ -291,6 +291,15 @@ describe GitPushService do
).once
end
end
it "doesn't close issues when external issue tracker is in use" do
allow(project).to receive(:default_issues_tracker?).and_return(false)
# The push still shouldn't create cross-reference notes.
expect {
service.execute(project, user, @oldrev, @newrev, 'refs/heads/hurf')
}.not_to change { Note.where(project_id: project.id, system: true).count }
end
end
describe "empty project" do
......
......@@ -31,5 +31,15 @@ describe Issues::CloseService do
expect(note.note).to include "Status changed to closed"
end
end
context "external issue tracker" do
before do
allow(project).to receive(:default_issues_tracker?).and_return(false)
@issue = Issues::CloseService.new(project, user, {}).execute(issue)
end
it { expect(@issue).to be_valid }
it { expect(@issue).to be_opened }
end
end
end
......@@ -12,9 +12,9 @@ describe Projects::DestroyService do
Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
end
it { Project.all.should_not include(project) }
it { Dir.exists?(path).should be_falsey }
it { Dir.exists?(remove_path).should be_falsey }
it { expect(Project.all).not_to include(project) }
it { expect(Dir.exists?(path)).to be_falsey }
it { expect(Dir.exists?(remove_path)).to be_falsey }
end
context 'Sidekiq fake' do
......@@ -23,9 +23,9 @@ describe Projects::DestroyService do
Sidekiq::Testing.fake! { destroy_project(project, user, {}) }
end
it { Project.all.should_not include(project) }
it { Dir.exists?(path).should be_falsey }
it { Dir.exists?(remove_path).should be_truthy }
it { expect(Project.all).not_to include(project) }
it { expect(Dir.exists?(path)).to be_falsey }
it { expect(Dir.exists?(remove_path)).to be_truthy }
end
def destroy_project(project, user, params)
......
......@@ -20,8 +20,7 @@ describe Projects::TransferService do
@result = transfer_project(project, user, new_namespace_id: nil)
end
it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil
it { expect(@result).to be_falsey }
it { expect(@result).to eq false }
it { expect(project.namespace).to eq(user.namespace) }
end
......@@ -30,8 +29,7 @@ describe Projects::TransferService do
@result = transfer_project(project, user, new_namespace_id: group.id)
end
it { expect(@result).not_to be_nil } # { result.should be_false } passes on nil
it { expect(@result).to be_falsey }
it { expect(@result).to eq false }
it { expect(project.namespace).to eq(user.namespace) }
end
......
......@@ -338,6 +338,15 @@ describe SystemNoteService do
to be_falsey
end
end
context 'when notable is an ExternalIssue' do
let(:noteable) { ExternalIssue.new('EXT-1234', project) }
it 'is truthy' do
mentioner = noteable.dup
expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
to be_truthy
end
end
end
describe '.cross_reference_exists?' do
......
......@@ -19,3 +19,36 @@ unless ENV['CI'] || ENV['CI_SERVER']
# Keep only the screenshots generated from the last failing test suite
Capybara::Screenshot.prune_strategy = :keep_last_run
end
module CapybaraHelpers
# Execute a block a certain number of times before considering it a failure
#
# The given block is called, and if it raises a `Capybara::ExpectationNotMet`
# error, we wait `interval` seconds and then try again, until `retries` is
# met.
#
# This allows for better handling of timing-sensitive expectations in a
# sketchy CI environment, for example.
#
# interval - Delay between retries in seconds (default: 0.5)
# retries - Number of times to execute before failing (default: 5)
def allowing_for_delay(interval: 0.5, retries: 5)
tries = 0
begin
yield
rescue Capybara::ExpectationNotMet => ex
if tries <= retries
tries += 1
sleep interval
retry
else
raise ex
end
end
end
end
RSpec.configure do |config|
config.include CapybaraHelpers, type: :feature
end
module LoginHelpers
# Internal: Create and log in as a user of the specified role
# Internal: Log in as a specific user or a new user of a specific role
#
# role - User role (e.g., :admin, :user)
def login_as(role)
@user = create(role)
# user_or_role - User object, or a role to create (e.g., :admin, :user)
#
# Examples:
#
# # Create a user automatically
# login_as(:user)
#
# # Create an admin automatically
# login_as(:admin)
#
# # Provide an existing User record
# user = create(:user)
# login_as(user)
def login_as(user_or_role)
if user_or_role.kind_of?(User)
@user = user_or_role
else
@user = create(user_or_role)
end
login_with(@user)
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment