Commit ffea9c46 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into...

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into store-variables-and-when-in-builds-table

# Conflicts:
#	db/schema.rb
parents 22c2814b f1083ba1
This diff is collapsed.
This diff is collapsed.
...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.10.0 (unreleased) v 8.10.0 (unreleased)
- Expose {should,force}_remove_source_branch (Ben Boeckel) - Expose {should,force}_remove_source_branch (Ben Boeckel)
- Disable PostgreSQL statement timeout during migrations
- Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho) - Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849 - Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666 - Replace Haml with Hamlit to make view rendering faster. !3666
...@@ -23,12 +24,15 @@ v 8.10.0 (unreleased) ...@@ -23,12 +24,15 @@ v 8.10.0 (unreleased)
- Add Sidekiq queue duration to transaction metrics. - Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964 - Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs - Let Workhorse serve format-patch diffs
- Display tooltip for mentioned users and groups !5261 (winniehell)
- Added day name to contribution calendar tooltips - Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810 - Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell) - Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836 - Fix MR-auto-close text added to description. !4836
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection) - Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
- Add Spring EmojiOne updates. - Add Spring EmojiOne updates.
- Fix fetching LFS objects for private CI projects
- Add syntax for multiline blockquote using `>>>` fence !3954 - Add syntax for multiline blockquote using `>>>` fence !3954
- Fix viewing notification settings when a project is pending deletion - Fix viewing notification settings when a project is pending deletion
- Updated compare dropdown menus to use GL dropdown - Updated compare dropdown menus to use GL dropdown
...@@ -53,12 +57,16 @@ v 8.10.0 (unreleased) ...@@ -53,12 +57,16 @@ v 8.10.0 (unreleased)
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets. - Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data - PipelinesFinder uses git cache data
- Track a user who created a pipeline
- Actually render old and new sections of parallel diff next to each other - Actually render old and new sections of parallel diff next to each other
- Throttle the update of `project.pushes_since_gc` to 1 minute. - Throttle the update of `project.pushes_since_gc` to 1 minute.
- Allow expanding and collapsing files in diff view (!4990) - Allow expanding and collapsing files in diff view (!4990)
- Collapse large diffs by default (!4990) - Collapse large diffs by default (!4990)
- Fix mentioned users list on diff notes
- Fix creation of deployment on build that is retried, redeployed or rollback
- Check for conflicts with existing Project's wiki path when creating a new project. - Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork - Show last push widget in upstream after push to fork
- Fix stage status shown for pipelines
- Cache todos pending/done dashboard query counts. - Cache todos pending/done dashboard query counts.
- Don't instantiate a git tree on Projects show default view - Don't instantiate a git tree on Projects show default view
- Bump Rinku to 2.0.0 - Bump Rinku to 2.0.0
...@@ -79,7 +87,9 @@ v 8.10.0 (unreleased) ...@@ -79,7 +87,9 @@ v 8.10.0 (unreleased)
- Add basic system information like memory and disk usage to the admin panel - Add basic system information like memory and disk usage to the admin panel
- Don't garbage collect commits that have related DB records like comments - Don't garbage collect commits that have related DB records like comments
- More descriptive message for git hooks and file locks - More descriptive message for git hooks and file locks
- Aliases of award emoji should be stored as original name. !5060 (dixpac)
- Handle custom Git hook result in GitLab UI - Handle custom Git hook result in GitLab UI
- Allow to access Container Registry for Public and Internal projects
- Allow '?', or '&' for label names - Allow '?', or '&' for label names
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests - Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page - Add date when user joined the team on the member page
...@@ -95,6 +105,12 @@ v 8.10.0 (unreleased) ...@@ -95,6 +105,12 @@ v 8.10.0 (unreleased)
- Redesign Builds and Pipelines pages - Redesign Builds and Pipelines pages
- Change status color and icon for running builds - Change status color and icon for running builds
- Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.` - Fix markdown rendering for: consecutive labels references, label references that begin with a digit or contains `.`
- Project export filename now includes the project and namespace path
- Fix last update timestamp on issues not preserved on gitlab.com and project imports
- Fix issues importing projects from EE to CE
- Fix creating group with space in group path
- Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
- Limit the number of retries on error to 3 for exporting projects
v 8.9.6 v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154 - Fix importing of events under notes for GitLab projects. !5154
......
...@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap' ...@@ -61,7 +61,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: 'omniauth-ldap'
# Git Wiki # Git Wiki
# Required manually in config/initializers/gollum.rb to control load order # Required manually in config/initializers/gollum.rb to control load order
gem 'gollum-lib', '~> 4.1.0', require: false gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.2', require: false gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
# Language detection # Language detection
...@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5' ...@@ -105,7 +105,7 @@ gem 'seed-fu', '~> 2.3.5'
# Markdown and HTML processing # Markdown and HTML processing
gem 'html-pipeline', '~> 1.11.0' gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1' gem 'github-markup', '~> 1.4'
gem 'redcarpet', '~> 3.3.3' gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12' ...@@ -113,7 +113,7 @@ gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 1.5.2' gem 'asciidoctor', '~> 1.5.2'
gem 'rouge', '~> 1.11' gem 'rouge', '~> 2.0'
# See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s # See https://groups.google.com/forum/#!topic/ruby-security-ann/aSbgDiwb24s
# and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM # and https://groups.google.com/forum/#!topic/ruby-security-ann/Dy7YiKb_pMM
...@@ -299,7 +299,7 @@ group :development, :test do ...@@ -299,7 +299,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.40.0', require: false gem 'rubocop', '~> 0.41.2', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'simplecov', '~> 0.11.0', require: false gem 'simplecov', '~> 0.11.0', require: false
......
...@@ -58,7 +58,7 @@ GEM ...@@ -58,7 +58,7 @@ GEM
faraday_middleware-multi_json (~> 0.0) faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0) oauth2 (~> 1.0)
asciidoctor (1.5.3) asciidoctor (1.5.3)
ast (2.2.0) ast (2.3.0)
attr_encrypted (3.0.1) attr_encrypted (3.0.1)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.0.0) attr_required (1.0.0)
...@@ -264,7 +264,7 @@ GEM ...@@ -264,7 +264,7 @@ GEM
escape_utils (~> 1.1.0) escape_utils (~> 1.1.0)
mime-types (>= 1.19) mime-types (>= 1.19)
rugged (>= 0.23.0b) rugged (>= 0.23.0b)
github-markup (1.3.3) github-markup (1.4.0)
gitlab-flowdock-git-hook (1.0.1) gitlab-flowdock-git-hook (1.0.1)
flowdock (~> 0.7) flowdock (~> 0.7)
gitlab-grit (>= 2.4.1) gitlab-grit (>= 2.4.1)
...@@ -287,13 +287,13 @@ GEM ...@@ -287,13 +287,13 @@ GEM
rubyntlm (~> 0.3) rubyntlm (~> 0.3)
globalid (0.3.6) globalid (0.3.6)
activesupport (>= 4.1.0) activesupport (>= 4.1.0)
gollum-grit_adapter (1.0.0) gollum-grit_adapter (1.0.1)
gitlab-grit (~> 2.7, >= 2.7.1) gitlab-grit (~> 2.7, >= 2.7.1)
gollum-lib (4.1.0) gollum-lib (4.2.1)
github-markup (~> 1.3.3) github-markup (~> 1.4.0)
gollum-grit_adapter (~> 1.0) gollum-grit_adapter (~> 1.0)
nokogiri (~> 1.6.4) nokogiri (~> 1.6.4)
rouge (~> 1.9) rouge (~> 2.0)
sanitize (~> 2.1.0) sanitize (~> 2.1.0)
stringex (~> 2.5.1) stringex (~> 2.5.1)
gollum-rugged_adapter (0.4.2) gollum-rugged_adapter (0.4.2)
...@@ -473,7 +473,7 @@ GEM ...@@ -473,7 +473,7 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.1.4)
activerecord (~> 4.0) activerecord (~> 4.0)
parser (2.3.1.0) parser (2.3.1.2)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
pkg-config (1.1.7) pkg-config (1.1.7)
...@@ -578,7 +578,7 @@ GEM ...@@ -578,7 +578,7 @@ GEM
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (1.11.0) rouge (2.0.3)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
...@@ -606,8 +606,8 @@ GEM ...@@ -606,8 +606,8 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.5.0) rspec-support (3.5.0)
rubocop (0.40.0) rubocop (0.41.2)
parser (>= 2.3.1.0, < 3.0) parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
...@@ -758,7 +758,7 @@ GEM ...@@ -758,7 +758,7 @@ GEM
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.7.2) unf_ext (0.0.7.2)
unicode-display_width (1.0.5) unicode-display_width (1.1.0)
unicorn (4.9.0) unicorn (4.9.0)
kgio (~> 2.6) kgio (~> 2.6)
rack rack
...@@ -859,12 +859,12 @@ DEPENDENCIES ...@@ -859,12 +859,12 @@ DEPENDENCIES
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
gemojione (~> 2.6) gemojione (~> 2.6)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
github-markup (~> 1.3.1) github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.2) gitlab_git (~> 10.2)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.1.0) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.0.1) gon (~> 6.0.1)
grape (~> 0.13.0) grape (~> 0.13.0)
...@@ -933,11 +933,11 @@ DEPENDENCIES ...@@ -933,11 +933,11 @@ DEPENDENCIES
request_store (~> 1.3.0) request_store (~> 1.3.0)
rerun (~> 0.11.0) rerun (~> 0.11.0)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.11) rouge (~> 2.0)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0) rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5) rspec-retry (~> 0.4.5)
rubocop (~> 0.40.0) rubocop (~> 0.41.2)
rubocop-rspec (~> 1.5.0) rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
sanitize (~> 2.0) sanitize (~> 2.0)
......
...@@ -53,7 +53,6 @@ ...@@ -53,7 +53,6 @@
#= require_directory ./u2f #= require_directory ./u2f
#= require_directory . #= require_directory .
#= require fuzzaldrin-plus #= require fuzzaldrin-plus
#= require u2f
window.slugify = (text) -> window.slugify = (text) ->
text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase()
......
...@@ -39,8 +39,6 @@ class Dispatcher ...@@ -39,8 +39,6 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new GLForm($('.issue-form')) new GLForm($('.issue-form'))
new IssuableForm($('.issue-form')) new IssuableForm($('.issue-form'))
new LabelsSelect()
new MilestoneSelect()
when 'projects:merge_requests:new', 'projects:merge_requests:edit' when 'projects:merge_requests:new', 'projects:merge_requests:edit'
new Diff() new Diff()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
......
...@@ -184,22 +184,20 @@ class @LabelsSelect ...@@ -184,22 +184,20 @@ class @LabelsSelect
.value() .value()
if $dropdown.hasClass 'js-extra-options' if $dropdown.hasClass 'js-extra-options'
extraData = []
if showAny
extraData.push(
isAny: true
title: 'Any Label'
)
if showNo if showNo
extraData.push( data.unshift(
id: 0 id: 0
title: 'No Label' title: 'No Label'
) )
if extraData.length if showAny
extraData.push 'divider' data.unshift(
data = extraData.concat(data) isAny: true
title: 'Any Label'
)
if data.length > 2
data.splice 2, 0, 'divider'
callback data callback data
...@@ -289,12 +287,6 @@ class @LabelsSelect ...@@ -289,12 +287,6 @@ class @LabelsSelect
defaultLabel defaultLabel
fieldName: $dropdown.data('field-name') fieldName: $dropdown.data('field-name')
id: (label) -> id: (label) ->
if $dropdown.hasClass('js-issuable-form-dropdown')
if label.id is 0
return
else
return label.id
if $dropdown.hasClass("js-filter-submit") and not label.isAny? if $dropdown.hasClass("js-filter-submit") and not label.isAny?
label.title label.title
else else
...@@ -308,9 +300,6 @@ class @LabelsSelect ...@@ -308,9 +300,6 @@ class @LabelsSelect
$selectbox.hide() $selectbox.hide()
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.removeAttr('style') $value.removeAttr('style')
return if $dropdown.hasClass('js-issuable-form-dropdown')
if $dropdown.hasClass 'js-multiselect' if $dropdown.hasClass 'js-multiselect'
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
selectedLabels = $dropdown selectedLabels = $dropdown
...@@ -332,7 +321,7 @@ class @LabelsSelect ...@@ -332,7 +321,7 @@ class @LabelsSelect
clicked: (label) -> clicked: (label) ->
_this.enableBulkLabelDropdown() _this.enableBulkLabelDropdown()
if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown') if $dropdown.hasClass('js-filter-bulk-update')
return return
page = $('body').data 'page' page = $('body').data 'page'
......
...@@ -62,7 +62,7 @@ class @MilestoneSelect ...@@ -62,7 +62,7 @@ class @MilestoneSelect
title: 'Upcoming' title: 'Upcoming'
) )
if extraOptions.length > 0 if extraOptions.length > 2
extraOptions.push 'divider' extraOptions.push 'divider'
callback(extraOptions.concat(data)) callback(extraOptions.concat(data))
......
...@@ -6,8 +6,20 @@ ...@@ -6,8 +6,20 @@
class @U2FAuthenticate class @U2FAuthenticate
constructor: (@container, u2fParams) -> constructor: (@container, u2fParams) ->
@appId = u2fParams.app_id @appId = u2fParams.app_id
@challenges = u2fParams.challenges @challenge = u2fParams.challenge
@signRequests = u2fParams.sign_requests
# The U2F Javascript API v1.1 requires a single challenge, with
# _no challenges per-request_. The U2F Javascript API v1.0 requires a
# challenge per-request, which is done by copying the single challenge
# into every request.
#
# In either case, we don't need the per-request challenges that the server
# has generated, so we can remove them.
#
# Note: The server library fixes this behaviour in (unreleased) version 1.0.0.
# This can be removed once we upgrade.
# https://github.com/castle/ruby-u2f/commit/103f428071a81cd3d5f80c2e77d522d5029946a4
@signRequests = u2fParams.sign_requests.map (request) -> _(request).omit('challenge')
start: () => start: () =>
if U2FUtil.isU2FSupported() if U2FUtil.isU2FSupported()
...@@ -16,7 +28,7 @@ class @U2FAuthenticate ...@@ -16,7 +28,7 @@ class @U2FAuthenticate
@renderNotSupported() @renderNotSupported()
authenticate: () => authenticate: () =>
u2f.sign(@appId, @challenges, @signRequests, (response) => u2f.sign(@appId, @challenge, @signRequests, (response) =>
if response.errorCode if response.errorCode
error = new U2FError(response.errorCode) error = new U2FError(response.errorCode)
@renderError(error); @renderError(error);
......
class @U2FUtil
@isU2FSupported: ->
window.u2f
# Helper class for U2F (universal 2nd factor) device registration and authentication.
class @U2FUtil
@isU2FSupported: ->
if @testMode
true
else
gon.u2f.browser_supports_u2f
@enableTestMode: ->
@testMode = true
<% if Rails.env.test? %>
U2FUtil.enableTestMode();
<% end %>
...@@ -155,13 +155,11 @@ class @UsersSelect ...@@ -155,13 +155,11 @@ class @UsersSelect
# display:block overrides the hide-collapse rule # display:block overrides the hide-collapse rule
$value.css('display', '') $value.css('display', '')
clicked: (user, $el, e) -> clicked: (user) ->
page = $('body').data 'page' page = $('body').data 'page'
isIssueIndex = page is 'projects:issues:index' isIssueIndex = page is 'projects:issues:index'
isMRIndex = page is page is 'projects:merge_requests:index' isMRIndex = page is page is 'projects:merge_requests:index'
if $dropdown.hasClass('js-filter-bulk-update') or $dropdown.hasClass('js-issuable-form-dropdown') if $dropdown.hasClass('js-filter-bulk-update')
e.preventDefault()
selectedId = user.id
return return
if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex) if $dropdown.hasClass('js-filter-submit') and (isIssueIndex or isMRIndex)
...@@ -174,8 +172,7 @@ class @UsersSelect ...@@ -174,8 +172,7 @@ class @UsersSelect
.closest('.selectbox') .closest('.selectbox')
.find("input[name='#{$dropdown.data('field-name')}']").val() .find("input[name='#{$dropdown.data('field-name')}']").val()
assignTo(selected) assignTo(selected)
id: (user) ->
user.id
renderRow: (user) -> renderRow: (user) ->
username = if user.username then "@#{user.username}" else "" username = if user.username then "@#{user.username}" else ""
avatar = if user.avatar_url then user.avatar_url else false avatar = if user.avatar_url then user.avatar_url else false
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 6px; right: 6px;
margin-top: -6px; margin-top: -4px;
color: $dropdown-toggle-icon-color; color: $dropdown-toggle-icon-color;
font-size: 10px; font-size: 10px;
} }
......
...@@ -324,10 +324,6 @@ ...@@ -324,10 +324,6 @@
.issuable-form-select-holder { .issuable-form-select-holder {
display: inline-block; display: inline-block;
width: 250px; width: 250px;
.dropdown-menu-toggle {
width: 100%;
}
} }
.table-holder { .table-holder {
......
...@@ -129,6 +129,8 @@ ...@@ -129,6 +129,8 @@
} }
.cancel-retry-btns { .cancel-retry-btns {
vertical-align: middle;
.btn:not(:first-child) { .btn:not(:first-child) {
margin-left: 8px; margin-left: 8px;
} }
......
...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base ...@@ -344,10 +344,6 @@ class ApplicationController < ActionController::Base
session[:skip_tfa] && session[:skip_tfa] > Time.current session[:skip_tfa] && session[:skip_tfa] > Time.current
end end
def browser_supports_u2f?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
def redirect_to_home_page_url? def redirect_to_home_page_url?
# If user is not signed-in and tries to access root_path - redirect him to landing page # If user is not signed-in and tries to access root_path - redirect him to landing page
# Don't redirect to the default URL to prevent endless redirections # Don't redirect to the default URL to prevent endless redirections
......
...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor ...@@ -57,7 +57,7 @@ module AuthenticatesWithTwoFactor
# Authenticate using the response from a U2F (universal 2nd factor) device # Authenticate using the response from a U2F (universal 2nd factor) device
def authenticate_with_two_factor_via_u2f(user) def authenticate_with_two_factor_via_u2f(user)
if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenges]) if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
# Remove any lingering user data from login # Remove any lingering user data from login
session.delete(:otp_user_id) session.delete(:otp_user_id)
session.delete(:challenges) session.delete(:challenges)
...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor ...@@ -77,11 +77,9 @@ module AuthenticatesWithTwoFactor
if key_handles.present? if key_handles.present?
sign_requests = u2f.authentication_requests(key_handles) sign_requests = u2f.authentication_requests(key_handles)
challenges = sign_requests.map(&:challenge) session[:challenge] ||= u2f.challenge
session[:challenges] = challenges gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id,
gon.push(u2f: { challenges: challenges, app_id: u2f_app_id, sign_requests: sign_requests })
sign_requests: sign_requests,
browser_supports_u2f: browser_supports_u2f? })
end end
end end
end end
...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController ...@@ -100,7 +100,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id, gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id,
register_requests: registration_requests, register_requests: registration_requests,
sign_requests: sign_requests, sign_requests: sign_requests })
browser_supports_u2f: browser_supports_u2f? })
end end
end end
...@@ -119,10 +119,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -119,10 +119,6 @@ class Projects::IssuesController < Projects::ApplicationController
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
end end
end end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end end
def referenced_merge_requests def referenced_merge_requests
...@@ -220,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -220,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params def issue_params
params.require(:issue).permit( params.require(:issue).permit(
:title, :assignee_id, :position, :description, :confidential, :title, :assignee_id, :position, :description, :confidential,
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: [] :milestone_id, :due_date, :state_event, :task_num, label_ids: []
) )
end end
......
...@@ -196,9 +196,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -196,9 +196,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
else else
render "edit" render "edit"
end end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end end
def remove_wip def remove_wip
...@@ -427,7 +424,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -427,7 +424,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:title, :assignee_id, :source_project_id, :source_branch, :title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id, :target_project_id, :target_branch, :milestone_id,
:state_event, :description, :task_num, :force_remove_source_branch, :state_event, :description, :task_num, :force_remove_source_branch,
:lock_version, label_ids: [] label_ids: []
) )
end end
......
module BlobHelper module BlobHelper
def highlighter(blob_name, blob_content, repository: nil, nowrap: false) def highlight(blob_name, blob_content, repository: nil, plain: false)
Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository) highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository)
end raw %(<pre class="code highlight"><code>#{highlighted}</code></pre>)
def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
end end
def no_highlight_files def no_highlight_files
......
...@@ -9,7 +9,7 @@ module IssuablesHelper ...@@ -9,7 +9,7 @@ module IssuablesHelper
def multi_label_name(current_labels, default_label) def multi_label_name(current_labels, default_label)
# current_labels may be a string from before # current_labels may be a string from before
if current_labels.is_a?(Array) && current_labels.any? if current_labels.is_a?(Array)
if current_labels.count > 1 if current_labels.count > 1
"#{current_labels[0]} +#{current_labels.count - 1} more" "#{current_labels[0]} +#{current_labels.count - 1} more"
else else
......
module U2fHelper
def inject_u2f_api?
browser.chrome? && browser.version.to_i >= 41 && !browser.device.mobile?
end
end
...@@ -204,7 +204,8 @@ class Ability ...@@ -204,7 +204,8 @@ class Ability
:download_code, :download_code,
:fork_project, :fork_project,
:read_commit_status, :read_commit_status,
:read_pipeline :read_pipeline,
:read_container_image
] ]
end end
......
...@@ -56,6 +56,7 @@ module Ci ...@@ -56,6 +56,7 @@ module Ci
new_build.yaml_variables = build.yaml_variables new_build.yaml_variables = build.yaml_variables
new_build.when = build.when new_build.when = build.when
new_build.user = user new_build.user = user
new_build.environment = build.environment
new_build.save new_build.save
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build) MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
new_build new_build
......
...@@ -6,6 +6,8 @@ module Ci ...@@ -6,6 +6,8 @@ module Ci
self.table_name = 'ci_commits' self.table_name = 'ci_commits'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
belongs_to :user
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
...@@ -215,6 +217,8 @@ module Ci ...@@ -215,6 +217,8 @@ module Ci
end end
def keep_around_commits def keep_around_commits
return unless project
project.repository.keep_around(self.sha) project.repository.keep_around(self.sha)
project.repository.keep_around(self.before_sha) project.repository.keep_around(self.before_sha)
end end
......
...@@ -65,8 +65,7 @@ module Awardable ...@@ -65,8 +65,7 @@ module Awardable
def create_award_emoji(name, current_user) def create_award_emoji(name, current_user)
return unless emoji_awardable? return unless emoji_awardable?
award_emoji.create(name: normalize_name(name), user: current_user)
award_emoji.create(name: name, user: current_user)
end end
def remove_award_emoji(name, current_user) def remove_award_emoji(name, current_user)
...@@ -80,4 +79,10 @@ module Awardable ...@@ -80,4 +79,10 @@ module Awardable
create_award_emoji(emoji_name, current_user) create_award_emoji(emoji_name, current_user)
end end
end end
private
def normalize_name(name)
Gitlab::AwardEmoji.normalize_emoji_name(name)
end
end end
...@@ -87,12 +87,6 @@ module Issuable ...@@ -87,12 +87,6 @@ module Issuable
User.find(assignee_id_was).update_cache_counts if assignee_id_was User.find(assignee_id_was).update_cache_counts if assignee_id_was
assignee.update_cache_counts if assignee assignee.update_cache_counts if assignee
end end
# We want to use optimistic lock for cases when only title or description are involved
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
def locking_enabled?
title_changed? || description_changed?
end
end end
module ClassMethods module ClassMethods
......
...@@ -14,14 +14,14 @@ module Mentionable ...@@ -14,14 +14,14 @@ module Mentionable
attr = attr.to_s attr = attr.to_s
mentionable_attrs << [attr, options] mentionable_attrs << [attr, options]
end end
end
included do
# Accessor for attributes marked mentionable. # Accessor for attributes marked mentionable.
def mentionable_attrs cattr_accessor :mentionable_attrs, instance_accessor: false do
@mentionable_attrs ||= [] []
end
end end
included do
if self < Participable if self < Participable
participant -> (user, ext) { all_references(user, extractor: ext) } participant -> (user, ext) { all_references(user, extractor: ext) }
end end
......
...@@ -41,9 +41,12 @@ module Participable ...@@ -41,9 +41,12 @@ module Participable
def participant(attr) def participant(attr)
participant_attrs << attr participant_attrs << attr
end end
end
def participant_attrs included do
@participant_attrs ||= [] # Accessor for participant attributes.
cattr_accessor :participant_attrs, instance_accessor: false do
[]
end end
end end
......
...@@ -38,7 +38,7 @@ class LegacyDiffNote < Note ...@@ -38,7 +38,7 @@ class LegacyDiffNote < Note
end end
def diff_line def diff_line
@diff_line ||= diff_file.line_for_line_code(self.line_code) @diff_line ||= diff_file.line_for_line_code(self.line_code) if diff_file
end end
def for_line?(line) def for_line?(line)
...@@ -55,7 +55,7 @@ class LegacyDiffNote < Note ...@@ -55,7 +55,7 @@ class LegacyDiffNote < Note
def active? def active?
return @active if defined?(@active) return @active if defined?(@active)
return true if for_commit? return true if for_commit?
return true unless self.diff return true unless diff_line
return false unless noteable return false unless noteable
noteable_diff = find_noteable_diff noteable_diff = find_noteable_diff
......
...@@ -229,8 +229,7 @@ class Note < ActiveRecord::Base ...@@ -229,8 +229,7 @@ class Note < ActiveRecord::Base
end end
def award_emoji_name def award_emoji_name
original_name = note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1] note.match(Banzai::Filter::EmojiFilter.emoji_pattern)[1]
Gitlab::AwardEmoji.normalize_emoji_name(original_name)
end end
private private
......
...@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base ...@@ -162,7 +162,7 @@ class Project < ActiveRecord::Base
validates :namespace, presence: true validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id
validates :import_url, addressable_url: true, if: :import_url validates :import_url, addressable_url: true, if: :external_import?
validates :star_count, numericality: { greater_than_or_equal_to: 0 } validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create validate :check_limit, on: :create
validate :avatar_type, validate :avatar_type,
...@@ -482,7 +482,7 @@ class Project < ActiveRecord::Base ...@@ -482,7 +482,7 @@ class Project < ActiveRecord::Base
end end
def create_or_update_import_data(data: nil, credentials: nil) def create_or_update_import_data(data: nil, credentials: nil)
return unless valid_import_url? return unless import_url.present? && valid_import_url?
project_import_data = import_data || build_import_data project_import_data = import_data || build_import_data
if data if data
...@@ -1038,8 +1038,8 @@ class Project < ActiveRecord::Base ...@@ -1038,8 +1038,8 @@ class Project < ActiveRecord::Base
pipelines.order(id: :desc).find_by(sha: sha, ref: ref) pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
end end
def ensure_pipeline(sha, ref) def ensure_pipeline(sha, ref, current_user = nil)
pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref) pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref, user: current_user)
end end
def enable_ci def enable_ci
......
...@@ -85,6 +85,7 @@ class User < ActiveRecord::Base ...@@ -85,6 +85,7 @@ class User < ActiveRecord::Base
has_one :abuse_report, dependent: :destroy has_one :abuse_report, dependent: :destroy
has_many :spam_logs, dependent: :destroy has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build' has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline'
has_many :todos, dependent: :destroy has_many :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy has_many :notification_settings, dependent: :destroy
has_many :award_emoji, dependent: :destroy has_many :award_emoji, dependent: :destroy
......
...@@ -2,6 +2,7 @@ module Ci ...@@ -2,6 +2,7 @@ module Ci
class CreatePipelineService < BaseService class CreatePipelineService < BaseService
def execute def execute
pipeline = project.pipelines.new(params) pipeline = project.pipelines.new(params)
pipeline.user = current_user
unless ref_names.include?(params[:ref]) unless ref_names.include?(params[:ref])
pipeline.errors.add(:base, 'Reference not found') pipeline.errors.add(:base, 'Reference not found')
......
...@@ -14,7 +14,13 @@ class CreateCommitBuildsService ...@@ -14,7 +14,13 @@ class CreateCommitBuildsService
return false return false
end end
@pipeline = Ci::Pipeline.new(project: project, sha: sha, ref: ref, before_sha: before_sha, tag: tag) @pipeline = Ci::Pipeline.new(
project: project,
sha: sha,
ref: ref,
before_sha: before_sha,
tag: tag,
user: user)
## ##
# Skip creating pipeline if no gitlab-ci.yml is found # Skip creating pipeline if no gitlab-ci.yml is found
......
...@@ -8,7 +8,6 @@ module Notes ...@@ -8,7 +8,6 @@ module Notes
if note.award_emoji? if note.award_emoji?
noteable = note.noteable noteable = note.noteable
todo_service.new_award_emoji(noteable, current_user) todo_service.new_award_emoji(noteable, current_user)
return noteable.create_award_emoji(note.award_emoji_name, current_user) return noteable.create_award_emoji(note.award_emoji_name, current_user)
end end
......
...@@ -10,7 +10,7 @@ module Projects ...@@ -10,7 +10,7 @@ module Projects
def save_all def save_all
if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save) if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
Gitlab::ImportExport::Saver.save(shared: @shared) Gitlab::ImportExport::Saver.save(project: project, shared: @shared)
notify_success notify_success
else else
cleanup_and_notify cleanup_and_notify
......
...@@ -194,7 +194,7 @@ class TodoService ...@@ -194,7 +194,7 @@ class TodoService
end end
def create_assignment_todo(issuable, author) def create_assignment_todo(issuable, author)
if issuable.assignee && issuable.assignee != author if issuable.assignee
attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED) attributes = attributes_for_todo(issuable.project, issuable, author, Todo::ASSIGNED)
create_todos(issuable.assignee, attributes) create_todos(issuable.assignee, attributes)
end end
...@@ -239,7 +239,6 @@ class TodoService ...@@ -239,7 +239,6 @@ class TodoService
def filter_mentioned_users(project, target, author) def filter_mentioned_users(project, target, author)
mentioned_users = target.mentioned_users(author) mentioned_users = target.mentioned_users(author)
mentioned_users = reject_users_without_access(mentioned_users, project, target) mentioned_users = reject_users_without_access(mentioned_users, project, target)
mentioned_users.delete(author)
mentioned_users.uniq mentioned_users.uniq
end end
......
- if inject_u2f_api?
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('u2f.js')
%div %div
.login-box .login-box
.login-heading .login-heading
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
name: "#{j(@project.name)}" name: "#{j(@project.name)}"
}; };
- if @group and @group.path - if @group && @group.persisted? && @group.path
:javascript :javascript
gl.groupOptions = gl.groupOptions || {}; gl.groupOptions = gl.groupOptions || {};
gl.groupOptions["#{j(@group.path)}"] = { gl.groupOptions["#{j(@group.path)}"] = {
......
- if @note.diff_note? - if @note.diff_note? && @note.diff_file
%p.details %p.details
New comment on diff for New comment on diff for
= link_to @note.diff_file.file_path, @target_url = link_to @note.diff_file.file_path, @target_url
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
- header_title "Two-Factor Authentication", profile_two_factor_auth_path - header_title "Two-Factor Authentication", profile_two_factor_auth_path
= render 'profiles/head' = render 'profiles/head'
- if inject_u2f_api?
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('u2f.js')
.row.prepend-top-default .row.prepend-top-default
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
Cant find HEAD commit for this branch Cant find HEAD commit for this branch
- stages_status = pipeline.statuses.stages_status - stages_status = pipeline.statuses.latest.stages_status
- stages.each do |stage| - stages.each do |stage|
%td %td
- status = stages_status[stage] - status = stages_status[stage]
...@@ -58,16 +58,7 @@ ...@@ -58,16 +58,7 @@
.controls.hidden-xs.pull-right .controls.hidden-xs.pull-right
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? } - artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
- if artifacts.present? - if artifacts.present?
.btn-group.inline .inline
.btn-group
%a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
= icon("play")
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to '#' do
= icon("play")
%span Deploy to production
.btn-group .btn-group
%a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'} %a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'}
= icon("download") = icon("download")
...@@ -80,7 +71,7 @@ ...@@ -80,7 +71,7 @@
%span Download '#{build.name}' artifacts %span Download '#{build.name}' artifacts
- if can?(current_user, :update_pipeline, @project) - if can?(current_user, :update_pipeline, @project)
.cancel-retry-btns .cancel-retry-btns.inline
- if pipeline.retryable? - if pipeline.retryable?
= link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do = link_to retry_namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: 'btn has-tooltip', title: "Retry", method: :post do
= icon("repeat") = icon("repeat")
......
...@@ -21,10 +21,10 @@ ...@@ -21,10 +21,10 @@
placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } }) placeholder: "Search assignee", data: { any_user: "Any Assignee", first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (@project.id if @project), selected: params[:assignee_id], field_name: "assignee_id", default_label: "Assignee" } })
.filter-item.inline.milestone-filter .filter-item.inline.milestone-filter
= render "shared/issuable/milestone_dropdown", selected: params[:milestone_title], name: :milestone_title, show_any: true, show_upcoming: true = render "shared/issuable/milestone_dropdown"
.filter-item.inline.labels-filter .filter-item.inline.labels-filter
= render "shared/issuable/label_dropdown", selected: params[:label_name], data_options: { field_name: "label_name[]" } = render "shared/issuable/label_dropdown"
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
......
= form_errors(issuable) = form_errors(issuable)
- if @conflict
.alert.alert-danger
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
Please check out
= link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank"
and make sure your changes will not unintentionally remove theirs
.form-group .form-group
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
.col-sm-10 .col-sm-10
...@@ -59,24 +52,38 @@ ...@@ -59,24 +52,38 @@
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder .issuable-form-select-holder
- project = @target_project || @project = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
- if issuable.assignee_id placeholder: 'Select assignee', class: 'custom-form-control', null_user: true,
= hidden_field_tag("#{issuable.class.model_name.param_key}[assignee_id]", issuable.assignee_id) selected: issuable.assignee_id, project: @target_project || @project,
= dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-user-search js-issuable-form-dropdown js-assignee-search", title: "Filter by assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", first_user: true, current_user: true, include_blank: true)
placeholder: "Search assignee", data: { first_user: (current_user.username if current_user), null_user: true, current_user: true, project_id: (project.id if project), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee" } }) %div
= link_to 'Assign to me', '#', class: 'assign-to-me-link prepend-top-5 inline'
.form-group.issue-milestone .form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- if milestone_options(issuable).present?
.issuable-form-select-holder .issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone_id, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false = f.select(:milestone_id, milestone_options(issuable),
{ include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } })
- else
.prepend-top-10
%span.light No open milestones available.
- if can? current_user, :admin_milestone, issuable.project
%div
= link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
.form-group .form-group
- has_labels = issuable.project.labels.any? - has_labels = issuable.project.labels.any?
- selected_labels = issuable.label_ids.any? ? issuable.label_ids : nil
- label_dropdown_toggle = issuable.labels.map { |label| label.title }
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" = f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- if has_labels
.issuable-form-select-holder .issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: selected_labels, selected_toggle: label_dropdown_toggle, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: "false" } = f.collection_select :label_ids, issuable.project.labels.all, :id, :name,
{ selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" }
- else
%span.light No labels yet.
- if can? current_user, :admin_label, issuable.project
%div
= link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank, class: "prepend-top-5 inline"
- if has_due_date - if has_due_date
.col-lg-6 .col-lg-6
.form-group .form-group
...@@ -142,5 +149,3 @@ ...@@ -142,5 +149,3 @@
= link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" }, = link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" },
method: :delete, class: 'btn btn-danger btn-grouped' method: :delete, class: 'btn btn-danger btn-grouped'
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel' = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
= f.hidden_field :lock_version
...@@ -4,21 +4,19 @@ ...@@ -4,21 +4,19 @@
- show_footer = local_assigns.fetch(:show_footer, true) - show_footer = local_assigns.fetch(:show_footer, true)
- data_options = local_assigns.fetch(:data_options, {}) - data_options = local_assigns.fetch(:data_options, {})
- classes = local_assigns.fetch(:classes, []) - classes = local_assigns.fetch(:classes, [])
- selected = local_assigns.fetch(:selected, nil) - dropdown_data = {toggle: 'dropdown', field_name: 'label_name[]', show_no: "true", show_any: "true", selected: params[:label_name], project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
- selected_toggle = local_assigns.fetch(:selected_toggle, nil)
- dropdown_data = {toggle: 'dropdown', field_name: "label_name[]", show_no: "true", show_any: "true", selected: selected, project_id: @project.try(:id), labels: labels_filter_path, default_label: "Label"}
- dropdown_data.merge!(data_options) - dropdown_data.merge!(data_options)
- classes << 'js-extra-options' if extra_options - classes << 'js-extra-options' if extra_options
- classes << 'js-filter-submit' if filter_submit - classes << 'js-filter-submit' if filter_submit
- if selected.present? - if params[:label_name].present?
- if selected.respond_to?('any?') - if params[:label_name].respond_to?('any?')
- selected.each do |label| - params[:label_name].each do |label|
= hidden_field_tag data_options[:field_name], label, id: nil = hidden_field_tag "label_name[]", label, id: nil
.dropdown .dropdown
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text %span.dropdown-toggle-text
= h(multi_label_name(selected_toggle || selected, "Label")) = h(multi_label_name(params[:label_name], "Label"))
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create } = render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label", show_footer: show_footer, show_create: show_create }
......
- if selected.present? - if params[:milestone_title].present?
= hidden_field_tag(name, selected) = hidden_field_tag(:milestone_title, params[:milestone_title])
= dropdown_tag(milestone_dropdown_label(selected), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable", = dropdown_tag(milestone_dropdown_label(params[:milestone_title]), options: { title: "Filter by milestone", toggle_class: 'js-milestone-select js-filter-submit', filter: true, dropdown_class: "dropdown-menu-selectable",
placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected, project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do placeholder: "Search milestones", footer_content: @project.present?, data: { show_no: true, show_any: true, show_upcoming: true, field_name: "milestone_title", selected: params[:milestone_title], project_id: @project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if @project - if @project
%ul.dropdown-footer-list %ul.dropdown-footer-list
- if can? current_user, :admin_milestone, @project - if can? current_user, :admin_milestone, @project
......
class ProjectExportWorker class ProjectExportWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options queue: :gitlab_shell, retry: true sidekiq_options queue: :gitlab_shell, retry: 3
def perform(current_user_id, project_id) def perform(current_user_id, project_id)
current_user = User.find(current_user_id) current_user = User.find(current_user_id)
......
...@@ -87,6 +87,7 @@ module Gitlab ...@@ -87,6 +87,7 @@ module Gitlab
config.assets.precompile << "profile/application.js" config.assets.precompile << "profile/application.js"
config.assets.precompile << "lib/utils/*.js" config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js" config.assets.precompile << "lib/*.js"
config.assets.precompile << "u2f.js"
# Version of your assets, change this if you want to expire all your assets # Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0' config.assets.version = '1.0'
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddLockToIssuables < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
add_column_with_default :issues, :lock_version, :integer, default: 0
add_column_with_default :merge_requests, :lock_version, :integer, default: 0
end
def down
remove_column :issues, :lock_version
remove_column :merge_requests, :lock_version
end
end
class AddUserIdToPipeline < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_column :ci_commits, :user_id, :integer
end
end
class AddIndexForPipelineUserId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_concurrent_index :ci_commits, :user_id
end
end
...@@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "recaptcha_site_key" t.string "recaptcha_site_key"
t.string "recaptcha_private_key" t.string "recaptcha_private_key"
t.integer "metrics_port", default: 8089 t.integer "metrics_port", default: 8089
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
t.integer "metrics_sample_interval", default: 15 t.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false t.boolean "sentry_enabled", default: false
t.string "sentry_dsn" t.string "sentry_dsn"
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
t.boolean "email_author_in_body", default: false t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility" t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: false t.boolean "repository_checks_enabled", default: false
...@@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "health_check_access_token" t.string "health_check_access_token"
t.boolean "send_user_confirmation_email", default: false t.boolean "send_user_confirmation_email", default: false
t.integer "container_registry_token_expire_delay", default: 5 t.integer "container_registry_token_expire_delay", default: 5
t.boolean "user_default_external", default: false, null: false
t.text "after_sign_up_text" t.text "after_sign_up_text"
t.string "repository_storage", default: "default" t.string "repository_storage", default: "default"
t.string "enabled_git_access_protocol" t.string "enabled_git_access_protocol"
t.boolean "user_default_external", default: false, null: false
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
...@@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.text "artifacts_metadata" t.text "artifacts_metadata"
t.integer "erased_by_id" t.integer "erased_by_id"
t.datetime "erased_at" t.datetime "erased_at"
t.datetime "artifacts_expire_at"
t.string "environment" t.string "environment"
t.datetime "artifacts_expire_at"
t.integer "artifacts_size" t.integer "artifacts_size"
t.string "when" t.string "when"
t.text "yaml_variables" t.text "yaml_variables"
...@@ -201,6 +201,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -201,6 +201,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.datetime "started_at" t.datetime "started_at"
t.datetime "finished_at" t.datetime "finished_at"
t.integer "duration" t.integer "duration"
t.integer "user_id"
end end
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
...@@ -212,6 +213,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -212,6 +213,7 @@ ActiveRecord::Schema.define(version: 20160716115710) do
add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree
add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree
add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree
add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree
create_table "ci_events", force: :cascade do |t| create_table "ci_events", force: :cascade do |t|
t.integer "project_id" t.integer "project_id"
...@@ -483,11 +485,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -483,11 +485,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "state" t.string "state"
t.integer "iid" t.integer "iid"
t.integer "updated_by_id" t.integer "updated_by_id"
t.integer "moved_to_id"
t.boolean "confidential", default: false t.boolean "confidential", default: false
t.datetime "deleted_at" t.datetime "deleted_at"
t.date "due_date" t.date "due_date"
t.integer "lock_version", default: 0, null: false t.integer "moved_to_id"
end end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
...@@ -627,7 +628,6 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -627,7 +628,6 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.integer "merge_user_id" t.integer "merge_user_id"
t.string "merge_commit_sha" t.string "merge_commit_sha"
t.datetime "deleted_at" t.datetime "deleted_at"
t.integer "lock_version", default: 0, null: false
end end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
...@@ -777,10 +777,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -777,10 +777,10 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.integer "user_id", null: false t.integer "user_id", null: false
t.string "token", null: false t.string "token", null: false
t.string "name", null: false t.string "name", null: false
t.boolean "revoked", default: false
t.datetime "expires_at"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.boolean "revoked", default: false
t.datetime "expires_at"
end end
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
...@@ -900,9 +900,9 @@ ActiveRecord::Schema.define(version: 20160716115710) do ...@@ -900,9 +900,9 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.string "type" t.string "type"
t.string "title" t.string "title"
t.integer "project_id" t.integer "project_id"
t.datetime "created_at", null: false t.datetime "created_at"
t.datetime "updated_at", null: false t.datetime "updated_at"
t.boolean "active", null: false t.boolean "active", default: false, null: false
t.text "properties" t.text "properties"
t.boolean "template", default: false t.boolean "template", default: false
t.boolean "push_events", default: true t.boolean "push_events", default: true
......
...@@ -985,11 +985,11 @@ directive defined in `.postgres_services` and `.mysql_services` respectively: ...@@ -985,11 +985,11 @@ directive defined in `.postgres_services` and `.mysql_services` respectively:
- ruby - ruby
test:postgres: test:postgres:
<< *job_definition <<: *job_definition
services: *postgres_definition services: *postgres_definition
test:mysql: test:mysql:
<< *job_definition <<: *job_definition
services: *mysql_definition services: *mysql_definition
``` ```
......
...@@ -44,7 +44,7 @@ it organized and easy to find. ...@@ -44,7 +44,7 @@ it organized and easy to find.
- When introducing a new document, be careful for the headings to be - When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`, of the following GitLab members for a review: `@axil`, `@rspeicher`,
`@dblessing`, `@ashleys`, `@nearlythere`. This is to ensure that no document `@dblessing`, `@ashleys`. This is to ensure that no document
with wrong heading is going live without an audit, thus preventing dead links with wrong heading is going live without an audit, thus preventing dead links
and redirection issues when corrected and redirection issues when corrected
- Leave exactly one newline after a heading - Leave exactly one newline after a heading
......
...@@ -37,7 +37,6 @@ Feature: Project Issues ...@@ -37,7 +37,6 @@ Feature: Project Issues
And I submit new issue "500 error on profile" And I submit new issue "500 error on profile"
Then I should see issue "500 error on profile" Then I should see issue "500 error on profile"
@javascript
Scenario: I submit new unassigned issue with labels Scenario: I submit new unassigned issue with labels
Given project "Shop" has labels: "bug", "feature", "enhancement" Given project "Shop" has labels: "bug", "feature", "enhancement"
And I click link "New Issue" And I click link "New Issue"
......
...@@ -89,7 +89,7 @@ Feature: Project Merge Requests ...@@ -89,7 +89,7 @@ Feature: Project Merge Requests
Then The list should be sorted by "Oldest updated" Then The list should be sorted by "Oldest updated"
@javascript @javascript
Scenario: Visiting Merge Requests from a different Project after sorting Scenario: Visiting Merge Requests from a differente Project after sorting
Given I visit project "Shop" merge requests page Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated" And I sort the list by "Oldest updated"
And I visit dashboard merge requests page And I visit dashboard merge requests page
......
...@@ -135,17 +135,19 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps ...@@ -135,17 +135,19 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps
end end
step 'I click "Assign to" dropdown"' do step 'I click "Assign to" dropdown"' do
click_button 'Assignee' first('.ajax-users-select').click
end end
step 'I should see the target project ID in the input selector' do step 'I should see the target project ID in the input selector' do
expect(find('.js-assignee-search')["data-project-id"]).to eq "#{@project.id}" expect(page).to have_selector("input[data-project-id=\"#{@project.id}\"]")
end end
step 'I should see the users from the target project ID' do step 'I should see the users from the target project ID' do
expect(page).to have_content 'Unassigned' expect(page).to have_selector('.user-result', visible: true, count: 3)
expect(page).to have_content current_user.name users = page.all('.user-name')
expect(page).to have_content @project.users.first.name expect(users[0].text).to eq 'Unassigned'
expect(users[1].text).to eq current_user.name
expect(users[2].text).to eq @project.users.first.name
end end
# Verify a link is generated against the correct project # Verify a link is generated against the correct project
......
...@@ -82,8 +82,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps ...@@ -82,8 +82,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
step 'I submit new issue "500 error on profile" with label \'bug\'' do step 'I submit new issue "500 error on profile" with label \'bug\'' do
fill_in "issue_title", with: "500 error on profile" fill_in "issue_title", with: "500 error on profile"
click_button "Label" select 'bug', from: "Labels"
click_link "bug"
click_button "Submit issue" click_button "Submit issue"
end end
......
...@@ -56,9 +56,9 @@ module API ...@@ -56,9 +56,9 @@ module API
not_found!('Award Emoji') unless can_read_awardable? not_found!('Award Emoji') unless can_read_awardable?
award = awardable.award_emoji.new(name: params[:name], user: current_user) award = awardable.create_award_emoji(params[:name], current_user)
if award.save if award.persisted?
present award, with: Entities::AwardEmoji present award, with: Entities::AwardEmoji
else else
not_found!("Award Emoji #{award.errors.messages}") not_found!("Award Emoji #{award.errors.messages}")
......
...@@ -64,7 +64,7 @@ module API ...@@ -64,7 +64,7 @@ module API
ref = branches.first ref = branches.first
end end
pipeline = @project.ensure_pipeline(commit.sha, ref) pipeline = @project.ensure_pipeline(commit.sha, ref, current_user)
name = params[:name] || params[:context] name = params[:name] || params[:context]
status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref]) status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref])
......
...@@ -63,7 +63,12 @@ module API ...@@ -63,7 +63,12 @@ module API
if access_status.status if access_status.status
# Return the repository full path so that gitlab-shell has it when # Return the repository full path so that gitlab-shell has it when
# handling ssh commands # handling ssh commands
response[:repository_path] = project.repository.path_to_repo response[:repository_path] =
if wiki?
project.wiki.repository.path_to_repo
else
project.repository.path_to_repo
end
end end
response response
......
...@@ -19,14 +19,22 @@ module Banzai ...@@ -19,14 +19,22 @@ module Banzai
language = node.attr('class') language = node.attr('class')
code = node.text code = node.text
css_classes = "code highlight"
lexer = Rouge::Lexer.find_fancy(language) || Rouge::Lexers::PlainText
formatter = Rouge::Formatters::HTML.new
begin begin
highlighted = block_code(code, language) code = formatter.format(lexer.lex(code))
css_classes << " js-syntax-highlight #{lexer.tag}"
rescue rescue
# Gracefully handle syntax highlighter bugs/errors to ensure # Gracefully handle syntax highlighter bugs/errors to ensure
# users can still access an issue/comment/etc. # users can still access an issue/comment/etc.
highlighted = "<pre>#{code}</pre>"
end end
highlighted = %(<pre class="#{css_classes}"><code>#{code}</code></pre>)
# Extracted to a method to measure it # Extracted to a method to measure it
replace_parent_pre_element(node, highlighted) replace_parent_pre_element(node, highlighted)
end end
...@@ -40,8 +48,7 @@ module Banzai ...@@ -40,8 +48,7 @@ module Banzai
# Override Rouge::Plugins::Redcarpet#rouge_formatter # Override Rouge::Plugins::Redcarpet#rouge_formatter
def rouge_formatter(lexer) def rouge_formatter(lexer)
Rouge::Formatters::HTMLGitlab.new( Rouge::Formatters::HTML.new
cssclass: "code highlight js-syntax-highlight #{lexer.tag}")
end end
end end
end end
......
...@@ -112,7 +112,7 @@ module Banzai ...@@ -112,7 +112,7 @@ module Banzai
data = data_attribute(project: project.id, author: author.try(:id)) data = data_attribute(project: project.id, author: author.try(:id))
text = link_text || User.reference_prefix + 'all' text = link_text || User.reference_prefix + 'all'
link_tag(url, data, text) link_tag(url, data, text, 'All Project and Group Members')
end end
def link_to_namespace(namespace, link_text: nil) def link_to_namespace(namespace, link_text: nil)
...@@ -128,7 +128,7 @@ module Banzai ...@@ -128,7 +128,7 @@ module Banzai
data = data_attribute(group: namespace.id) data = data_attribute(group: namespace.id)
text = link_text || Group.reference_prefix + group text = link_text || Group.reference_prefix + group
link_tag(url, data, text) link_tag(url, data, text, namespace.name)
end end
def link_to_user(user, namespace, link_text: nil) def link_to_user(user, namespace, link_text: nil)
...@@ -136,11 +136,11 @@ module Banzai ...@@ -136,11 +136,11 @@ module Banzai
data = data_attribute(user: namespace.owner_id) data = data_attribute(user: namespace.owner_id)
text = link_text || User.reference_prefix + user text = link_text || User.reference_prefix + user
link_tag(url, data, text) link_tag(url, data, text, namespace.owner_name)
end end
def link_tag(url, data, text) def link_tag(url, data, text, title)
%(<a href="#{url}" #{data} class="#{link_class}">#{escape_once(text)}</a>) %(<a href="#{url}" #{data} class="#{link_class}" title="#{escape_once(title)}">#{escape_once(text)}</a>)
end end
end end
end end
......
...@@ -63,7 +63,7 @@ module Grack ...@@ -63,7 +63,7 @@ module Grack
def ci_request?(login, password) def ci_request?(login, password)
matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login) matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
if project && matched_login.present? && git_cmd == 'git-upload-pack' if project && matched_login.present?
underscored_service = matched_login['s'].underscore underscored_service = matched_login['s'].underscore
if underscored_service == 'gitlab_ci' if underscored_service == 'gitlab_ci'
......
...@@ -20,11 +20,19 @@ module Gitlab ...@@ -20,11 +20,19 @@ module Gitlab
if Database.postgresql? if Database.postgresql?
options = options.merge({ algorithm: :concurrently }) options = options.merge({ algorithm: :concurrently })
disable_statement_timeout
end end
add_index(table_name, column_name, options) add_index(table_name, column_name, options)
end end
# Long-running migrations may take more than the timeout allowed by
# the database. Disable the session's statement timeout to ensure
# migrations don't get killed prematurely. (PostgreSQL only)
def disable_statement_timeout
ActiveRecord::Base.connection.execute('SET statement_timeout TO 0') if Database.postgresql?
end
# Updates the value of a column in batches. # Updates the value of a column in batches.
# #
# This method updates the table in batches of 5% of the total row count. # This method updates the table in batches of 5% of the total row count.
...@@ -133,6 +141,8 @@ module Gitlab ...@@ -133,6 +141,8 @@ module Gitlab
'in the body of your migration class' 'in the body of your migration class'
end end
disable_statement_timeout
transaction do transaction do
add_column(table, column, type, default: nil) add_column(table, column, type, default: nil)
......
...@@ -15,6 +15,7 @@ module Gitlab ...@@ -15,6 +15,7 @@ module Gitlab
end end
def execute def execute
ActiveRecord::Base.no_touching do
project_identifier = CGI.escape(project.import_source) project_identifier = CGI.escape(project.import_source)
# Issues && Comments # Issues && Comments
...@@ -39,9 +40,11 @@ module Gitlab ...@@ -39,9 +40,11 @@ module Gitlab
description: body, description: body,
title: issue["title"], title: issue["title"],
state: issue["state"], state: issue["state"],
updated_at: issue["updated_at"],
author_id: gl_user_id(project, issue["author"]["id"]) author_id: gl_user_id(project, issue["author"]["id"])
) )
end end
end
true true
end end
......
module Gitlab module Gitlab
class Highlight class Highlight
def self.highlight(blob_name, blob_content, repository: nil, nowrap: true, plain: false) def self.highlight(blob_name, blob_content, repository: nil, plain: false)
new(blob_name, blob_content, nowrap: nowrap, repository: repository). new(blob_name, blob_content, repository: repository).
highlight(blob_content, continue: false, plain: plain) highlight(blob_content, continue: false, plain: plain)
end end
...@@ -13,30 +13,34 @@ module Gitlab ...@@ -13,30 +13,34 @@ module Gitlab
highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe) highlight(file_name, blob.data, repository: repository).lines.map!(&:html_safe)
end end
attr_reader :lexer def initialize(blob_name, blob_content, repository: nil)
def initialize(blob_name, blob_content, repository: nil, nowrap: true) @formatter = Rouge::Formatters::HTMLGitlab.new
@repository = repository
@blob_name = blob_name @blob_name = blob_name
@blob_content = blob_content @blob_content = blob_content
@repository = repository
@formatter = rouge_formatter(nowrap: nowrap)
@lexer = custom_language || begin
Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
rescue Rouge::Lexer::AmbiguousGuess => e
e.alternatives.sort_by(&:tag).first
end
end end
def highlight(text, continue: true, plain: false) def highlight(text, continue: true, plain: false)
if plain if plain
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe hl_lexer = Rouge::Lexers::PlainText
continue = false
else else
@formatter.format(@lexer.lex(text, continue: continue)).html_safe hl_lexer = self.lexer
end end
@formatter.format(hl_lexer.lex(text, continue: continue)).html_safe
rescue rescue
@formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe @formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
end end
def lexer
@lexer ||= custom_language || begin
Rouge::Lexer.guess(filename: @blob_name, source: @blob_content).new
rescue Rouge::Guesser::Ambiguous => e
e.alternatives.sort_by(&:tag).first
end
end
private private
def custom_language def custom_language
...@@ -46,16 +50,5 @@ module Gitlab ...@@ -46,16 +50,5 @@ module Gitlab
Rouge::Lexer.find_fancy(language_name) Rouge::Lexer.find_fancy(language_name)
end end
def rouge_formatter(options = {})
options = options.reverse_merge(
nowrap: true,
cssclass: 'code highlight',
lineanchors: true,
lineanchorsid: 'LC'
)
Rouge::Formatters::HTMLGitlab.new(options)
end
end end
end end
...@@ -3,6 +3,7 @@ module Gitlab ...@@ -3,6 +3,7 @@ module Gitlab
extend self extend self
VERSION = '0.1.1' VERSION = '0.1.1'
FILENAME_LIMIT = 50
def export_path(relative_path:) def export_path(relative_path:)
File.join(storage_path, relative_path) File.join(storage_path, relative_path)
...@@ -28,6 +29,12 @@ module Gitlab ...@@ -28,6 +29,12 @@ module Gitlab
'VERSION' 'VERSION'
end end
def export_filename(project:)
basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.path}_#{project.path}"
"#{basename[0..FILENAME_LIMIT]}_export.tar.gz"
end
def version def version
VERSION VERSION
end end
......
...@@ -12,7 +12,10 @@ module Gitlab ...@@ -12,7 +12,10 @@ module Gitlab
json = IO.read(@path) json = IO.read(@path)
@tree_hash = ActiveSupport::JSON.decode(json) @tree_hash = ActiveSupport::JSON.decode(json)
@project_members = @tree_hash.delete('project_members') @project_members = @tree_hash.delete('project_members')
ActiveRecord::Base.no_touching do
create_relations create_relations
end
rescue => e rescue => e
@shared.error(e) @shared.error(e)
false false
......
...@@ -87,7 +87,7 @@ module Gitlab ...@@ -87,7 +87,7 @@ module Gitlab
project_id = @relation_hash.delete('project_id') project_id = @relation_hash.delete('project_id')
# project_id may not be part of the export, but we always need to populate it if required. # project_id may not be part of the export, but we always need to populate it if required.
@relation_hash['project_id'] = project_id if relation_class.column_names.include?('project_id') @relation_hash['project_id'] = project_id
@relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id'] @relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id']
@relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id'] @relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id']
@relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id'] @relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id']
...@@ -111,7 +111,7 @@ module Gitlab ...@@ -111,7 +111,7 @@ module Gitlab
end end
def imported_object def imported_object
imported_object = relation_class.new(@relation_hash) imported_object = relation_class.new(parsed_relation_hash)
yield(imported_object) if block_given? yield(imported_object) if block_given?
imported_object.importing = true if imported_object.respond_to?(:importing) imported_object.importing = true if imported_object.respond_to?(:importing)
imported_object imported_object
...@@ -125,6 +125,10 @@ module Gitlab ...@@ -125,6 +125,10 @@ module Gitlab
def admin_user? def admin_user?
@user.is_admin? @user.is_admin?
end end
def parsed_relation_hash
@relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
end
end end
end end
end end
...@@ -7,7 +7,8 @@ module Gitlab ...@@ -7,7 +7,8 @@ module Gitlab
new(*args).save new(*args).save
end end
def initialize(shared:) def initialize(project:, shared:)
@project = project
@shared = shared @shared = shared
end end
...@@ -36,7 +37,7 @@ module Gitlab ...@@ -36,7 +37,7 @@ module Gitlab
end end
def archive_file def archive_file
@archive_file ||= File.join(@shared.export_path, '..', "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_project_export.tar.gz") @archive_file ||= File.join(@shared.export_path, '..', Gitlab::ImportExport.export_filename(project: @project))
end end
end end
end end
......
...@@ -47,6 +47,8 @@ module Gitlab ...@@ -47,6 +47,8 @@ module Gitlab
end end
def render_storage_upload_store_response(oid, size, tmp_file_name) def render_storage_upload_store_response(oid, size, tmp_file_name)
return render_forbidden unless tmp_file_name
render_response_to_push do render_response_to_push do
render_lfs_upload_ok(oid, size, tmp_file_name) render_lfs_upload_ok(oid, size, tmp_file_name)
end end
......
...@@ -74,8 +74,6 @@ module Gitlab ...@@ -74,8 +74,6 @@ module Gitlab
lfs.render_storage_upload_authorize_response(oid, size) lfs.render_storage_upload_authorize_response(oid, size)
else else
tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP']) tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP'])
return nil unless tmp_file_name
lfs.render_storage_upload_store_response(oid, size, tmp_file_name) lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
end end
end end
......
require 'cgi'
module Rouge module Rouge
module Formatters module Formatters
class HTMLGitlab < Rouge::Formatter class HTMLGitlab < Rouge::Formatters::HTML
tag 'html_gitlab' tag 'html_gitlab'
# Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance. # Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
# #
# [+nowrap+] If set to True, don't wrap the output at all, not
# even inside a <tt><pre></tt> tag (default: false).
# [+cssclass+] CSS class for the wrapping <tt><div></tt> tag
# (default: 'highlight').
# [+linenos+] If set to 'table', output line numbers as a table
# with two cells, one containing the line numbers,
# the other the whole code. This is copy paste friendly,
# but may cause alignment problems with some browsers
# or fonts. If set to 'inline', the line numbers will
# be integrated in the <tt><pre></tt> tag that contains
# the code (default: nil).
# [+linenostart+] The line number for the first line (default: 1). # [+linenostart+] The line number for the first line (default: 1).
# [+lineanchors+] If set to true the formatter will wrap each output def initialize(linenostart: 1)
# line in an anchor tag with a name of L-linenumber.
# This allows easy linking to certain lines
# (default: false).
# [+lineanchorsid+] If lineanchors is true the name of the anchors can
# be changed with lineanchorsid to e.g. foo-linenumber
# (default: 'L').
# [+anchorlinenos+] If set to true, will wrap line numbers in <tt><a></tt>
# tags. Used in combination with linenos and lineanchors
# (default: false).
# [+inline_theme+] Inline CSS styles for the <pre> tag (default: false).
def initialize(
nowrap: false,
cssclass: 'highlight',
linenos: nil,
linenostart: 1,
lineanchors: false,
lineanchorsid: 'L',
anchorlinenos: false,
inline_theme: nil
)
@nowrap = nowrap
@cssclass = cssclass
@linenos = linenos
@linenostart = linenostart @linenostart = linenostart
@lineanchors = lineanchors @line_number = linenostart
@lineanchorsid = lineanchorsid
@anchorlinenos = anchorlinenos
@inline_theme = Theme.find(inline_theme).new if inline_theme.is_a?(String)
end
def render(tokens)
case @linenos
when 'table'
render_tableized(tokens)
when 'inline'
render_untableized(tokens)
else
render_untableized(tokens)
end
end
alias_method :format, :render
private
def render_untableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<pre class=\"#{@cssclass}\"><code>" unless @nowrap
html << wrap_lines(data[:code])
html << "</code></pre>\n" unless @nowrap
html
end
def render_tableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<div class=\"#{@cssclass}\">" unless @nowrap
html << '<table><tbody>'
html << "<td class=\"linenos\"><pre>"
html << wrap_linenos(data[:numbers])
html << '</pre></td>'
html << "<td class=\"lines\"><pre><code>"
html << wrap_lines(data[:code])
html << '</code></pre></td>'
html << '</tbody></table>'
html << '</div>' unless @nowrap
html
end
def process_tokens(tokens)
rendered = []
current_line = ''
tokens.each do |tok, val|
# In the case of multi-line values (e.g. comments), we need to apply
# styling to each line since span elements are inline.
val.lines.each do |line|
stripped = line.chomp
current_line << span(tok, stripped)
if line.end_with?("\n")
rendered << current_line
current_line = ''
end
end end
end
# Add leftover text
rendered << current_line if current_line.present?
num_lines = rendered.size def stream(tokens, &b)
numbers = (@linenostart..num_lines + @linenostart - 1).to_a is_first = true
token_lines(tokens) do |line|
yield "\n" unless is_first
is_first = false
{ numbers: numbers, code: rendered } yield %(<span id="LC#{@line_number}" class="line">)
end line.each { |token, value| yield span(token, value) }
yield %(</span>)
def wrap_linenos(numbers) @line_number += 1
if @anchorlinenos
numbers.map! do |number|
"<a href=\"##{@lineanchorsid}#{number}\">#{number}</a>"
end
end
numbers.join("\n")
end
def wrap_lines(lines)
if @lineanchors
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
if @linenos == 'inline'
"<a name=\"L#{number}\"></a>" \
"<span class=\"linenos\">#{number}</span>" \
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
else
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
end
end
elsif @linenos == 'inline'
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
"<span class=\"linenos\">#{number}</span>#{line}"
end
end
lines.join("\n")
end
def span(tok, val)
# http://stackoverflow.com/a/1600584/2587286
val = CGI.escapeHTML(val)
if tok.shortname.empty?
val
else
if @inline_theme
rules = @inline_theme.style_for(tok).rendered_rules
"<span style=\"#{rules.to_a.join(';')}\"#{val}</span>"
else
"<span class=\"#{tok.shortname}\">#{val}</span>"
end
end end
end end
end end
......
require 'spec_helper' require 'spec_helper'
feature 'Group', feature: true do feature 'Group', feature: true do
describe 'description' do
let(:group) { create(:group) }
let(:path) { group_path(group) }
before do before do
login_as(:admin) login_as(:admin)
end end
describe 'creating a group with space in group path' do
it 'renders new group form with validation errors' do
visit new_group_path
fill_in 'Group path', with: 'space group'
click_button 'Create group'
expect(current_path).to eq(groups_path)
expect(page).to have_content("Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-' or end in '.'.")
end
end
describe 'description' do
let(:group) { create(:group) }
let(:path) { group_path(group) }
it 'parses Markdown' do it 'parses Markdown' do
group.update_attribute(:description, 'This is **my** group') group.update_attribute(:description, 'This is **my** group')
visit path visit path
......
...@@ -55,7 +55,7 @@ feature 'issue move to another project' do ...@@ -55,7 +55,7 @@ feature 'issue move to another project' do
first('.select2-choice').click first('.select2-choice').click
end end
fill_in('s2id_autogen1_search', with: new_project_search.name) fill_in('s2id_autogen2_search', with: new_project_search.name)
page.within '.select2-drop' do page.within '.select2-drop' do
expect(page).to have_content(new_project_search.name) expect(page).to have_content(new_project_search.name)
......
...@@ -50,8 +50,9 @@ describe 'Issues', feature: true do ...@@ -50,8 +50,9 @@ describe 'Issues', feature: true do
expect(page).to have_content "Assignee #{@user.name}" expect(page).to have_content "Assignee #{@user.name}"
first('.js-user-search').click first('#s2id_issue_assignee_id').click
click_link 'Unassigned' sleep 2 # wait for ajax stuff to complete
first('.user-result').click
click_button 'Save changes' click_button 'Save changes'
...@@ -120,17 +121,6 @@ describe 'Issues', feature: true do ...@@ -120,17 +121,6 @@ describe 'Issues', feature: true do
expect(page).to have_content date.to_s(:medium) expect(page).to have_content date.to_s(:medium)
end end
end end
it 'warns about version conflict' do
issue.update(title: "New title")
fill_in 'issue_title', with: 'bug 345'
fill_in 'issue_description', with: 'bug description'
click_button 'Save changes'
expect(page).to have_content 'Someone edited the issue the same time you did'
end
end end
end end
......
...@@ -17,16 +17,5 @@ feature 'Edit Merge Request', feature: true do ...@@ -17,16 +17,5 @@ feature 'Edit Merge Request', feature: true do
it 'form should have class js-quick-submit' do it 'form should have class js-quick-submit' do
expect(page).to have_selector('.js-quick-submit') expect(page).to have_selector('.js-quick-submit')
end end
it 'warns about version conflict' do
merge_request.update(title: "New title")
fill_in 'merge_request_title', with: 'bug 345'
fill_in 'merge_request_description', with: 'bug description'
click_button 'Save changes'
expect(page).to have_content 'Someone edited the merge request the same time you did'
end
end end
end end
...@@ -426,4 +426,23 @@ describe "Internal Project Access", feature: true do ...@@ -426,4 +426,23 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/container_registry" do
before do
stub_container_registry_tags('latest')
stub_container_registry_config(enabled: true)
end
subject { namespace_project_container_registry_index_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
end end
...@@ -362,4 +362,23 @@ describe "Private Project Access", feature: true do ...@@ -362,4 +362,23 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/container_registry" do
before do
stub_container_registry_tags('latest')
stub_container_registry_config(enabled: true)
end
subject { namespace_project_container_registry_index_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor }
end
end end
...@@ -426,4 +426,23 @@ describe "Public Project Access", feature: true do ...@@ -426,4 +426,23 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :external }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
end end
describe "GET /:project_path/container_registry" do
before do
stub_container_registry_tags('latest')
stub_container_registry_config(enabled: true)
end
subject { namespace_project_container_registry_index_path(project.namespace, project) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for developer }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :external }
it { is_expected.to be_allowed_for :visitor }
end
end end
require 'spec_helper' require 'spec_helper'
feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: true, js: true do
before { allow_any_instance_of(U2fHelper).to receive(:inject_u2f_api?).and_return(true) }
def register_u2f_device(u2f_device = nil) def register_u2f_device(u2f_device = nil)
u2f_device ||= FakeU2fDevice.new(page) u2f_device ||= FakeU2fDevice.new(page)
u2f_device.respond_to_u2f_registration u2f_device.respond_to_u2f_registration
...@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature: ...@@ -208,21 +210,52 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', feature:
expect(page.body).to match('Authentication via U2F device failed') expect(page.body).to match('Authentication via U2F device failed')
end end
end end
describe "when more than one device has been registered by the same user" do
it "allows logging in with either device" do
# Register first device
user = login_as(:user)
user.update_attribute(:otp_required_for_login, true)
visit profile_two_factor_auth_path
expect(page).to have_content("Your U2F device needs to be set up.")
first_device = register_u2f_device
# Register second device
visit profile_two_factor_auth_path
expect(page).to have_content("Your U2F device needs to be set up.")
second_device = register_u2f_device
logout
# Authenticate as both devices
[first_device, second_device].each do |device|
login_as(user)
device.respond_to_u2f_authentication
click_on "Login Via U2F Device"
expect(page.body).to match('We heard back from your U2F device')
click_on "Authenticate via U2F Device"
expect(page.body).to match('Signed in successfully')
logout
end
end
end end
describe "when two-factor authentication is disabled" do describe "when two-factor authentication is disabled" do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
login_as(user) user = login_as(:user)
user.update_attribute(:otp_required_for_login, true) user.update_attribute(:otp_required_for_login, true)
visit profile_account_path visit profile_account_path
click_on 'Manage Two-Factor Authentication' click_on 'Manage Two-Factor Authentication'
expect(page).to have_content("Your U2F device needs to be set up.")
register_u2f_device register_u2f_device
end end
it "deletes u2f registrations" do it "deletes u2f registrations" do
expect { click_on "Disable" }.to change { U2fRegistration.count }.from(1).to(0) expect { click_on "Disable" }.to change { U2fRegistration.count }.by(-1)
end
end end
end end
end end
...@@ -121,7 +121,7 @@ ...@@ -121,7 +121,7 @@
:type: old :type: old
:number: 9 :number: 9
:text: | :text: |
-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span> -<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
:type: new :type: new
:number: 9 :number: 9
:text: | :text: |
+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span> +<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -241,7 +241,7 @@ ...@@ -241,7 +241,7 @@
:type: old :type: old
:number: 13 :number: 13
:text: | :text: |
-<span id="LC13" class="line"> <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span> -<span id="LC13" class="line"> <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -315,7 +315,7 @@ ...@@ -315,7 +315,7 @@
:type: new :type: new
:number: 15 :number: 15
:text: | :text: |
+<span id="LC15" class="line"> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span></span> +<span id="LC15" class="line"> <span class="s2">"PWD"</span> <span class="o">=&gt;</span> <span class="n">path</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -623,7 +623,7 @@ ...@@ -623,7 +623,7 @@
:type: :type:
:number: 20 :number: 20
:text: |2 :text: |2
<span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span> <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
...@@ -638,7 +638,7 @@ ...@@ -638,7 +638,7 @@
:type: :type:
:number: 26 :number: 26
:text: |2 :text: |2
<span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span> <span id="LC26" class="line"> <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">""</span></span>
:line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26 :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
:position: !ruby/object:Gitlab::Diff::Position :position: !ruby/object:Gitlab::Diff::Position
attributes: attributes:
......
...@@ -16,19 +16,19 @@ describe BlobHelper do ...@@ -16,19 +16,19 @@ describe BlobHelper do
describe '#highlight' do describe '#highlight' do
it 'should return plaintext for unknown lexer context' do it 'should return plaintext for unknown lexer context' do
result = helper.highlight(blob_name, no_context_content, nowrap: true) result = helper.highlight(blob_name, no_context_content)
expect(result).to eq('<span id="LC1" class="line">:type &quot;assem&quot;))</span>') expect(result).to eq(%[<pre class="code highlight"><code><span id="LC1" class="line">:type "assem"))</span></code></pre>])
end end
it 'should highlight single block' do it 'should highlight single block' do
expected = %Q[<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span> expected = %Q[<pre class="code highlight"><code><span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
<span id="LC2" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>] <span id="LC2" class="line"><span class="ss">:type</span> <span class="s">"assem"</span><span class="p">))</span></span></code></pre>]
expect(helper.highlight(blob_name, blob_content, nowrap: true)).to eq(expected) expect(helper.highlight(blob_name, blob_content)).to eq(expected)
end end
it 'should highlight multi-line comments' do it 'should highlight multi-line comments' do
result = helper.highlight(blob_name, multiline_content, nowrap: true) result = helper.highlight(blob_name, multiline_content)
html = Nokogiri::HTML(result) html = Nokogiri::HTML(result)
lines = html.search('.s') lines = html.search('.s')
expect(lines.count).to eq(3) expect(lines.count).to eq(3)
...@@ -41,33 +41,19 @@ describe BlobHelper do ...@@ -41,33 +41,19 @@ describe BlobHelper do
let(:blob_name) { 'test.diff' } let(:blob_name) { 'test.diff' }
let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"} let(:blob_content) { "+aaa\n+bbb\n- ccc\n ddd\n"}
let(:expected) do let(:expected) do
%q(<span id="LC1" class="line"><span class="gi">+aaa</span></span> %q(<pre class="code highlight"><code><span id="LC1" class="line"><span class="gi">+aaa</span></span>
<span id="LC2" class="line"><span class="gi">+bbb</span></span> <span id="LC2" class="line"><span class="gi">+bbb</span></span>
<span id="LC3" class="line"><span class="gd">- ccc</span></span> <span id="LC3" class="line"><span class="gd">- ccc</span></span>
<span id="LC4" class="line"> ddd</span>) <span id="LC4" class="line"> ddd</span></code></pre>)
end end
it 'should highlight each line properly' do it 'should highlight each line properly' do
result = helper.highlight(blob_name, blob_content, nowrap: true) result = helper.highlight(blob_name, blob_content)
expect(result).to eq(expected) expect(result).to eq(expected)
end end
end end
end end
describe "#highlighter" do
it 'should highlight continued blocks' do
# Both lines have LC1 as ID since formatter doesn't support continue at the moment
expected = [
'<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>',
'<span id="LC1" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>'
]
highlighter = helper.highlighter(blob_name, blob_content, nowrap: true)
result = split_content.map{ |content| highlighter.highlight(content) }
expect(result).to eq(expected)
end
end
describe "#sanitize_svg" do describe "#sanitize_svg" do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') } let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
let(:data) { open(input_svg_path).read } let(:data) { open(input_svg_path).read }
......
...@@ -57,7 +57,7 @@ describe EventsHelper do ...@@ -57,7 +57,7 @@ describe EventsHelper do
expected = '<pre class="code highlight js-syntax-highlight ruby">' \ expected = '<pre class="code highlight js-syntax-highlight ruby">' \
"<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \ "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
" <span class=\"s1\">\'hello world\'</span>\n" \ " <span class=\"s1\">\'hello world\'</span>\n" \
"<span class=\"k\">end</span>" \ "<span class=\"k\">end</span>\n" \
'</code></pre>' '</code></pre>'
expect(helper.event_note(input)).to eq(expected) expect(helper.event_note(input)).to eq(expected)
end end
......
...@@ -5,13 +5,12 @@ ...@@ -5,13 +5,12 @@
#= require ./mock_u2f_device #= require ./mock_u2f_device
describe 'U2FAuthenticate', -> describe 'U2FAuthenticate', ->
U2FUtil.enableTestMode()
fixture.load('u2f/authenticate') fixture.load('u2f/authenticate')
beforeEach -> beforeEach ->
@u2fDevice = new MockU2FDevice @u2fDevice = new MockU2FDevice
@container = $("#js-authenticate-u2f") @container = $("#js-authenticate-u2f")
@component = new U2FAuthenticate(@container, {}, "token") @component = new U2FAuthenticate(@container, {sign_requests: []}, "token")
@component.start() @component.start()
it 'allows authenticating via a U2F device', -> it 'allows authenticating via a U2F device', ->
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#= require ./mock_u2f_device #= require ./mock_u2f_device
describe 'U2FRegister', -> describe 'U2FRegister', ->
U2FUtil.enableTestMode()
fixture.load('u2f/register') fixture.load('u2f/register')
beforeEach -> beforeEach ->
......
...@@ -3,15 +3,35 @@ require 'spec_helper' ...@@ -3,15 +3,35 @@ require 'spec_helper'
describe Banzai::Filter::SyntaxHighlightFilter, lib: true do describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
include FilterSpecHelper include FilterSpecHelper
it 'highlights valid code blocks' do context "when no language is specified" do
result = filter('<pre><code>def fun end</code>') it "highlights as plaintext" do
expect(result.to_html).to eq("<pre class=\"code highlight js-syntax-highlight plaintext\"><code>def fun end</code></pre>\n") result = filter('<pre><code>def fun end</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>def fun end</code></pre>')
end
end end
it 'passes through invalid code blocks' do context "when a valid language is specified" do
allow_any_instance_of(described_class).to receive(:block_code).and_raise(StandardError) it "highlights as that language" do
result = filter('<pre><code class="ruby">def fun end</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>')
end
end
result = filter('<pre><code>This is a test</code></pre>') context "when an invalid language is specified" do
expect(result.to_html).to eq('<pre>This is a test</pre>') it "highlights as plaintext" do
result = filter('<pre><code class="gnuplot">This is a test</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>This is a test</code></pre>')
end
end
context "when Rouge formatting fails" do
before do
allow_any_instance_of(Rouge::Formatter).to receive(:format).and_raise(StandardError)
end
it "highlights as plaintext" do
result = filter('<pre><code class="ruby">This is a test</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight"><code>This is a test</code></pre>')
end
end end
end end
...@@ -54,12 +54,12 @@ describe Gitlab::BitbucketImport::Client, lib: true do ...@@ -54,12 +54,12 @@ describe Gitlab::BitbucketImport::Client, lib: true do
context 'project import' do context 'project import' do
it 'calls .from_project with no errors' do it 'calls .from_project with no errors' do
project = create(:empty_project) project = create(:empty_project)
project.import_url = "ssh://git@bitbucket.org/test/test.git"
project.create_or_update_import_data(credentials: project.create_or_update_import_data(credentials:
{ user: "git", { user: "git",
password: nil, password: nil,
bb_session: { bitbucket_access_token: "test", bb_session: { bitbucket_access_token: "test",
bitbucket_access_token_secret: "test" } }) bitbucket_access_token_secret: "test" } })
project.import_url = "ssh://git@bitbucket.org/test/test.git"
expect { described_class.from_project(project) }.not_to raise_error expect { described_class.from_project(project) }.not_to raise_error
end end
......
...@@ -13,6 +13,10 @@ describe Gitlab::Database::MigrationHelpers, lib: true do ...@@ -13,6 +13,10 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
context 'outside a transaction' do context 'outside a transaction' do
before do before do
expect(model).to receive(:transaction_open?).and_return(false) expect(model).to receive(:transaction_open?).and_return(false)
unless Gitlab::Database.postgresql?
allow_any_instance_of(Gitlab::Database::MigrationHelpers).to receive(:disable_statement_timeout)
end
end end
context 'using PostgreSQL' do context 'using PostgreSQL' do
......
...@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do ...@@ -28,13 +28,13 @@ describe Gitlab::Diff::Highlight, lib: true do
end end
it 'highlights and marks removed lines' do it 'highlights and marks removed lines' do
code = %Q{-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n} code = %Q{-<span id="LC9" class="line"> <span class="k">raise</span> <span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[4].text).to eq(code) expect(subject[4].text).to eq(code)
end end
it 'highlights and marks added lines' do it 'highlights and marks added lines' do
code = %Q{+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n} code = %Q{+<span id="LC9" class="line"> <span class="k">raise</span> <span class="no"><span class='idiff left'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff right'> </span><span class="s2">"System commands must be given as an array of strings"</span></span>\n}
expect(subject[5].text).to eq(code) expect(subject[5].text).to eq(code)
end end
......
require 'spec_helper'
describe Gitlab::ImportExport, services: true do
describe 'export filename' do
let(:project) { create(:project, :public, path: 'project-path') }
it 'contains the project path' do
expect(described_class.export_filename(project: project)).to include(project.path)
end
it 'contains the namespace path' do
expect(described_class.export_filename(project: project)).to include(project.namespace.path)
end
it 'does not go over a certain length' do
project.path = 'a' * 100
expect(described_class.export_filename(project: project).length).to be < 70
end
end
end
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
"deleted_at": null, "deleted_at": null,
"due_date": null, "due_date": null,
"moved_to_id": null, "moved_to_id": null,
"test_ee_field": "test",
"notes": [ "notes": [
{ {
"id": 351, "id": 351,
......
...@@ -30,6 +30,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do ...@@ -30,6 +30,14 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty
end end
it 'preserves updated_at on issues' do
restored_project_json
issue = Issue.where(description: 'Aliquam enim illo et possimus.').first
expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC')
end
context 'event at forth level of the tree' do context 'event at forth level of the tree' do
let(:event) { Event.where(title: 'test levels').first } let(:event) { Event.where(title: 'test levels').first }
......
...@@ -5,9 +5,12 @@ describe Ci::Pipeline, models: true do ...@@ -5,9 +5,12 @@ describe Ci::Pipeline, models: true do
let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project } let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:statuses) } it { is_expected.to have_many(:statuses) }
it { is_expected.to have_many(:trigger_requests) } it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:builds) } it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :sha } it { is_expected.to validate_presence_of :sha }
it { is_expected.to validate_presence_of :status } it { is_expected.to validate_presence_of :status }
......
...@@ -177,10 +177,10 @@ describe CommitStatus, models: true do ...@@ -177,10 +177,10 @@ describe CommitStatus, models: true do
describe '#stages' do describe '#stages' do
before do before do
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'success' create :commit_status, pipeline: pipeline, stage: 'build', name: 'linux', stage_idx: 0, status: 'success'
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'build', stage_idx: 0, status: 'failed' create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'failed'
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'deploy', stage_idx: 2, status: 'running' create :commit_status, pipeline: pipeline, stage: 'deploy', name: 'staging', stage_idx: 2, status: 'running'
FactoryGirl.create :commit_status, pipeline: pipeline, stage: 'test', stage_idx: 1, status: 'success' create :commit_status, pipeline: pipeline, stage: 'test', name: 'rspec', stage_idx: 1, status: 'success'
end end
context 'stages list' do context 'stages list' do
...@@ -192,7 +192,7 @@ describe CommitStatus, models: true do ...@@ -192,7 +192,7 @@ describe CommitStatus, models: true do
end end
context 'stages with statuses' do context 'stages with statuses' do
subject { CommitStatus.where(pipeline: pipeline).stages_status } subject { CommitStatus.where(pipeline: pipeline).latest.stages_status }
it 'return list of stages with statuses' do it 'return list of stages with statuses' do
is_expected.to eq({ is_expected.to eq({
...@@ -201,6 +201,20 @@ describe CommitStatus, models: true do ...@@ -201,6 +201,20 @@ describe CommitStatus, models: true do
'deploy' => 'running' 'deploy' => 'running'
}) })
end end
context 'when build is retried' do
before do
create :commit_status, pipeline: pipeline, stage: 'build', name: 'mac', stage_idx: 0, status: 'success'
end
it 'ignores a previous state' do
is_expected.to eq({
'build' => 'success',
'test' => 'success',
'deploy' => 'running'
})
end
end
end end
end end
......
...@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do ...@@ -16,10 +16,10 @@ describe LegacyDiffNote, models: true do
end end
describe '#active?' do describe '#active?' do
it 'is always true when the note has no associated diff' do it 'is always true when the note has no associated diff line' do
note = build(:legacy_diff_note_on_merge_request) note = build(:legacy_diff_note_on_merge_request)
expect(note).to receive(:diff).and_return(nil) expect(note).to receive(:diff_line).and_return(nil)
expect(note).to be_active expect(note).to be_active
end end
...@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do ...@@ -27,7 +27,7 @@ describe LegacyDiffNote, models: true do
it 'is never true when the note has no noteable associated' do it 'is never true when the note has no noteable associated' do
note = build(:legacy_diff_note_on_merge_request) note = build(:legacy_diff_note_on_merge_request)
expect(note).to receive(:diff).and_return(double) expect(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:noteable).and_return(nil) expect(note).to receive(:noteable).and_return(nil)
expect(note).not_to be_active expect(note).not_to be_active
...@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do ...@@ -47,7 +47,7 @@ describe LegacyDiffNote, models: true do
merge = build_stubbed(:merge_request, :simple) merge = build_stubbed(:merge_request, :simple)
note = build(:legacy_diff_note_on_merge_request, noteable: merge) note = build(:legacy_diff_note_on_merge_request, noteable: merge)
allow(note).to receive(:diff).and_return(double) allow(note).to receive(:diff_line).and_return(double)
expect(note).to receive(:find_noteable_diff).and_return(nil) expect(note).to receive(:find_noteable_diff).and_return(nil)
expect(note).not_to be_active expect(note).not_to be_active
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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