Commit 0ca27574 authored by James Lopez's avatar James Lopez

fix merge conflicts

parents 8076d38a 2c3f3cb3
...@@ -134,6 +134,11 @@ spinach 9 10: *spinach-knapsack ...@@ -134,6 +134,11 @@ spinach 9 10: *spinach-knapsack
image: "ruby:2.3" image: "ruby:2.3"
only: only:
- master - master
cache:
key: "ruby-23"
paths:
- vendor/apt
- vendor/ruby
.rspec-knapsack-ruby23: &rspec-knapsack-ruby23 .rspec-knapsack-ruby23: &rspec-knapsack-ruby23
<<: *rspec-knapsack <<: *rspec-knapsack
......
Please view this file on the master branch, on stable branches it's out of date. 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)
- Replace Haml with Hamlit to make view rendering faster. !3666
- Wrap code blocks on Activies and Todos page. !4783 (winniehell) - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Add Sidekiq queue duration to transaction metrics.
- Make images fit to the size of the viewport !4810
- 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
- Fix pagination when sorting by columns with lots of ties (like priority)
- Exclude email check from the standard health check
- Fix changing issue state columns in milestone view
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Check for conflicts with existing Project's wiki path when creating a new project.
- Add API endpoint for a group issues !4520 (mahcsig)
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
v 8.9.3 (unreleased)
- Fix encrypted data backwards compatibility after upgrading attr_encrypted gem
v 8.9.2
- Fix visibility of snippets when searching.
- Fix an information disclosure when requesting access to a group containing private projects.
- Update omniauth-saml to 1.6.0 !4951
v 8.9.1
- Refactor labels documentation. !3347
- Eager load award emoji on notes. !4628
- Fix some CI wording in documentation. !4660
- Document `GIT_STRATEGY` and `GIT_DEPTH`. !4720
- Add documentation for the export & import features. !4732
- Add some docs for Docker Registry configuration. !4738
- Ensure we don't send the "access request declined" email to access requesters on project deletion. !4744
- Display group/project access requesters separately in the admin area. !4798
- Add documentation and examples for configuring cloud storage for registry images. !4812
- Clarifies documentation about artifact expiry. !4831
- Fix the Network graph links. !4832
- Fix MR-auto-close text added to description. !4836
- Add documentation for award emoji now that comments can be awarded with emojis. !4839
- Fix typo in export failure email. !4847
- Fix header vertical centering. !4170
- Fix subsequent SAML sign ins. !4718
- Set button label when picking an option from status dropdown. !4771
- Prevent invalid URLs from raising exceptions in WikiLink Filter. !4775
- Handle external issues in IssueReferenceFilter. !4789
- Support for rendering/redacting multiple documents. !4828
- Update Todos documentation and screenshots to include new functionality. !4840
- Hide nav arrows by default. !4843
- Added bottom padding to label color suggestion link. !4845
- Use jQuery objects in ref dropdown. !4850
- Fix GitLab project import issues related to notes and builds. !4855
- Restrict header logo to 36px so it doesn't overflow. !4861
- Fix unwanted label unassignment. !4863
- Fix mobile Safari bug where horizontal nav arrows would flicker on scroll. !4869
- Restore old behavior around diff notes to outdated discussions. !4870
- Fix merge requests project settings help link anchor. !4873
- Fix 404 when accessing pipelines as guest user on public projects. !4881
- Remove width restriction for logo on sign-in page. !4888
- Bump gitlab_git to 10.2.3 to fix false truncated warnings with ISO-8559 files. !4884
- Apply selected value as label. !4886
- Fix temp file being deleted after the request while importing a GitLab project. !4894
- Fix pagination when sorting by columns with lots of ties (like priority)
- Implement Subresource Integrity for CSS and JavaScript assets. This prevents malicious assets from loading in the case of a CDN compromise.
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Fix a wrong MR status when merge_when_build_succeeds & project.only_allow_merge_if_build_succeeds are true. !4912
- Add SMTP as default delivery method to match gitlab-org/omnibus-gitlab!826. !4915
- Remove duplicate 'New Page' button on edit wiki page
v 8.9.1 (unreleased) v 8.9.1 (unreleased)
- Set import_url validation to be more strict - Set import_url validation to be more strict
......
...@@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1' ...@@ -30,7 +30,7 @@ gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-google-oauth2', '~> 0.2.0'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.5.0' gem 'omniauth-saml', '~> 1.6.0'
gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
...@@ -76,7 +76,7 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' ...@@ -76,7 +76,7 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
gem "kaminari", "~> 0.17.0" gem "kaminari", "~> 0.17.0"
# HAML # HAML
gem "haml-rails", '~> 0.9.0' gem 'hamlit', '~> 2.5'
# Files attachments # Files attachments
gem "carrierwave", '~> 0.10.0' gem "carrierwave", '~> 0.10.0'
...@@ -234,7 +234,7 @@ gem 'net-ssh', '~> 3.0.1' ...@@ -234,7 +234,7 @@ gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0' gem 'base32', '~> 0.3.0'
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 0.15' gem 'sentry-raven', '~> 1.1.0'
gem 'premailer-rails', '~> 1.9.0' gem 'premailer-rails', '~> 1.9.0'
......
...@@ -277,7 +277,7 @@ GEM ...@@ -277,7 +277,7 @@ GEM
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab_emoji (0.3.1) gitlab_emoji (0.3.1)
gemojione (~> 2.2, >= 2.2.1) gemojione (~> 2.2, >= 2.2.1)
gitlab_git (10.2.0) gitlab_git (10.2.3)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
...@@ -320,14 +320,10 @@ GEM ...@@ -320,14 +320,10 @@ GEM
grape-entity (0.4.8) grape-entity (0.4.8)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
haml (4.0.7) hamlit (2.5.0)
temple (~> 0.7.6)
thor
tilt tilt
haml-rails (0.9.0)
actionpack (>= 4.0.1)
activesupport (>= 4.0.1)
haml (>= 4.0.6, < 5.0)
html2haml (>= 1.0.1)
railties (>= 4.0.1)
hashie (3.4.3) hashie (3.4.3)
health_check (1.5.1) health_check (1.5.1)
rails (>= 2.3.0) rails (>= 2.3.0)
...@@ -337,11 +333,6 @@ GEM ...@@ -337,11 +333,6 @@ GEM
html-pipeline (1.11.0) html-pipeline (1.11.0)
activesupport (>= 2) activesupport (>= 2)
nokogiri (~> 1.4) nokogiri (~> 1.4)
html2haml (2.0.0)
erubis (~> 2.7.0)
haml (~> 4.0.0)
nokogiri (~> 1.6.0)
ruby_parser (~> 3.5)
htmlentities (4.3.4) htmlentities (4.3.4)
http_parser.rb (0.5.3) http_parser.rb (0.5.3)
httparty (0.13.7) httparty (0.13.7)
...@@ -468,9 +459,9 @@ GEM ...@@ -468,9 +459,9 @@ GEM
omniauth-oauth2 (1.3.1) omniauth-oauth2 (1.3.1)
oauth2 (~> 1.0) oauth2 (~> 1.0)
omniauth (~> 1.2) omniauth (~> 1.2)
omniauth-saml (1.5.0) omniauth-saml (1.6.0)
omniauth (~> 1.3) omniauth (~> 1.3)
ruby-saml (~> 1.1, >= 1.1.1) ruby-saml (~> 1.3)
omniauth-shibboleth (1.2.1) omniauth-shibboleth (1.2.1)
omniauth (>= 1.0.0) omniauth (>= 1.0.0)
omniauth-twitter (1.2.1) omniauth-twitter (1.2.1)
...@@ -631,9 +622,8 @@ GEM ...@@ -631,9 +622,8 @@ GEM
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-progressbar (1.8.1) ruby-progressbar (1.8.1)
ruby-saml (1.1.2) ruby-saml (1.3.0)
nokogiri (>= 1.5.10) nokogiri (>= 1.5.10)
uuid (~> 2.3)
ruby_parser (3.8.2) ruby_parser (3.8.2)
sexp_processor (~> 4.1) sexp_processor (~> 4.1)
rubyntlm (0.5.2) rubyntlm (0.5.2)
...@@ -665,7 +655,7 @@ GEM ...@@ -665,7 +655,7 @@ GEM
activesupport (>= 3.1, < 4.3) activesupport (>= 3.1, < 4.3)
select2-rails (3.5.9.3) select2-rails (3.5.9.3)
thor (~> 0.14) thor (~> 0.14)
sentry-raven (0.15.6) sentry-raven (1.1.0)
faraday (>= 0.7.6) faraday (>= 0.7.6)
settingslogic (2.0.9) settingslogic (2.0.9)
sexp_processor (4.7.0) sexp_processor (4.7.0)
...@@ -733,6 +723,7 @@ GEM ...@@ -733,6 +723,7 @@ GEM
railties (>= 3.2.5, < 6) railties (>= 3.2.5, < 6)
teaspoon-jasmine (2.2.0) teaspoon-jasmine (2.2.0)
teaspoon (>= 1.0.0) teaspoon (>= 1.0.0)
temple (0.7.7)
term-ansicolor (1.3.2) term-ansicolor (1.3.2)
tins (~> 1.0) tins (~> 1.0)
test_after_commit (0.4.2) test_after_commit (0.4.2)
...@@ -882,7 +873,7 @@ DEPENDENCIES ...@@ -882,7 +873,7 @@ DEPENDENCIES
gon (~> 6.0.1) gon (~> 6.0.1)
grape (~> 0.13.0) grape (~> 0.13.0)
grape-entity (~> 0.4.2) grape-entity (~> 0.4.2)
haml-rails (~> 0.9.0) hamlit (~> 2.5)
health_check (~> 1.5.1) health_check (~> 1.5.1)
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
...@@ -920,7 +911,7 @@ DEPENDENCIES ...@@ -920,7 +911,7 @@ DEPENDENCIES
omniauth-gitlab (~> 1.0.0) omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.2.0) omniauth-google-oauth2 (~> 0.2.0)
omniauth-kerberos (~> 0.3.0) omniauth-kerberos (~> 0.3.0)
omniauth-saml (~> 1.5.0) omniauth-saml (~> 1.6.0)
omniauth-shibboleth (~> 1.2.0) omniauth-shibboleth (~> 1.2.0)
omniauth-twitter (~> 1.2.0) omniauth-twitter (~> 1.2.0)
omniauth_crowd (~> 2.2.0) omniauth_crowd (~> 2.2.0)
...@@ -960,7 +951,7 @@ DEPENDENCIES ...@@ -960,7 +951,7 @@ DEPENDENCIES
sdoc (~> 0.3.20) sdoc (~> 0.3.20)
seed-fu (~> 2.3.5) seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
sentry-raven (~> 0.15) sentry-raven (~> 1.1.0)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
sham_rack sham_rack
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
......
8.9.0-pre 8.10.0-pre
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
#= require_directory ./ci #= require_directory ./ci
#= require_directory ./commit #= require_directory ./commit
#= require_directory ./extensions #= require_directory ./extensions
#= require_directory ./lib #= require_directory ./lib/utils
#= require_directory ./u2f #= require_directory ./u2f
#= require_directory . #= require_directory .
#= require fuzzaldrin-plus #= require fuzzaldrin-plus
......
...@@ -341,7 +341,9 @@ class @AwardsHandler ...@@ -341,7 +341,9 @@ class @AwardsHandler
for emoji in frequentlyUsedEmojis for emoji in frequentlyUsedEmojis
$(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul)
$('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) $('.emoji-menu-content')
.prepend(ul)
.prepend($('<h5>').text('Frequently used'))
@frequentEmojiBlockRendered = true @frequentEmojiBlockRendered = true
...@@ -356,7 +358,7 @@ class @AwardsHandler ...@@ -356,7 +358,7 @@ class @AwardsHandler
if term if term
# Generate a search result block # Generate a search result block
h5 = $('<h5>').text('Search results').addClass('emoji-search') h5 = $('<h5>').text('Search results')
found_emojis = @searchEmojis(term).show() found_emojis = @searchEmojis(term).show()
ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis) ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis)
$('.emoji-menu-content ul, .emoji-menu-content h5').hide() $('.emoji-menu-content ul, .emoji-menu-content h5').hide()
......
...@@ -19,6 +19,7 @@ class @TemplateSelector ...@@ -19,6 +19,7 @@ class @TemplateSelector
data: @data, data: @data,
filterable: true, filterable: true,
selectable: true, selectable: true,
toggleLabel: @toggleLabel,
search: search:
fields: ['name'] fields: ['name']
clicked: @onClick clicked: @onClick
...@@ -31,6 +32,9 @@ class @TemplateSelector ...@@ -31,6 +32,9 @@ class @TemplateSelector
@onFilenameUpdate() @onFilenameUpdate()
) )
toggleLabel: (item) ->
item.name
onFilenameUpdate: -> onFilenameUpdate: ->
return unless @$input.length return unless @$input.length
......
...@@ -280,7 +280,7 @@ class GitLabDropdown ...@@ -280,7 +280,7 @@ class GitLabDropdown
html = @renderData(data) html = @renderData(data)
# Render the full menu # Render the full menu
full_html = @renderMenu(html.join("")) full_html = @renderMenu(html)
@appendMenu(full_html) @appendMenu(full_html)
...@@ -351,7 +351,8 @@ class GitLabDropdown ...@@ -351,7 +351,8 @@ class GitLabDropdown
if @options.renderMenu if @options.renderMenu
menu_html = @options.renderMenu(html) menu_html = @options.renderMenu(html)
else else
menu_html = "<ul>#{html}</ul>" menu_html = $('<ul />')
.append(html)
return menu_html return menu_html
...@@ -360,7 +361,9 @@ class GitLabDropdown ...@@ -360,7 +361,9 @@ class GitLabDropdown
selector = '.dropdown-content' selector = '.dropdown-content'
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one .dropdown-content" selector = ".dropdown-page-one .dropdown-content"
$(selector, @dropdown).html html $(selector, @dropdown)
.empty()
.append(html)
# Render the row # Render the row
renderItem: (data, group = false, index = false) -> renderItem: (data, group = false, index = false) ->
...@@ -459,7 +462,7 @@ class GitLabDropdown ...@@ -459,7 +462,7 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
@updateLabel() @updateLabel(selectedObject, el, @)
else else
selectedObject selectedObject
else if el.hasClass(INDETERMINATE_CLASS) else if el.hasClass(INDETERMINATE_CLASS)
...@@ -486,7 +489,7 @@ class GitLabDropdown ...@@ -486,7 +489,7 @@ class GitLabDropdown
# Toggle the dropdown label # Toggle the dropdown label
if @options.toggleLabel if @options.toggleLabel
@updateLabel(selectedObject, el) @updateLabel(selectedObject, el, @)
if value? if value?
if !field.length and fieldName if !field.length and fieldName
@addInput(fieldName, value) @addInput(fieldName, value)
...@@ -585,8 +588,8 @@ class GitLabDropdown ...@@ -585,8 +588,8 @@ class GitLabDropdown
# Scroll the dropdown content up # Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop) $dropdownContent.scrollTop(listItemTop - dropdownContentTop)
updateLabel: (selected = null, el = null) => updateLabel: (selected = null, el = null, instance = null) =>
$(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el) $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el, instance)
$.fn.glDropdown = (opts) -> $.fn.glDropdown = (opts) ->
return @.each -> return @.each ->
......
...@@ -4,5 +4,4 @@ ...@@ -4,5 +4,4 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file. # the compiled file.
# #
#= require Chart
#= require_tree . #= require_tree .
...@@ -68,12 +68,15 @@ issuable_created = false ...@@ -68,12 +68,15 @@ issuable_created = false
Turbolinks.visit(issuesUrl); Turbolinks.visit(issuesUrl);
initChecks: -> initChecks: ->
@issuableBulkActions = $('.bulk-update').data('bulkActions')
$('.check_all_issues').off('click').on('click', -> $('.check_all_issues').off('click').on('click', ->
$('.selected_issue').prop('checked', @checked) $('.selected_issue').prop('checked', @checked)
Issuable.checkChanged() Issuable.checkChanged()
) )
$('.selected_issue').off('change').on('change', Issuable.checkChanged) $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(@))
checkChanged: -> checkChanged: ->
checked_issues = $('.selected_issue:checked') checked_issues = $('.selected_issue:checked')
...@@ -88,3 +91,6 @@ issuable_created = false ...@@ -88,3 +91,6 @@ issuable_created = false
$('#update_issues_ids').val [] $('#update_issues_ids').val []
$('.issues_bulk_update').hide() $('.issues_bulk_update').hide()
$('.issues-other-filters').show() $('.issues-other-filters').show()
@issuableBulkActions.willUpdateLabels = false
return true
...@@ -99,7 +99,7 @@ class @Issue ...@@ -99,7 +99,7 @@ class @Issue
# If the user doesn't have the required permissions the container isn't # If the user doesn't have the required permissions the container isn't
# rendered at all. # rendered at all.
return unless $container return if $container.length is 0
$.getJSON($container.data('path')) $.getJSON($container.data('path'))
.error -> .error ->
......
...@@ -6,6 +6,13 @@ class @IssueStatusSelect ...@@ -6,6 +6,13 @@ class @IssueStatusSelect
$(el).glDropdown( $(el).glDropdown(
selectable: true selectable: true
fieldName: fieldName fieldName: fieldName
toggleLabel: (selected, el, instance) =>
label = 'Author'
$item = instance.dropdown.find('.is-active')
label = $item.text() if $item.length
label
clicked: (item, $el, e)->
e.preventDefault()
id: (obj, el) -> id: (obj, el) ->
$(el).data("id") $(el).data("id")
) )
...@@ -7,6 +7,11 @@ class @IssuableBulkActions ...@@ -7,6 +7,11 @@ class @IssuableBulkActions
@issues = @getElement('.issues-list .issue') @issues = @getElement('.issues-list .issue')
} = opts } = opts
# Save instance
@form.data 'bulkActions', @
@willUpdateLabels = false
@bindEvents() @bindEvents()
# Fixes bulk-assign not working when navigating through pages # Fixes bulk-assign not working when navigating through pages
...@@ -87,6 +92,7 @@ class @IssuableBulkActions ...@@ -87,6 +92,7 @@ class @IssuableBulkActions
add_label_ids : [] add_label_ids : []
remove_label_ids : [] remove_label_ids : []
if @willUpdateLabels
@getLabelsToApply().map (id) -> @getLabelsToApply().map (id) ->
formData.update.add_label_ids.push id formData.update.add_label_ids.push id
......
...@@ -319,6 +319,8 @@ class @LabelsSelect ...@@ -319,6 +319,8 @@ class @LabelsSelect
multiSelect: $dropdown.hasClass 'js-multiselect' multiSelect: $dropdown.hasClass 'js-multiselect'
clicked: (label) -> clicked: (label) ->
_this.enableBulkLabelDropdown()
if $dropdown.hasClass('js-filter-bulk-update') if $dropdown.hasClass('js-filter-bulk-update')
return return
...@@ -377,3 +379,8 @@ class @LabelsSelect ...@@ -377,3 +379,8 @@ class @LabelsSelect
label_ids.push $("#issue_#{issue_id}").data('labels') label_ids.push $("#issue_#{issue_id}").data('labels')
_.intersection.apply _, label_ids _.intersection.apply _, label_ids
enableBulkLabelDropdown: ->
if $('.selected_issue:checked').length
issuableBulkActions = $('.bulk-update').data('bulkActions')
issuableBulkActions.willUpdateLabels = true
...@@ -3,11 +3,10 @@ hideEndFade = ($scrollingTabs) -> ...@@ -3,11 +3,10 @@ hideEndFade = ($scrollingTabs) ->
$this = $(@) $this = $(@)
$this $this
.find('.fade-right') .siblings('.fade-right')
.toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth')) .toggleClass('scrolling', $this.width() < $this.prop('scrollWidth'))
$ -> $ ->
$('.fade-left').addClass('end-scroll')
hideEndFade($('.scrolling-tabs')) hideEndFade($('.scrolling-tabs'))
...@@ -21,5 +20,5 @@ $ -> ...@@ -21,5 +20,5 @@ $ ->
currentPosition = $this.scrollLeft() currentPosition = $this.scrollLeft()
maxPosition = $this.prop('scrollWidth') - $this.outerWidth() maxPosition = $this.prop('scrollWidth') - $this.outerWidth()
$this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0) $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0)
$this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition) $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1)
#= require raphael
#= require g.raphael
#= require g.bar
...@@ -4,17 +4,9 @@ class @Milestone ...@@ -4,17 +4,9 @@ class @Milestone
type: "PUT" type: "PUT"
url: issue_url url: issue_url
data: data data: data
success: (data) -> success: (_data) =>
if data.saved == true @successCallback(_data, li)
if data.assignee_avatar_url error: (data) ->
img_tag = $('<img/>')
img_tag.attr('src', data.assignee_avatar_url)
img_tag.addClass('avatar s16')
$(li).find('.assignee-icon').html(img_tag)
else
$(li).find('.assignee-icon').html('')
$(li).effect 'highlight'
else
new Flash("Issue update failed", 'alert') new Flash("Issue update failed", 'alert')
dataType: "json" dataType: "json"
...@@ -25,8 +17,9 @@ class @Milestone ...@@ -25,8 +17,9 @@ class @Milestone
type: "PUT" type: "PUT"
url: sort_issues_url url: sort_issues_url
data: data data: data
success: (data) -> success: (_data) =>
if data.saved != true @successCallback(_data)
error: ->
new Flash("Issues update failed", 'alert') new Flash("Issues update failed", 'alert')
dataType: "json" dataType: "json"
...@@ -37,9 +30,10 @@ class @Milestone ...@@ -37,9 +30,10 @@ class @Milestone
type: "PUT" type: "PUT"
url: sort_mr_url url: sort_mr_url
data: data data: data
success: (data) -> success: (_data) =>
if data.saved != true @successCallback(_data)
new Flash("MR update failed", 'alert') error: (data) ->
new Flash("Issue update failed", 'alert')
dataType: "json" dataType: "json"
@updateMergeRequest: (li, merge_request_url, data) -> @updateMergeRequest: (li, merge_request_url, data) ->
...@@ -47,19 +41,22 @@ class @Milestone ...@@ -47,19 +41,22 @@ class @Milestone
type: "PUT" type: "PUT"
url: merge_request_url url: merge_request_url
data: data data: data
success: (data) -> success: (_data) =>
if data.saved == true @successCallback(_data, li)
if data.assignee_avatar_url error: (data) ->
new Flash("Issue update failed", 'alert')
dataType: "json"
@successCallback: (data, element) =>
if data.assignee
img_tag = $('<img/>') img_tag = $('<img/>')
img_tag.attr('src', data.assignee_avatar_url) img_tag.attr('src', data.assignee.avatar_url)
img_tag.addClass('avatar s16') img_tag.addClass('avatar s16')
$(li).find('.assignee-icon').html(img_tag) $(element).find('.assignee-icon').html(img_tag)
else
$(li).find('.assignee-icon').html('')
$(li).effect 'highlight'
else else
new Flash("Issue update failed", 'alert') $(element).find('.assignee-icon').html('')
dataType: "json"
$(element).effect 'highlight'
constructor: -> constructor: ->
oldMouseStart = $.ui.sortable.prototype._mouseStart oldMouseStart = $.ui.sortable.prototype._mouseStart
...@@ -81,6 +78,8 @@ class @Milestone ...@@ -81,6 +78,8 @@ class @Milestone
stop: (event, ui) -> stop: (event, ui) ->
$(".issues-sortable-list").css "min-height", "0px" $(".issues-sortable-list").css "min-height", "0px"
update: (event, ui) -> update: (event, ui) ->
# Prevents sorting from container which element has been removed.
if $(this).find(ui.item).length > 0
data = $(this).sortable("serialize") data = $(this).sortable("serialize")
Milestone.sortIssues(data) Milestone.sortIssues(data)
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the # It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file. # the compiled file.
# #
#= require raphael
#= require g.raphael
#= require g.bar
#= require_tree . #= require_tree .
$ -> $ ->
......
...@@ -70,17 +70,20 @@ class @Project ...@@ -70,17 +70,20 @@ class @Project
fieldName: 'ref' fieldName: 'ref'
renderRow: (ref) -> renderRow: (ref) ->
if ref.header? if ref.header?
"<li class='dropdown-header'>#{ref.header}</li>" $('<li />')
.addClass('dropdown-header')
.text(ref.header)
else else
isActiveClass = if ref is selected then 'is-active' else '' link = $('<a />')
.attr('href', '#')
"<li> .addClass(if ref is selected then 'is-active' else '')
<a href='#' data-ref='#{escape(ref)}' class='#{isActiveClass}'> .text(ref)
#{ref} .attr('data-ref', escape(ref))
</a>
</li>" $('<li />')
.append(link)
id: (obj, $el) -> id: (obj, $el) ->
$el.data('ref') $el.attr('data-ref')
toggleLabel: (obj, $el) -> toggleLabel: (obj, $el) ->
$el.text().trim() $el.text().trim()
clicked: (e) -> clicked: (e) ->
......
# This is a manifest file that'll be compiled into including all the files listed below.
# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
# be included in the compiled file accessible from http://example.com/assets/application.js
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
# #
#= require d3
#= require_tree . #= require_tree .
...@@ -26,7 +26,6 @@ header { ...@@ -26,7 +26,6 @@ header {
text-align: center; text-align: center;
#tanuki-logo, img { #tanuki-logo, img {
width: 36px;
height: 36px; height: 36px;
} }
} }
...@@ -132,6 +131,10 @@ header { ...@@ -132,6 +131,10 @@ header {
transition-duration: .3s; transition-duration: .3s;
z-index: 999; z-index: 999;
svg, img {
height: 36px;
}
&:hover { &:hover {
cursor: pointer; cursor: pointer;
} }
......
@mixin fade($gradient-direction, $rgba, $gradient-color) { @mixin fade($gradient-direction, $rgba, $gradient-color) {
visibility: visible; visibility: hidden;
opacity: 1; opacity: 0;
z-index: 2; z-index: 2;
position: absolute; position: absolute;
bottom: 12px; bottom: 12px;
...@@ -13,9 +13,9 @@ ...@@ -13,9 +13,9 @@
background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%); background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%); background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
&.end-scroll { &.scrolling {
visibility: hidden; visibility: visible;
opacity: 0; opacity: 1;
transition-duration: .3s; transition-duration: .3s;
} }
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
...@@ -272,7 +273,7 @@ ...@@ -272,7 +273,7 @@
float: right; float: right;
padding: 7px 0 0; padding: 7px 0 0;
@media (max-width: $screen-xs-max) { @media (max-width: $screen-sm-max) {
display: none; display: none;
} }
...@@ -303,41 +304,9 @@ ...@@ -303,41 +304,9 @@
} }
.nav-links { .nav-links {
@include scrolling-links();
border-bottom: none; border-bottom: none;
height: 51px; height: 51px;
svg {
position: relative;
top: 2px;
margin-right: 2px;
height: 15px;
width: auto;
path,
polygon {
fill: $layout-link-gray;
}
}
.fade-right {
@include fade(left, rgba(250, 250, 250, 0.4), $background-color);
right: 0;
.fa {
right: -7px;
}
}
.fade-left {
@include fade(right, rgba(250, 250, 250, 0.4), $background-color);
left: 0;
.fa {
left: -7px;
}
}
li { li {
a { a {
...@@ -373,18 +342,6 @@ ...@@ -373,18 +342,6 @@
} }
} }
} }
.nav-control {
.fade-right {
@media (min-width: $screen-xs-max) {
right: 68px;
}
@media (max-width: $screen-xs-min) {
right: 0;
}
}
}
} }
.scrolling-tabs-container { .scrolling-tabs-container {
...@@ -392,15 +349,42 @@ ...@@ -392,15 +349,42 @@
.nav-links { .nav-links {
@include scrolling-links(); @include scrolling-links();
}
.fade-right { .fade-right {
@include fade(left, rgba(255, 255, 255, 0.4), $background-color); @include fade(left, rgba(255, 255, 255, 0.4), $background-color);
right: 0; right: -5px;
.fa {
right: -7px;
}
} }
.fade-left { .fade-left {
@include fade(right, rgba(255, 255, 255, 0.4), $background-color); @include fade(right, rgba(255, 255, 255, 0.4), $background-color);
left: -5px;
.fa {
left: -7px;
}
}
&.sub-nav-scroll {
.fade-right {
right: 0;
.fa {
right: -23px;
}
}
.fade-left {
left: 0; left: 0;
.fa {
left: 10px;
}
} }
} }
} }
...@@ -413,21 +397,19 @@ ...@@ -413,21 +397,19 @@
.fade-right { .fade-right {
@include fade(left, rgba(255, 255, 255, 0.4), $white-light); @include fade(left, rgba(255, 255, 255, 0.4), $white-light);
right: 0; right: -5px;
.fa {
right: -7px;
}
} }
.fade-left { .fade-left {
@include fade(right, rgba(255, 255, 255, 0.4), $white-light); @include fade(right, rgba(255, 255, 255, 0.4), $white-light);
left: 0; left: -5px;
}
&.event-filter {
.fade-right {
visibility: hidden;
@media (max-width: $screen-xs-max) { .fa {
visibility: visible; left: -7px;
}
} }
} }
} }
......
...@@ -8,8 +8,9 @@ ...@@ -8,8 +8,9 @@
.emoji-menu { .emoji-menu {
position: absolute; position: absolute;
margin-top: 3px; margin-top: 3px;
z-index: 1000; padding: $gl-padding;
min-width: 160px; z-index: 9;
width: 300px;
font-size: 14px; font-size: 14px;
background-color: $award-emoji-menu-bg; background-color: $award-emoji-menu-bg;
border: 1px solid $award-emoji-menu-border; border: 1px solid $award-emoji-menu-border;
...@@ -33,18 +34,16 @@ ...@@ -33,18 +34,16 @@
} }
.emoji-menu-content { .emoji-menu-content {
padding: $gl-padding;
width: 300px;
height: 300px; height: 300px;
overflow-y: scroll; overflow-y: scroll;
}
}
input.emoji-search { .emoji-search {
background-image: url("data:image/png;base64,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"); background-image: url("data:image/png;base64,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");
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right 5px center; background-position: right 5px center;
background-size: 16px; background-size: 16px;
}
}
} }
.emoji-menu-list { .emoji-menu-list {
......
...@@ -63,5 +63,6 @@ ...@@ -63,5 +63,6 @@
border: 1px solid $table-border-gray; border: 1px solid $table-border-gray;
padding: 5px; padding: 5px;
margin: 5px; margin: 5px;
max-height: calc(100vh - 100px);
} }
} }
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
border: 1px solid $table-border-gray; border: 1px solid $table-border-gray;
padding: 5px; padding: 5px;
margin: 5px; margin: 5px;
max-height: calc(100vh - 100px);
} }
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
height: 30px; height: 30px;
display: inline-block; display: inline-block;
margin-right: 10px; margin-right: 10px;
margin-bottom: 10px;
} }
&.suggest-colors-dropdown { &.suggest-colors-dropdown {
......
...@@ -113,6 +113,7 @@ ul.notes { ...@@ -113,6 +113,7 @@ ul.notes {
border: 1px solid $table-border-gray; border: 1px solid $table-border-gray;
padding: 5px; padding: 5px;
margin: 5px 0; margin: 5px 0;
max-height: calc(100vh - 100px);
} }
} }
} }
......
class Dashboard::GroupsController < Dashboard::ApplicationController class Dashboard::GroupsController < Dashboard::ApplicationController
def index def index
@group_members = current_user.group_members.page(params[:page]) @group_members = current_user.group_members.includes(:source).page(params[:page])
end end
end end
...@@ -12,9 +12,13 @@ class Import::GitlabProjectsController < Import::BaseController ...@@ -12,9 +12,13 @@ class Import::GitlabProjectsController < Import::BaseController
return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." }) return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
end end
imported_file = project_params[:file].path + "-import"
FileUtils.copy_entry(project_params[:file].path, imported_file)
@project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id], @project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id],
current_user, current_user,
File.expand_path(project_params[:file].path), File.expand_path(imported_file),
project_params[:path]).execute project_params[:path]).execute
if @project.saved? if @project.saved?
......
...@@ -16,6 +16,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -16,6 +16,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action :from_merge_request, only: [:edit, :update] before_action :from_merge_request, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update] before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff] before_action :editor_variables, except: [:show, :preview, :diff]
before_action :validate_diff_params, only: :diff
def new def new
commit unless @repository.empty? commit unless @repository.empty?
...@@ -146,4 +147,10 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -146,4 +147,10 @@ class Projects::BlobController < Projects::ApplicationController
file_content_encoding: params[:encoding] file_content_encoding: params[:encoding]
} }
end end
def validate_diff_params
if [:since, :to, :offset].any? { |key| params[key].blank? }
render nothing: true
end
end
end end
...@@ -18,9 +18,16 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -18,9 +18,16 @@ class Projects::CommitController < Projects::ApplicationController
apply_diff_view_cookie! apply_diff_view_cookie!
@grouped_diff_notes = commit.notes.grouped_diff_notes @grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit) @note = @project.build_commit_note(commit)
@notes = commit.notes.non_diff_notes.fresh
@noteable = @commit @noteable = @commit
@comments_target = { @comments_target = {
noteable_type: 'Commit', noteable_type: 'Commit',
......
...@@ -62,8 +62,12 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -62,8 +62,12 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def show def show
raw_notes = @issue.notes_with_associations.fresh
@notes = Banzai::NoteRenderer.
render(raw_notes, @project, current_user, @path, @project_wiki, @ref)
@note = @project.notes.new(noteable: @issue) @note = @project.notes.new(noteable: @issue)
@notes = @issue.notes.with_associations.fresh
@noteable = @issue @noteable = @issue
respond_to do |format| respond_to do |format|
...@@ -111,6 +115,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -111,6 +115,7 @@ class Projects::IssuesController < Projects::ApplicationController
render :edit render :edit
end end
end end
format.json do format.json do
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
......
...@@ -85,6 +85,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -85,6 +85,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes @grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
respond_to do |format| respond_to do |format|
format.html format.html
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } } format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
...@@ -190,7 +199,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -190,7 +199,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge def merge
return access_denied! unless @merge_request.can_be_merged_by?(current_user) return access_denied! unless @merge_request.can_be_merged_by?(current_user)
unless @merge_request.mergeable? # Disable the CI check if merge_when_build_succeeds is enabled since we have
# to wait until CI completes to know
unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?)
@status = :failed @status = :failed
return return
end end
...@@ -205,7 +216,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -205,7 +216,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present? if params[:merge_when_build_succeeds].present?
if @merge_request.pipeline && @merge_request.pipeline.active? unless @merge_request.pipeline
@status = :failed
return
end
if @merge_request.pipeline.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request) .execute(@merge_request)
@status = :merge_when_build_succeeds @status = :merge_when_build_succeeds
...@@ -320,8 +336,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -320,8 +336,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_show_vars def define_show_vars
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request) @note = @project.notes.new(noteable: @merge_request)
@notes = @merge_request.mr_and_commit_notes.inc_author.fresh
@discussions = @notes.discussions @discussions = @merge_request.mr_and_commit_notes.
inc_author_project_award_emoji.
fresh.
discussions
@notes = Banzai::NoteRenderer.render(
@discussions.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
@noteable = @merge_request @noteable = @merge_request
# Get commits from repository # Get commits from repository
...@@ -368,4 +397,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -368,4 +397,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def ensure_ref_fetched def ensure_ref_fetched
@merge_request.ensure_ref_fetched @merge_request.ensure_ref_fetched
end end
def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active?
end
end end
...@@ -24,6 +24,10 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -24,6 +24,10 @@ class Projects::NotesController < Projects::ApplicationController
def create def create
@note = Notes::CreateService.new(project, current_user, note_params).execute @note = Notes::CreateService.new(project, current_user, note_params).execute
if @note.is_a?(Note)
Banzai::NoteRenderer.render([@note], @project, current_user)
end
respond_to do |format| respond_to do |format|
format.json { render json: note_json(@note) } format.json { render json: note_json(@note) }
format.html { redirect_back_or_default } format.html { redirect_back_or_default }
...@@ -33,6 +37,10 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -33,6 +37,10 @@ class Projects::NotesController < Projects::ApplicationController
def update def update
@note = Notes::UpdateService.new(project, current_user, note_params).execute(note) @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
if @note.is_a?(Note)
Banzai::NoteRenderer.render([@note], @project, current_user)
end
respond_to do |format| respond_to do |format|
format.json { render json: note_json(@note) } format.json { render json: note_json(@note) }
format.html { redirect_back_or_default } format.html { redirect_back_or_default }
...@@ -118,6 +126,8 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -118,6 +126,8 @@ class Projects::NotesController < Projects::ApplicationController
name: note.name name: note.name
} }
elsif note.valid? elsif note.valid?
Banzai::NoteRenderer.render([note], @project, current_user)
{ {
valid: true, valid: true,
id: note.id, id: note.id,
......
...@@ -197,7 +197,7 @@ module ApplicationHelper ...@@ -197,7 +197,7 @@ module ApplicationHelper
def render_markup(file_name, file_content) def render_markup(file_name, file_content)
if gitlab_markdown?(file_name) if gitlab_markdown?(file_name)
Haml::Helpers.preserve(markdown(file_content)) Hamlit::RailsHelpers.preserve(markdown(file_content))
elsif asciidoc?(file_name) elsif asciidoc?(file_name)
asciidoc(file_content) asciidoc(file_content)
elsif plain?(file_name) elsif plain?(file_name)
......
module BlobHelper module BlobHelper
def highlighter(blob_name, blob_content, nowrap: false) def highlighter(blob_name, blob_content, repository: nil, nowrap: false)
Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap) Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository)
end end
def highlight(blob_name, blob_content, nowrap: false, plain: false) def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false)
Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain) Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository)
end end
def no_highlight_files def no_highlight_files
......
module JavascriptHelper module JavascriptHelper
def page_specific_javascripts(js = nil) def page_specific_javascript_tag(js)
@page_specific_javascripts = js unless js.nil? javascript_include_tag asset_path(js), { "data-turbolinks-track" => true }
@page_specific_javascripts
end end
end end
...@@ -196,7 +196,8 @@ class Ability ...@@ -196,7 +196,8 @@ class Ability
@public_project_rules ||= project_guest_rules + [ @public_project_rules ||= project_guest_rules + [
:download_code, :download_code,
:fork_project, :fork_project,
:read_commit_status :read_commit_status,
:read_pipeline
] ]
end end
......
...@@ -163,13 +163,26 @@ module Ci ...@@ -163,13 +163,26 @@ module Ci
end end
def skip_ci? def skip_ci?
git_commit_message =~ /(\[ci skip\])/ if git_commit_message git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message
end end
def environments def environments
builds.where.not(environment: nil).success.pluck(:environment).uniq builds.where.not(environment: nil).success.pluck(:environment).uniq
end end
# Manually set the notes for a Ci::Pipeline
# There is no ActiveRecord relation between Ci::Pipeline and notes
# as they are related to a commit sha. This method helps importing
# them using the +Gitlab::ImportExport::RelationFactory+ class.
def notes=(notes)
notes.each do |note|
note[:id] = nil
note[:commit_id] = sha
note[:noteable_id] = self['id']
note.save!
end
end
def notes def notes
Note.for_commit_id(sha) Note.for_commit_id(sha)
end end
......
...@@ -13,6 +13,7 @@ module Ci ...@@ -13,6 +13,7 @@ module Ci
attr_encrypted :value, attr_encrypted :value,
mode: :per_attribute_iv_and_salt, mode: :per_attribute_iv_and_salt,
insecure_mode: true,
key: Gitlab::Application.secrets.db_key_base, key: Gitlab::Application.secrets.db_key_base,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
end end
......
...@@ -2,10 +2,11 @@ module Awardable ...@@ -2,10 +2,11 @@ module Awardable
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
has_many :award_emoji, as: :awardable, dependent: :destroy has_many :award_emoji, -> { includes(:user) }, as: :awardable, dependent: :destroy
if self < Participable if self < Participable
participant :award_emoji_with_associations # By default we always load award_emoji user association
participant :award_emoji
end end
end end
...@@ -34,12 +35,9 @@ module Awardable ...@@ -34,12 +35,9 @@ module Awardable
end end
end end
def award_emoji_with_associations
award_emoji.includes(:user)
end
def grouped_awards(with_thumbs: true) def grouped_awards(with_thumbs: true)
awards = award_emoji_with_associations.group_by(&:name) # By default we always load award_emoji user association
awards = award_emoji.group_by(&:name)
if with_thumbs if with_thumbs
awards[AwardEmoji::UPVOTE_NAME] ||= [] awards[AwardEmoji::UPVOTE_NAME] ||= []
......
...@@ -19,9 +19,14 @@ module Issuable ...@@ -19,9 +19,14 @@ module Issuable
belongs_to :milestone belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy do has_many :notes, as: :noteable, dependent: :destroy do
def authors_loaded? def authors_loaded?
# We check first if we're loaded to not load unnecesarily. # We check first if we're loaded to not load unnecessarily.
loaded? && to_a.all? { |note| note.association(:author).loaded? } loaded? && to_a.all? { |note| note.association(:author).loaded? }
end end
def award_emojis_loaded?
# We check first if we're loaded to not load unnecessarily.
loaded? && to_a.all? { |note| note.association(:award_emoji).loaded? }
end
end end
has_many :label_links, as: :target, dependent: :destroy has_many :label_links, as: :target, dependent: :destroy
has_many :labels, through: :label_links has_many :labels, through: :label_links
...@@ -49,7 +54,7 @@ module Issuable ...@@ -49,7 +54,7 @@ module Issuable
scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) } scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
scope :join_project, -> { joins(:project) } scope :join_project, -> { joins(:project) }
scope :inc_notes_with_associations, -> { includes(notes: :author) } scope :inc_notes_with_associations, -> { includes(notes: [ :project, :author, :award_emoji ]) }
scope :references_project, -> { references(:project) } scope :references_project, -> { references(:project) }
scope :non_archived, -> { join_project.where(projects: { archived: false }) } scope :non_archived, -> { join_project.where(projects: { archived: false }) }
...@@ -112,7 +117,7 @@ module Issuable ...@@ -112,7 +117,7 @@ module Issuable
end end
def sort(method, excluded_labels: []) def sort(method, excluded_labels: [])
case method.to_s sorted = case method.to_s
when 'milestone_due_asc' then order_milestone_due_asc when 'milestone_due_asc' then order_milestone_due_asc
when 'milestone_due_desc' then order_milestone_due_desc when 'milestone_due_desc' then order_milestone_due_desc
when 'downvotes_desc' then order_downvotes_desc when 'downvotes_desc' then order_downvotes_desc
...@@ -121,6 +126,9 @@ module Issuable ...@@ -121,6 +126,9 @@ module Issuable
else else
order_by(method) order_by(method)
end end
# Break ties with the ID column for pagination
sorted.order(id: :desc)
end end
def order_labels_priority(excluded_labels: []) def order_labels_priority(excluded_labels: [])
...@@ -257,7 +265,14 @@ module Issuable ...@@ -257,7 +265,14 @@ module Issuable
# already have their authors loaded (possibly because the scope # already have their authors loaded (possibly because the scope
# `inc_notes_with_associations` was used) and skip the inclusion if that's # `inc_notes_with_associations` was used) and skip the inclusion if that's
# the case. # the case.
notes.authors_loaded? ? notes : notes.includes(:author) includes = []
includes << :author unless notes.authors_loaded?
includes << :award_emoji unless notes.award_emojis_loaded?
if includes.any?
notes.includes(includes)
else
notes
end
end end
def updated_tasks def updated_tasks
......
...@@ -11,7 +11,7 @@ class Group < Namespace ...@@ -11,7 +11,7 @@ class Group < Namespace
has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members
has_many :owners, has_many :owners,
-> { where(members: { access_level: Gitlab::Access::OWNER }) }, -> { where(members: { requested_at: nil, access_level: Gitlab::Access::OWNER }) },
through: :group_members, through: :group_members,
source: :user source: :user
......
...@@ -20,7 +20,7 @@ class LegacyDiffNote < Note ...@@ -20,7 +20,7 @@ class LegacyDiffNote < Note
end end
def discussion_id def discussion_id
@discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code, active?) @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code)
end end
def diff_file_hash def diff_file_hash
......
...@@ -264,19 +264,19 @@ class MergeRequest < ActiveRecord::Base ...@@ -264,19 +264,19 @@ class MergeRequest < ActiveRecord::Base
self.title.sub(WIP_REGEX, "") self.title.sub(WIP_REGEX, "")
end end
def mergeable? def mergeable?(skip_ci_check: false)
return false unless mergeable_state? return false unless mergeable_state?(skip_ci_check: skip_ci_check)
check_if_can_be_merged check_if_can_be_merged
can_be_merged? can_be_merged?
end end
def mergeable_state? def mergeable_state?(skip_ci_check: false)
return false unless open? return false unless open?
return false if work_in_progress? return false if work_in_progress?
return false if broken? return false if broken?
return false unless mergeable_ci_state? return false unless skip_ci_check || mergeable_ci_state?
true true
end end
......
...@@ -6,6 +6,10 @@ class Note < ActiveRecord::Base ...@@ -6,6 +6,10 @@ class Note < ActiveRecord::Base
include Awardable include Awardable
include Importable include Importable
# Attribute containing rendered and redacted Markdown as generated by
# Banzai::ObjectRenderer.
attr_accessor :note_html
default_value_for :system, false default_value_for :system, false
attr_mentionable :note, pipeline: :note attr_mentionable :note, pipeline: :note
...@@ -49,11 +53,13 @@ class Note < ActiveRecord::Base ...@@ -49,11 +53,13 @@ class Note < ActiveRecord::Base
scope :fresh, ->{ order(created_at: :asc, id: :asc) } scope :fresh, ->{ order(created_at: :asc, id: :asc) }
scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) } scope :inc_author, ->{ includes(:author) }
scope :inc_author_project_award_emoji, ->{ includes(:project, :author, :award_emoji) }
scope :legacy_diff_notes, ->{ where(type: 'LegacyDiffNote') } scope :legacy_diff_notes, ->{ where(type: 'LegacyDiffNote') }
scope :non_diff_notes, ->{ where(type: ['Note', nil]) } scope :non_diff_notes, ->{ where(type: ['Note', nil]) }
scope :with_associations, -> do scope :with_associations, -> do
# FYI noteable cannot be loaded for LegacyDiffNote for commits
includes(:author, :noteable, :updated_by, includes(:author, :noteable, :updated_by,
project: [:project_members, { group: [:group_members] }]) project: [:project_members, { group: [:group_members] }])
end end
......
...@@ -161,6 +161,7 @@ class Project < ActiveRecord::Base ...@@ -161,6 +161,7 @@ class Project < ActiveRecord::Base
validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
validate :visibility_level_allowed_by_group validate :visibility_level_allowed_by_group
validate :visibility_level_allowed_as_fork validate :visibility_level_allowed_as_fork
validate :check_wiki_path_conflict
add_authentication_token_field :runners_token add_authentication_token_field :runners_token
before_save :ensure_runners_token before_save :ensure_runners_token
...@@ -539,6 +540,16 @@ class Project < ActiveRecord::Base ...@@ -539,6 +540,16 @@ class Project < ActiveRecord::Base
self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.") self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.")
end end
def check_wiki_path_conflict
return if path.blank?
path_to_check = path.ends_with?('.wiki') ? path.chomp('.wiki') : "#{path}.wiki"
if Project.where(namespace_id: namespace_id, path: path_to_check).exists?
errors.add(:name, 'has already been taken')
end
end
def to_param def to_param
path path
end end
......
...@@ -7,6 +7,7 @@ class ProjectImportData < ActiveRecord::Base ...@@ -7,6 +7,7 @@ class ProjectImportData < ActiveRecord::Base
marshal: true, marshal: true,
encode: true, encode: true,
mode: :per_attribute_iv_and_salt, mode: :per_attribute_iv_and_salt,
insecure_mode: true,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
serialize :data, JSON serialize :data, JSON
......
...@@ -130,7 +130,7 @@ class Repository ...@@ -130,7 +130,7 @@ class Repository
end end
def find_tag(name) def find_tag(name)
raw_repository.tags.find { |tag| tag.name == name } tags.find { |tag| tag.name == name }
end end
def add_branch(user, branch_name, target) def add_branch(user, branch_name, target)
...@@ -978,6 +978,10 @@ class Repository ...@@ -978,6 +978,10 @@ class Repository
raw_repository.ls_files(actual_ref) raw_repository.ls_files(actual_ref)
end end
def gitattribute(path, name)
raw_repository.attributes(path)[name]
end
def copy_gitattributes(ref) def copy_gitattributes(ref)
actual_ref = ref || root_ref actual_ref = ref || root_ref
begin begin
......
...@@ -20,6 +20,7 @@ class Snippet < ActiveRecord::Base ...@@ -20,6 +20,7 @@ class Snippet < ActiveRecord::Base
length: { within: 0..255 }, length: { within: 0..255 },
format: { with: Gitlab::Regex.file_name_regex, format: { with: Gitlab::Regex.file_name_regex,
message: Gitlab::Regex.file_name_regex_message } message: Gitlab::Regex.file_name_regex_message }
validates :content, presence: true validates :content, presence: true
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
...@@ -81,6 +82,11 @@ class Snippet < ActiveRecord::Base ...@@ -81,6 +82,11 @@ class Snippet < ActiveRecord::Base
0 0
end end
# alias for compatibility with blobs and highlighting
def path
file_name
end
def name def name
file_name file_name
end end
...@@ -135,7 +141,16 @@ class Snippet < ActiveRecord::Base ...@@ -135,7 +141,16 @@ class Snippet < ActiveRecord::Base
end end
def accessible_to(user) def accessible_to(user)
where('visibility_level IN (?) OR author_id = ?', [Snippet::INTERNAL, Snippet::PUBLIC], user) return are_public unless user.present?
return all if user.admin?
where(
'visibility_level IN (:visibility_levels)
OR author_id = :author_id
OR project_id IN (:project_ids)',
visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL],
author_id: user.id,
project_ids: user.authorized_projects.select(:id))
end end
end end
end end
...@@ -25,6 +25,7 @@ class User < ActiveRecord::Base ...@@ -25,6 +25,7 @@ class User < ActiveRecord::Base
attr_encrypted :otp_secret, attr_encrypted :otp_secret,
key: Gitlab::Application.config.secret_key_base, key: Gitlab::Application.config.secret_key_base,
mode: :per_attribute_iv_and_salt, mode: :per_attribute_iv_and_salt,
insecure_mode: true,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
devise :two_factor_authenticatable, devise :two_factor_authenticatable,
...@@ -57,7 +58,7 @@ class User < ActiveRecord::Base ...@@ -57,7 +58,7 @@ class User < ActiveRecord::Base
# Groups # Groups
has_many :members, dependent: :destroy has_many :members, dependent: :destroy
has_many :group_members, dependent: :destroy, source: 'GroupMember' has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember'
has_many :groups, through: :group_members has_many :groups, through: :group_members
has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group
has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group
...@@ -65,7 +66,7 @@ class User < ActiveRecord::Base ...@@ -65,7 +66,7 @@ class User < ActiveRecord::Base
# Projects # Projects
has_many :groups_projects, through: :groups, source: :projects has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects has_many :personal_projects, through: :namespace, source: :projects
has_many :project_members, dependent: :destroy, class_name: 'ProjectMember' has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember'
has_many :projects, through: :project_members has_many :projects, through: :project_members
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :users_star_projects, dependent: :destroy has_many :users_star_projects, dependent: :destroy
...@@ -308,7 +309,7 @@ class User < ActiveRecord::Base ...@@ -308,7 +309,7 @@ class User < ActiveRecord::Base
def generate_password def generate_password
if self.force_random_password if self.force_random_password
self.password = self.password_confirmation = Devise.friendly_token.first(8) self.password = self.password_confirmation = Devise.friendly_token.first(Devise.password_length.min)
end end
end end
......
...@@ -159,8 +159,9 @@ class TodoService ...@@ -159,8 +159,9 @@ class TodoService
def create_todos(users, attributes) def create_todos(users, attributes)
Array(users).map do |user| Array(users).map do |user|
next if pending_todos(user, attributes).exists? next if pending_todos(user, attributes).exists?
Todo.create(attributes.merge(user_id: user.id)) todo = Todo.create(attributes.merge(user_id: user.id))
user.update_todos_count_cache user.update_todos_count_cache
todo
end end
end end
......
- page_title "Groups", @user.name, "Users" - page_title "Groups", @user.name, "Users"
= render 'admin/users/head' = render 'admin/users/head'
- if @user.group_members.present? - group_members = @user.group_members.includes(:source)
- if group_members.any?
.panel.panel-default .panel.panel-default
.panel-heading Groups: .panel-heading Groups:
%ul.well-list %ul.well-list
- @user.group_members.each do |group_member| - group_members.each do |group_member|
- group = group_member.group - group = group_member.group
%li.group_member %li.group_member
%span{class: ("list-item-name" unless group_member.owner?)} %span{class: ("list-item-name" unless group_member.owner?)}
......
.bs-callout.help-callout
%h4 How to setup CI for this project
%ol
%li
Add at least one runner to the project.
Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions.
%li
Put the .gitlab-ci.yml in the root of your repository. Examples can be found in
#{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}.
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
%li
Return to this page and refresh it, it should show a new build.
.alert.alert-danger
%p
Now you need Runners to process your builds.
%span
Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it
.emoji-menu .emoji-menu
= text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Seach emojis"
.emoji-menu-content .emoji-menu-content
= text_field_tag :emoji_search, "", class: "emoji-search search-input form-control"
- Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis| - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis|
%h5.emoji-menu-title %h5.emoji-menu-title
= Gitlab::AwardEmoji::CATEGORIES[category] = Gitlab::AwardEmoji::CATEGORIES[category]
......
...@@ -30,8 +30,8 @@ ...@@ -30,8 +30,8 @@
= javascript_include_tag "application" = javascript_include_tag "application"
- if page_specific_javascripts - if content_for?(:page_specific_javascripts)
= javascript_include_tag page_specific_javascripts, {"data-turbolinks-track" => true} = yield :page_specific_javascripts
= csrf_meta_tags = csrf_meta_tags
......
- if current_user && current_user.is_admin? && Ci::Runner.count.zero?
= render 'ci/shared/no_runners'
.page-with-sidebar{ class: page_sidebar_class }
= render "layouts/broadcast"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
- if defined?(sidebar) && sidebar
= render "layouts/ci/#{sidebar}"
- elsif current_user
= render 'layouts/nav/dashboard'
.collapse-nav
= render partial: 'layouts/collapse_button'
- if current_user
= link_to current_user, class: 'sidebar-user', title: "Profile" do
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
.username
= current_user.username
.content-wrapper
= render "layouts/flash"
= render 'layouts/ci/info'
%div{ class: container_class }
.content
.clearfix
= yield
%html{lang: "en"}
%head
%meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
%title
GitLab CI
%body
= yield :header
%table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"}
%tr
%td{align: "left", style: "margin: 0; padding: 10px;"}
= yield
%br
%tr
%td{align: "left", style: "margin: 0; padding: 10px;"}
%p{style: "font-size:small;color:#777"}
- if @project
You're receiving this notification because you are the one who triggered a build on the #{@project.name} project.
%div{ class: nav_control_class } .scrolling-tabs-container{ class: nav_control_class }
= render 'layouts/nav/admin_settings' = render 'layouts/nav/admin_settings'
.fade-left
%ul.nav-links.scrolling-tabs
%li.fade-left
= icon('arrow-left') = icon('arrow-left')
.fade-right
= icon('arrow-right')
%ul.nav-links.scrolling-tabs
= nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do = nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
%span %span
...@@ -37,5 +38,3 @@ ...@@ -37,5 +38,3 @@
= link_to admin_spam_logs_path, title: "Spam Logs" do = link_to admin_spam_logs_path, title: "Spam Logs" do
%span %span
Spam Logs Spam Logs
%li.fade-right
= icon('arrow-right')
%div{ class: nav_control_class } .scrolling-tabs-container{ class: nav_control_class }
= render 'layouts/nav/group_settings' = render 'layouts/nav/group_settings'
.fade-left
%ul.nav-links.scrolling-tabs
%li.fade-left
= icon('arrow-left') = icon('arrow-left')
.fade-right
= icon('arrow-right')
%ul.nav-links.scrolling-tabs
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do = link_to group_path(@group), title: 'Home' do
%span %span
...@@ -32,5 +33,3 @@ ...@@ -32,5 +33,3 @@
= link_to group_group_members_path(@group), title: 'Members' do = link_to group_group_members_path(@group), title: 'Members' do
%span %span
Members Members
%li.fade-right
= icon('arrow-right')
%ul.nav-links.scrolling-tabs .scrolling-tabs-container
%li.fade-left .fade-left
= icon('arrow-left') = icon('arrow-left')
.fade-right
= icon('arrow-right')
%ul.nav-links.scrolling-tabs
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do = link_to profile_path, title: 'Profile Settings' do
%span %span
...@@ -44,5 +47,3 @@ ...@@ -44,5 +47,3 @@
= link_to audit_log_profile_path, title: 'Audit Log' do = link_to audit_log_profile_path, title: 'Audit Log' do
%span %span
Audit Log Audit Log
%li.fade-right
= icon('arrow-right')
...@@ -24,10 +24,12 @@ ...@@ -24,10 +24,12 @@
data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
Leave Project Leave Project
%div{ class: nav_control_class } .scrolling-tabs-container{ class: nav_control_class }
%ul.nav-links.scrolling-tabs .fade-left
%li.fade-left
= icon('arrow-left') = icon('arrow-left')
.fade-right
= icon('arrow-right')
%ul.nav-links.scrolling-tabs
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do = nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
%span %span
...@@ -111,5 +113,3 @@ ...@@ -111,5 +113,3 @@
%li.hidden %li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits Commits
%li.fade-right
= icon('arrow-right')
...@@ -8,4 +8,4 @@ ...@@ -8,4 +8,4 @@
%strong Only allow merge requests to be merged if the build succeeds %strong Only allow merge requests to be merged if the build succeeds
.help-block .help-block
Builds need to be configured to enable this feature. Builds need to be configured to enable this feature.
= link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds') = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
...@@ -16,4 +16,4 @@ ...@@ -16,4 +16,4 @@
.file-content.code .file-content.code
.nothing-here-block Empty file .nothing-here-block Empty file
- else - else
= render 'shared/file_highlight', blob: blob = render 'shared/file_highlight', blob: blob, repository: @repository
.scrolling-tabs-container .scrolling-tabs-container.sub-nav-scroll
.fade-left
= icon('arrow-left')
.fade-right
= icon('arrow-right')
.nav-links.sub-nav.scrolling-tabs .nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) } %ul{ class: (container_class) }
%li.fade-left
= icon('arrow-left')
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_files_path(@project) do = link_to project_files_path(@project) do
Files Files
...@@ -26,5 +28,3 @@ ...@@ -26,5 +28,3 @@
= nav_link(controller: [:tags, :releases]) do = nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do = link_to namespace_project_tags_path(@project.namespace, @project) do
Tags Tags
%li.fade-right
= icon('arrow-right')
.nav-links.sub-nav .nav-links.sub-nav
%ul{ class: (container_class) } %ul{ class: (container_class) }
- page_specific_javascripts asset_path("graphs/application.js") - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/chart.js')
= page_specific_javascript_tag('graphs/application.js')
= nav_link(action: :show) do = nav_link(action: :show) do
= link_to 'Contributors', namespace_project_graph_path = link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do = nav_link(action: :commits) do
......
- page_title "Network", @ref - page_title "Network", @ref
- page_specific_javascripts asset_path("network/application.js") - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/raphael.js')
= page_specific_javascript_tag('network/application.js')
= render "projects/commits/head" = render "projects/commits/head"
= render "head" = render "head"
%div{ class: (container_class) } %div{ class: (container_class) }
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
.note-body{class: note_editable ? 'js-task-list-container' : ''} .note-body{class: note_editable ? 'js-task-list-container' : ''}
.note-text .note-text
= preserve do = preserve do
= markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author) = note.note_html
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
- if note_editable - if note_editable
= render 'projects/notes/edit_form', note: note = render 'projects/notes/edit_form', note: note
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
Edit Page Edit Page
.nav-controls .nav-controls
- if !(@page && @page.persisted?)
- if can?(current_user, :create_wiki, @project) - if can?(current_user, :create_wiki, @project)
= link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
New Page New Page
......
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
.blob-result .blob-result
.file-holder .file-holder
.file-title .file-title
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename))
= link_to blob_link do
%i.fa.fa-file %i.fa.fa-file
%strong %strong
= blob.filename = blob.filename
.file-content.code.term .file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
%ul.nav-links.event-filter.scrolling-tabs %ul.nav-links.event-filter.scrolling-tabs
%li.fade-left
= icon('arrow-left')
= event_filter_link EventFilter.push, 'Push events' = event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events' = event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments' = event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team' = event_filter_link EventFilter.team, 'Team'
%li.fade-right
= icon('arrow-right')
- repository = nil unless local_assigns.key?(:repository)
.file-content.code.js-syntax-highlight .file-content.code.js-syntax-highlight
.line-numbers .line-numbers
- if blob.data.present? - if blob.data.present?
- link_icon = icon('link') - link_icon = icon('link')
- link = blob_link if defined?(blob_link)
- blob.data.each_line.each_with_index do |_, index| - blob.data.each_line.each_with_index do |_, index|
- offset = defined?(first_line_number) ? first_line_number : 1 - offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset - i = index + offset
-# We're not using `link_to` because it is too slow once we get to thousands of lines. -# We're not using `link_to` because it is too slow once we get to thousands of lines.
%a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i} %a.diff-line-num{href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i}
= link_icon = link_icon
= i = i
.blob-content{data: {blob_id: blob.id}} .blob-content{data: {blob_id: blob.id}}
= highlight(blob.name, blob.data, plain: blob.no_highlighting?) = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?)
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do
- render_colored_label(label) - render_colored_label(label)
%span{ class: "assignee-icon" }
- if assignee - if assignee
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
......
- page_title @user.name - page_title @user.name
- page_description @user.bio - page_description @user.bio
- page_specific_javascripts asset_path("users/application.js") - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/d3.js')
= page_specific_javascript_tag('users/application.js')
- header_title @user.name, user_path(@user) - header_title @user.name, user_path(@user)
- @no_container = true - @no_container = true
......
...@@ -84,6 +84,8 @@ module Gitlab ...@@ -84,6 +84,8 @@ module Gitlab
config.assets.precompile << "graphs/application.js" config.assets.precompile << "graphs/application.js"
config.assets.precompile << "users/application.js" config.assets.precompile << "users/application.js"
config.assets.precompile << "network/application.js" config.assets.precompile << "network/application.js"
config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.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'
......
Haml::Template.options[:ugly] = true
# Remove the `:coffee` and `:coffeescript` filters
#
# See https://git.io/vztMu and http://stackoverflow.com/a/17571242/223897
Haml::Filters.remove_filter('coffee')
Haml::Filters.remove_filter('coffeescript')
module Hamlit
class TemplateHandler
def call(template)
Engine.new(
generator: Temple::Generators::RailsOutputBuffer,
attr_quote: '"',
).call(template.source)
end
end
end
ActionView::Template.register_template_handler(
:haml,
Hamlit::TemplateHandler.new,
)
Hamlit::Filters.remove_filter('coffee')
Hamlit::Filters.remove_filter('coffeescript')
# Email forcibly included in the standard checks, but the email health check
# doesn't support the full range of SMTP options, which can result in failures
# for valid SMTP configurations.
# Overwrite the HealthCheck's detection of whether email is configured
# in order to avoid the email check during standard checks
module HealthCheck
class Utils
def self.mailer_configured?
false
end
end
end
HealthCheck.setup do |config| HealthCheck.setup do |config|
config.standard_checks = ['database', 'migrations', 'cache'] config.standard_checks = ['database', 'migrations', 'cache']
config.full_checks = ['database', 'migrations', 'cache']
end end
...@@ -113,6 +113,10 @@ if Gitlab::Metrics.enabled? ...@@ -113,6 +113,10 @@ if Gitlab::Metrics.enabled?
config.instrument_methods(Banzai::Renderer) config.instrument_methods(Banzai::Renderer)
config.instrument_methods(Banzai::Querying) config.instrument_methods(Banzai::Querying)
config.instrument_instance_methods(Banzai::ObjectRenderer)
config.instrument_instance_methods(Banzai::Redactor)
config.instrument_methods(Banzai::NoteRenderer)
[Issuable, Mentionable, Participable].each do |klass| [Issuable, Mentionable, Participable].each do |klass|
config.instrument_instance_methods(klass) config.instrument_instance_methods(klass)
config.instrument_instance_methods(klass::ClassMethods) config.instrument_instance_methods(klass::ClassMethods)
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
if Rails.env.production? if Rails.env.production?
Rails.application.config.action_mailer.delivery_method = :smtp Rails.application.config.action_mailer.delivery_method = :smtp
ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = { ActionMailer::Base.smtp_settings = {
address: "email.server.com", address: "email.server.com",
port: 465, port: 465,
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast. - [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics. - [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics.
- [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint. - [Monitoring uptime](monitoring/health_check.md) Check the server status using the health check endpoint.
- [Debugging Tips](administration/troubleshooting/debug.md) Tips to debug problems when things go wrong
- [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs. - [Sidekiq Troubleshooting](administration/troubleshooting/sidekiq.md) Debug when Sidekiq appears hung and is not processing jobs.
- [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability. - [High Availability](administration/high_availability/README.md) Configure multiple servers for scaling or high availability.
- [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab. - [Container Registry](administration/container_registry.md) Configure Docker Registry with GitLab.
......
# Debugging Tips
Sometimes things don't work the way they should. Here are some tips on debugging issues out
in production.
## The GNU Project Debugger (gdb)
`gdb` is a must-have tool for debugging issues. To install on Ubuntu/Debian:
```
sudo apt-get install gdb
```
On CentOS:
```
sudo yum install gdb
```
## Common Problems
Many of the tips to diagnose issues below apply to many different situations. We'll use one
concrete example to illustrate what you can do to learn what is going wrong.
### 502 Gateway Timeout after unicorn spins at 100% CPU
This error occurs when the Web server times out (default: 60 s) after not
hearing back from the unicorn worker. If the CPU spins to 100% while this in
progress, there may be something taking longer than it should.
To fix this issue, we first need to figure out what is happening. The
following tips are only recommended if you do NOT mind users being affected by
downtime. Otherwise skip to the next section.
1. Load the problematic URL
1. Run `sudo gdb -p <PID>` to attach to the unicorn process.
1. In the gdb window, type:
```
call (void) rb_backtrace()
```
1. This forces the process to generate a Ruby backtrace. Check
`/var/log/gitlab/unicorn/unicorn_stderr.log` for the backtace. For example, you may see:
```ruby
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `block in start'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:33:in `loop'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:36:in `block (2 levels) in start'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:44:in `sample'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `sample_objects'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each_with_object'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:68:in `each'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `block in sample_objects'
from /opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/metrics/sampler.rb:69:in `name'
```
1. To see the current threads, run:
```
apply all thread bt
```
1. Once you're done debugging with `gdb`, be sure to detach from the process and exit:
```
detach
exit
```
Note that if the unicorn process terminates before you are able to run these
commands, gdb will report an error. To buy more time, you can always raise the
Unicorn timeout. For omnibus users, you can edit `/etc/gitlab/gitlab.rb` and
increase it from 60 seconds to 300:
```ruby
unicorn['worker_timeout'] = 300
```
For source installations, edit `config/unicorn.rb`.
[Reconfigure] GitLab for the changes to take effect.
[Reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
#### Troubleshooting without affecting other users
The previous section attached to a running unicorn process, and this may have
undesirable effects for users trying to access GitLab during this time. If you
are concerned about affecting others during a production system, you can run a
separate Rails process to debug the issue:
1. Log in to your GitLab account.
1. Copy the URL that is causing problems (e.g. https://gitlab.com/ABC).
1. Obtain the private token for your user (Profile Settings -> Account).
1. Bring up the GitLab Rails console. For omnibus users, run:
````
sudo gitlab-rails console
```
1. At the Rails console, run:
```ruby
[1] pry(main)> app.get '<URL FROM STEP 1>/private_token?<TOKEN FROM STEP 2>'
```
For example:
```ruby
[1] pry(main)> app.get 'https://gitlab.com/gitlab-org/gitlab-ce/issues/1?private_token=123456'
```
1. In a new window, run `top`. It should show this ruby process using 100% CPU. Write down the PID.
1. Follow step 2 from the previous section on using gdb.
# More information
* [Debugging Stuck Ruby Processes](https://blog.newrelic.com/2013/04/29/debugging-stuck-ruby-processes-what-to-do-before-you-kill-9/)
* [Cheatsheet of using gdb and ruby processes](gdb-stuck-ruby.txt)
# Here's the script I'll use to demonstrate - it just loops forever:
$ cat test.rb
#!/usr/bin/env ruby
loop do
sleep 1
end
# Now, I'll start the script in the background, and redirect stdout and stderr
# to /dev/null:
$ ruby ./test.rb >/dev/null 2>/dev/null &
[1] 1343
# Next, I'll grab the PID of the script (1343):
$ ps aux | grep test.rb
vagrant 1343 0.0 0.4 3884 1652 pts/0 S 14:42 0:00 ruby ./test.rb
vagrant 1345 0.0 0.2 4624 852 pts/0 S+ 14:42 0:00 grep --color=auto test.rb
# Now I start gdb. Note that I'm using sudo here. This may or may not be
# necessary in your setup. I'd try without sudo first, and fall back to adding
# it if the next step fails:
$ sudo gdb
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>.
# OK, now I'm in gdb, and I want to instruct it to attach to our Ruby process.
# I can do that using the 'attach' command, which takes a PID (the one we
# gathered above):
(gdb) attach 1343
Attaching to process 1343
Reading symbols from /opt/vagrant_ruby/bin/ruby...done.
Reading symbols from /lib/i386-linux-gnu/librt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/librt.so.1
Reading symbols from /lib/i386-linux-gnu/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libdl.so.2
Reading symbols from /lib/i386-linux-gnu/libcrypt.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libcrypt.so.1
Reading symbols from /lib/i386-linux-gnu/libm.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libm.so.6
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/i386-linux-gnu/libc.so.6
Reading symbols from /lib/i386-linux-gnu/libpthread.so.0...(no debugging symbols found)...done.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
Loaded symbols for /lib/i386-linux-gnu/libpthread.so.0
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0xb770c424 in __kernel_vsyscall ()
# Great, now gdb is attached to the target process. If the step above fails, try
# going back and running gdb under sudo. The next thing I want to do is gather
# C-level backtraces from all threads in the process. The following command
# stands for 'thread apply all backtrace':
(gdb) t a a bt
Thread 1 (Thread 0xb74d76c0 (LWP 1343)):
#0 0xb770c424 in __kernel_vsyscall ()
#1 0xb75d7abd in select () from /lib/i386-linux-gnu/libc.so.6
#2 0x08069c56 in rb_thread_wait_for (time=...) at eval.c:11376
#3 0x080a20fd in rb_f_sleep (argc=1, argv=0xbf85f490) at process.c:1633
#4 0x0805e0e2 in call_cfunc (argv=0xbf85f490, argc=1, len=-1, recv=3075299660, func=0x80a20b0 <rb_f_sleep>)
at eval.c:5778
#5 rb_call0 (klass=3075304600, recv=3075299660, id=9393, oid=9393, argc=1, argv=0xbf85f490, body=0xb74c85a8, flags=2)
at eval.c:5928
#6 0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=9393, argc=1, argv=0xbf85f490, scope=1,
self=<optimized out>) at eval.c:6176
#7 0x080651ec in rb_eval (self=3075299660, n=0xb74c4e1c) at eval.c:3521
#8 0x0805c31c in rb_yield_0 (val=6, self=3075299660, klass=<optimized out>, flags=0, avalue=0) at eval.c:5095
#9 0x0806a1e5 in loop_i () at eval.c:5227
#10 0x08058dbd in rb_rescue2 (b_proc=0x806a1c0 <loop_i>, data1=0, r_proc=0, data2=0) at eval.c:5491
#11 0x08058f28 in rb_f_loop () at eval.c:5252
#12 0x0805e0c1 in call_cfunc (argv=0x0, argc=0, len=0, recv=3075299660, func=0x8058ef0 <rb_f_loop>) at eval.c:5781
#13 rb_call0 (klass=3075304600, recv=3075299660, id=4121, oid=4121, argc=0, argv=0x0, body=0xb74d4dbc, flags=2)
at eval.c:5928
#14 0x0805e35d in rb_call (klass=3075304600, recv=3075299660, mid=4121, argc=0, argv=0x0, scope=1, self=<optimized out>)
at eval.c:6176
#15 0x080651ec in rb_eval (self=3075299660, n=0xb74c4dcc) at eval.c:3521
#16 0x080662c6 in rb_eval (self=3075299660, n=0xb74c4de0) at eval.c:3236
#17 0x08068ee4 in ruby_exec_internal () at eval.c:1654
#18 0x08068f24 in ruby_exec () at eval.c:1674
#19 0x0806b2cd in ruby_run () at eval.c:1684
#20 0x08053771 in main (argc=2, argv=0xbf860204, envp=0xbf860210) at main.c:48
# C backtraces are sometimes sufficient, but often Ruby backtraces are necessary
# for debugging as well. Ruby has a built-in function called rb_backtrace() that
# we can use to dump out a Ruby backtrace, but it prints to stdout or stderr
# (depending on your Ruby version), which might have been redirected to a file
# or to /dev/null (as in our example) when the process started up.
#
# To get aroundt this, we'll do a little trick and redirect the target process's
# stdout and stderr to the current TTY, so that any output from the process
# will appear directly on our screen.
# First, let's close the existing file descriptors for stdout and stderr
# (FD 1 and 2, respectively):
(gdb) call (void) close(1)
(gdb) call (void) close(2)
# Next, we need to figure out the device name for the current TTY:
(gdb) shell tty
/dev/pts/0
# OK, now we can pass the device name obtained above to open() and attach
# file descriptors 1 and 2 back to the current TTY with these calls:
(gdb) call (int) open("/dev/pts/0", 2, 0)
$1 = 1
(gdb) call (int) open("/dev/pts/0", 2, 0)
$2 = 2
# Finally, we call rb_backtrace() in order to dump the Ruby backtrace:
(gdb) call (void) rb_backtrace()
from ./test.rb:4:in `sleep'
from ./test.rb:4
from ./test.rb:3:in `loop'
from ./test.rb:3
# And here's how we get out of gdb. Once you've quit, you'll probably want to
# clean up the stuck process by killing it.
(gdb) quit
A debugging session is active.
Inferior 1 [process 1343] will be detached.
Quit anyway? (y or n) y
Detaching from program: /opt/vagrant_ruby/bin/ruby, process 1343
$
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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