summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml135
-rw-r--r--CHANGELOG17
-rw-r--r--CONTRIBUTING.md1
-rw-r--r--app/assets/javascripts/admin.js.coffee13
-rw-r--r--app/assets/javascripts/broadcast_message.js.coffee22
-rw-r--r--app/assets/javascripts/dashboard.js.coffee9
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee2
-rw-r--r--app/assets/javascripts/logo.js.coffee3
-rw-r--r--app/assets/javascripts/projects_list.js.coffee34
-rw-r--r--app/assets/stylesheets/pages/admin.scss10
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb4
-rw-r--r--app/controllers/ci/application_controller.rb47
-rw-r--r--app/controllers/ci/projects_controller.rb11
-rw-r--r--app/controllers/dashboard/projects_controller.rb4
-rw-r--r--app/controllers/explore/projects_controller.rb36
-rw-r--r--app/controllers/groups_controller.rb19
-rw-r--r--app/controllers/projects/artifacts_controller.rb12
-rw-r--r--app/controllers/projects/badges_controller.rb11
-rw-r--r--app/controllers/projects/builds_controller.rb24
-rw-r--r--app/controllers/projects/commit_controller.rb12
-rw-r--r--app/controllers/projects/commits_controller.rb3
-rw-r--r--app/controllers/projects/compare_controller.rb23
-rw-r--r--app/controllers/projects/runner_projects_controller.rb2
-rw-r--r--app/controllers/projects/runners_controller.rb2
-rw-r--r--app/controllers/projects/triggers_controller.rb2
-rw-r--r--app/controllers/projects/variables_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/helpers/broadcast_messages_helper.rb6
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/snippets_helper.rb6
-rw-r--r--app/models/ability.rb80
-rw-r--r--app/models/application_setting.rb5
-rw-r--r--app/models/concerns/issuable.rb5
-rw-r--r--app/models/merge_request.rb1
-rw-r--r--app/models/milestone.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/repository.rb50
-rw-r--r--app/services/ci/image_for_build_service.rb19
-rw-r--r--app/services/git_push_service.rb9
-rw-r--r--app/views/admin/application_settings/_form.html.haml10
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml7
-rw-r--r--app/views/admin/broadcast_messages/preview.js.haml1
-rw-r--r--app/views/admin/builds/_build.html.haml6
-rw-r--r--app/views/dashboard/_projects_head.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml8
-rw-r--r--app/views/explore/projects/_projects.html.haml2
-rw-r--r--app/views/groups/_projects.html.haml17
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/notify/_note_message.html.haml3
-rw-r--r--app/views/notify/new_issue_email.html.haml3
-rw-r--r--app/views/notify/new_merge_request_email.html.haml3
-rw-r--r--app/views/projects/builds/index.html.haml2
-rw-r--r--app/views/projects/builds/show.html.haml5
-rw-r--r--app/views/projects/commit/_builds.html.haml2
-rw-r--r--app/views/projects/commit_statuses/_commit_status.html.haml10
-rw-r--r--app/views/projects/commits/show.html.haml5
-rw-r--r--app/views/projects/compare/_form.html.haml5
-rw-r--r--app/views/projects/edit.html.haml8
-rw-r--r--app/views/projects/forks/index.html.haml19
-rw-r--r--app/views/projects/tree/_readme.html.haml2
-rw-r--r--app/views/projects/variables/show.html.haml4
-rw-r--r--app/views/search/results/_snippet_blob.html.haml44
-rw-r--r--app/views/shared/_import_form.html.haml2
-rw-r--r--app/views/shared/_logo.svg28
-rw-r--r--app/views/shared/projects/_list.html.haml3
-rw-r--r--config/application.rb18
-rw-r--r--config/gitlab.yml.example8
-rw-r--r--config/initializers/smtp_settings.rb.sample2
-rw-r--r--config/routes.rb14
-rw-r--r--config/unicorn.rb.example9
-rw-r--r--db/fixtures/development/14_builds.rb39
-rw-r--r--db/fixtures/production/001_admin.rb4
-rw-r--r--db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb5
-rw-r--r--db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb2
-rw-r--r--db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb5
-rw-r--r--db/migrate/20160209130428_add_index_to_snippet.rb5
-rw-r--r--db/schema.rb5
-rw-r--r--doc/api/builds.md68
-rw-r--r--doc/api/merge_requests.md188
-rw-r--r--doc/api/projects.md10
-rw-r--r--doc/ci/docker/using_docker_images.md2
-rw-r--r--doc/ci/quick_start/README.md8
-rw-r--r--doc/ci/variables/README.md5
-rw-r--r--doc/install/installation.md13
-rw-r--r--doc/install/relative_url.md126
-rw-r--r--doc/install/requirements.md2
-rw-r--r--doc/markdown/img/logo.pngbin0 -> 11097 bytes
-rw-r--r--doc/markdown/markdown.md8
-rw-r--r--doc/permissions/permissions.md9
-rw-r--r--doc/update/8.4-to-8.5.md106
-rw-r--r--doc/web_hooks/web_hooks.md26
-rw-r--r--features/admin/broadcast_messages.feature6
-rw-r--r--features/project/badges/build.feature22
-rw-r--r--features/project/builds/permissions.feature35
-rw-r--r--features/project/commits/commits.feature36
-rw-r--r--features/steps/admin/broadcast_messages.rb14
-rw-r--r--features/steps/project/badges/build.rb28
-rw-r--r--features/steps/project/builds/summary.rb8
-rw-r--r--features/steps/project/commits/commits.rb57
-rw-r--r--features/steps/shared/builds.rb33
-rw-r--r--features/steps/shared/project.rb12
-rw-r--r--lib/api/builds.rb29
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/merge_requests.rb13
-rw-r--r--lib/api/projects.rb12
-rw-r--r--lib/api/triggers.rb8
-rw-r--r--lib/api/variables.rb2
-rw-r--r--lib/backup/manager.rb3
-rw-r--r--lib/banzai/pipeline/broadcast_message_pipeline.rb16
-rw-r--r--lib/gitlab/backend/shell.rb2
-rw-r--r--lib/gitlab/blame.rb1
-rw-r--r--lib/gitlab/current_settings.rb4
-rw-r--r--lib/gitlab/git.rb6
-rw-r--r--lib/gitlab/snippet_search_results.rb10
-rwxr-xr-xlib/support/init.d/gitlab4
-rwxr-xr-xlib/support/init.d/gitlab.default.example13
-rw-r--r--lib/tasks/gitlab/check.rake18
-rwxr-xr-xscripts/prepare_build.sh12
-rw-r--r--spec/factories/ci/builds.rb42
-rw-r--r--spec/features/builds_spec.rb2
-rw-r--r--spec/features/commits_spec.rb152
-rw-r--r--spec/features/security/project/public_access_spec.rb54
-rw-r--r--spec/mailers/notify_spec.rb37
-rw-r--r--spec/models/application_setting_spec.rb8
-rw-r--r--spec/models/concerns/issuable_spec.rb10
-rw-r--r--spec/models/milestone_spec.rb14
-rw-r--r--spec/models/repository_spec.rb127
-rw-r--r--spec/requests/api/builds_spec.rb8
-rw-r--r--spec/requests/api/commit_status_spec.rb48
-rw-r--r--spec/requests/api/merge_requests_spec.rb23
-rw-r--r--spec/requests/ci/api/builds_spec.rb106
-rw-r--r--spec/services/git_push_service_spec.rb30
134 files changed, 1962 insertions, 648 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index dbdbae9d787..1c4d98ea3f6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,6 +5,11 @@ services:
- postgres:latest
- redis:latest
+cache:
+ key: "ruby22"
+ paths:
+ - vendor
+
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
@@ -137,23 +142,145 @@ bundler:audit:
# Ruby 2.1 jobs
-spec:ruby21:
+spec:feature:ruby21:
image: ruby:2.1
+ only:
+ - master
script:
- RAILS_ENV=test bundle exec rake assets:precompile 2>/dev/null
- - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:feature
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
+
+spec:api:ruby21:
+ image: ruby:2.1
only:
- master
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:api
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
-spinach:ruby21:
+spec:models:ruby21:
image: ruby:2.1
+ only:
+ - master
script:
- - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
tags:
- ruby
- mysql
+
+spec:lib:ruby21:
+ image: ruby:2.1
only:
- master
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
+
+spec:services:ruby21:
+ image: ruby:2.1
+ only:
+ - master
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
+
+spec:benchmark:ruby21:
+ image: ruby:2.1
+ only:
+ - master
+ script:
+ - RAILS_ENV=test bundle exec rake spec:benchmark
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
+ allow_failure: true
+
+spec:other:ruby21:
+ image: ruby:2.1
+ only:
+ - master
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:other
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
+
+spinach:project:half:ruby21:
+ image: ruby:2.1
+ only:
+ - master
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
+
+spinach:project:rest:ruby21:
+ image: ruby:2.1
+ only:
+ - master
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
+
+spinach:other:ruby21:
+ image: ruby:2.1
+ only:
+ - master
+ script:
+ - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:other
+ cache:
+ key: "ruby21"
+ paths:
+ - vendor
+ tags:
+ - ruby
+ - mysql
+
diff --git a/CHANGELOG b/CHANGELOG
index b31fad1d3de..3fc9558e285 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,30 +1,38 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.5.0 (unreleased)
+ - Cache various Repository methods to improve performance (Yorick Peterse)
- Ensure rake tasks that don't need a DB connection can be run without one
- Update New Relic gem to 3.14.1.311 (Stan Hu)
- Add "visibility" flag to GET /projects api endpoint
+ - Add an option to supply root email through an environmental variable (Koichiro Mikami)
- Ignore binary files in code search to prevent Error 500 (Stan Hu)
- Render sanitized SVG images (Stan Hu)
- Support download access by PRIVATE-TOKEN header (Stan Hu)
- Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
+ - Add option to include the sender name in body of Notify email (Jason Lee)
- New UI for pagination
- Don't prevent sign out when 2FA enforcement is enabled and user hasn't yet
set it up
+ - API: Added "merge_requests/:merge_request_id/closes_issues" (Gal Schlezinger)
- Fix diff comments loaded by AJAX to load comment with diff in discussion tab
- Whitelist raw "abbr" elements when parsing Markdown (Benedict Etzel)
- Fix label links for a merge request pointing to issues list
- Don't vendor minified JS
+ - Increase project import timeout to 15 minutes
- Display 404 error on group not found
- Track project import failure
- Support Two-factor Authentication for LDAP users
- Display database type and version in Administration dashboard
+ - Allow limited Markdown in Broadcast Messages
- Fix visibility level text in admin area (Zeger-Jan van de Weg)
- Warn admin during OAuth of granting admin rights (Zeger-Jan van de Weg)
- Update the ExternalIssue regex pattern (Blake Hitchcock)
- Remember user's inline/side-by-side diff view preference in a cookie (Kirill Katsnelson)
- Optimized performance of finding issues to be closed by a merge request
+ - API: Expose MergeRequest#merge_status (Andrei Dziahel)
- Revert "Add IP check against DNSBLs at account sign-up"
+ - Actually use the `skip_merges` option in Repository#commits (Tony Chu)
- Fix API to keep request parameters in Link header (Michael Potthoff)
- Deprecate API "merge_request/:merge_request_id/comments". Use "merge_requests/:merge_request_id/notes" instead
- Deprecate API "merge_request/:merge_request_id/...". Use "merge_requests/:merge_request_id/..." instead
@@ -37,9 +45,17 @@ v 8.5.0 (unreleased)
- Fixed logo animation on Safari (Roman Rott)
- Hide remove source branch button when the MR is merged but new commits are pushed (Zeger-Jan van de Weg)
- In seach autocomplete show only groups and projects you are member of
+ - Fix: init.d script not working on OS X
+ - Faster snippet search
+ - Title for milestones should be unique (Zeger-Jan van de Weg)
+ - Validate correctness of maximum attachment size application setting
+ - Replaces "Create merge request" link with one to the "Merge Request" when one exists
+ - Fix CI builds badge, add a new link to builds badge, deprecate the old one
v 8.4.4
- Update omniauth-saml gem to 1.4.2
+ - Prevent long-running backup tasks from timing out the database connection
+ - Add a Project setting to allow guests to view build logs (defaults to true)
v 8.4.3
- Increase lfs_objects size column to 8-byte integer to allow files larger
@@ -375,6 +391,7 @@ v 8.1.0
- Improved performance of the trending projects page
- Remove CI migration task
- Improved performance of finding projects by their namespace
+ - Add assignee data to Issuables' hook_data (Bram Daams)
- Fix bug where transferring a project would result in stale commit links (Stan Hu)
- Fix build trace updating
- Include full path of source and target branch names in New Merge Request page (Stan Hu)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 263f98b1e57..c4522998f42 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -364,6 +364,7 @@ corresponding merge request should be updated to have the following:
This makes it easier for release managers to keep track of what still has to be
merged and where changes have to be merged into.
+Like all merge requests the target should be master so all bugfixes are in master.
## Definition of done
diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee
index eb951f71711..b2b8e1b7ffb 100644
--- a/app/assets/javascripts/admin.js.coffee
+++ b/app/assets/javascripts/admin.js.coffee
@@ -12,19 +12,6 @@ class @Admin
e.preventDefault()
$('.js-toggle-colors-container').toggle()
- $('input#broadcast_message_color').on 'input', ->
- previewColor = $(@).val()
- $('div.broadcast-message-preview').css('background-color', previewColor)
-
- $('input#broadcast_message_font').on 'input', ->
- previewColor = $(@).val()
- $('div.broadcast-message-preview').css('color', previewColor)
-
- $('textarea#broadcast_message_message').on 'input', ->
- previewMessage = $(@).val()
- previewMessage = "Your message here" if previewMessage.trim() == ''
- $('div.broadcast-message-preview span').text(previewMessage)
-
$('.log-tabs a').click (e) ->
e.preventDefault()
$(this).tab('show')
diff --git a/app/assets/javascripts/broadcast_message.js.coffee b/app/assets/javascripts/broadcast_message.js.coffee
new file mode 100644
index 00000000000..a38a329c4c2
--- /dev/null
+++ b/app/assets/javascripts/broadcast_message.js.coffee
@@ -0,0 +1,22 @@
+$ ->
+ $('input#broadcast_message_color').on 'input', ->
+ previewColor = $(@).val()
+ $('div.broadcast-message-preview').css('background-color', previewColor)
+
+ $('input#broadcast_message_font').on 'input', ->
+ previewColor = $(@).val()
+ $('div.broadcast-message-preview').css('color', previewColor)
+
+ previewPath = $('textarea#broadcast_message_message').data('preview-path')
+
+ $('textarea#broadcast_message_message').on 'input', ->
+ message = $(@).val()
+
+ if message == ''
+ $('.js-broadcast-message-preview').text("Your message here")
+ else
+ $.ajax(
+ url: previewPath
+ type: "POST"
+ data: { broadcast_message: { message: message } }
+ )
diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee
index dd295088312..62143e66cfe 100644
--- a/app/assets/javascripts/dashboard.js.coffee
+++ b/app/assets/javascripts/dashboard.js.coffee
@@ -1,10 +1,11 @@
@Dashboard =
init: ->
+ $(".projects-list-filter").off('keyup')
this.initSearch()
initSearch: ->
@timer = null
- $("#project-filter-form-field").on('keyup', ->
+ $(".projects-list-filter").on('keyup', ->
clearTimeout(@timer)
@timer = setTimeout(Dashboard.filterResults, 500)
)
@@ -13,8 +14,8 @@
$('.projects-list-holder').fadeTo(250, 0.5)
form = null
- form = $("#project-filter-form")
- search = $("#project-filter-form-field").val()
+ form = $("form#project-filter-form")
+ search = $(".projects-list-filter").val()
project_filter_url = form.attr('action') + '?' + form.serialize()
$.ajax
@@ -24,7 +25,7 @@
complete: ->
$('.projects-list-holder').fadeTo(250, 1)
success: (data) ->
- $('div.projects-list-holder').replaceWith(data.html)
+ $('.projects-list-holder').replaceWith(data.html)
# Change url so if user reload a page - search results are saved
history.replaceState {page: project_filter_url}, document.title, project_filter_url
dataType: "json"
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index d4a2b74b143..b17f8e51470 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -16,6 +16,8 @@ class Dispatcher
shortcut_handler = null
switch page
+ when 'explore:projects:index', 'explore:projects:starred', 'explore:projects:trending'
+ Dashboard.init()
when 'projects:issues:index'
Issues.init()
shortcut_handler = new ShortcutsNavigation()
diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee
index e7d884662ea..35b2fbbba07 100644
--- a/app/assets/javascripts/logo.js.coffee
+++ b/app/assets/javascripts/logo.js.coffee
@@ -44,6 +44,7 @@ $(document).on('page:fetch', start)
$(document).on('page:change', stop)
$ ->
- # Make logo clickable
+ # Make logo clickable as part of a workaround for Safari visited
+ # link behaviour (See !2690).
$('#logo').on 'click', ->
$('#js-shortcuts-home').get(0).click()
diff --git a/app/assets/javascripts/projects_list.js.coffee b/app/assets/javascripts/projects_list.js.coffee
index ebf7140b7e3..eab34be652a 100644
--- a/app/assets/javascripts/projects_list.js.coffee
+++ b/app/assets/javascripts/projects_list.js.coffee
@@ -3,22 +3,24 @@ class @ProjectsList
$(".projects-list .js-expand").on 'click', (e) ->
e.preventDefault()
list = $(this).closest('.projects-list')
- list.find("li").show()
- list.find("li.bottom").hide()
- $(".projects-list-filter").keyup ->
- terms = $(this).val()
- uiBox = $('div.projects-list-holder')
- filterSelector = $(this).data('filter-selector') || 'span.filter-title'
+ $("#filter_projects").on 'keyup', ->
+ ProjectsList.filter_results($("#filter_projects"))
- if terms == "" || terms == undefined
- uiBox.find("ul.projects-list li").show()
- else
- uiBox.find("ul.projects-list li").each (index) ->
- name = $(this).find(filterSelector).text()
+ @filter_results: ($element) ->
+ terms = $element.val()
+ filterSelector = $element.data('filter-selector') || 'span.filter-title'
- if name.toLowerCase().search(terms.toLowerCase()) == -1
- $(this).hide()
- else
- $(this).show()
- uiBox.find("ul.projects-list li.bottom").hide()
+ if not terms
+ $(".projects-list li").show()
+ $('.gl-pagination').show()
+ else
+ $(".projects-list li").each (index) ->
+ $this = $(this)
+ name = $this.find(filterSelector).text()
+
+ if name.toLowerCase().indexOf(terms.toLowerCase()) == -1
+ $this.hide()
+ else
+ $this.show()
+ $('.gl-pagination').hide()
diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss
index 144852e7874..a61161810a3 100644
--- a/app/assets/stylesheets/pages/admin.scss
+++ b/app/assets/stylesheets/pages/admin.scss
@@ -55,6 +55,16 @@
@extend .alert-warning;
padding: 10px;
text-align: center;
+
+ > div, p {
+ display: inline;
+ margin: 0;
+
+ a {
+ color: inherit;
+ text-decoration: underline;
+ }
+ }
}
.broadcast-message-preview {
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 1515086b16d..04a99d8c84a 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -81,6 +81,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:sentry_dsn,
:akismet_enabled,
:akismet_api_key,
+ :email_author_in_body,
restricted_visibility_levels: [],
import_sources: []
)
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index a470d865408..fc342924987 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -36,6 +36,10 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
end
end
+ def preview
+ @message = broadcast_message_params[:message]
+ end
+
protected
def finder
diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb
index c420b59c3a2..5bb7d499cdc 100644
--- a/app/controllers/ci/application_controller.rb
+++ b/app/controllers/ci/application_controller.rb
@@ -3,52 +3,5 @@ module Ci
def self.railtie_helpers_paths
"app/helpers/ci"
end
-
- private
-
- def authorize_access_project!
- unless can?(current_user, :read_project, project)
- return page_404
- end
- end
-
- def authorize_manage_builds!
- unless can?(current_user, :manage_builds, project)
- return page_404
- end
- end
-
- def authenticate_admin!
- return render_404 unless current_user.is_admin?
- end
-
- def authorize_manage_project!
- unless can?(current_user, :admin_project, project)
- return page_404
- end
- end
-
- def page_404
- render file: "#{Rails.root}/public/404.html", status: 404, layout: false
- end
-
- def default_headers
- headers['X-Frame-Options'] = 'DENY'
- headers['X-XSS-Protection'] = '1; mode=block'
- end
-
- # JSON for infinite scroll via Pager object
- def pager_json(partial, count)
- html = render_to_string(
- partial,
- layout: false,
- formats: [:html]
- )
-
- render json: {
- html: html,
- count: count
- }
- end
end
end
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 3004c2d27f0..d1824b481d7 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -1,8 +1,7 @@
module Ci
class ProjectsController < Ci::ApplicationController
- before_action :project, except: [:index]
- before_action :authenticate_user!, except: [:index, :build, :badge]
- before_action :authorize_access_project!, except: [:index, :badge]
+ before_action :project
+ before_action :authorize_read_project!, except: [:badge]
before_action :no_cache, only: [:badge]
protect_from_forgery
@@ -13,9 +12,13 @@ module Ci
# Project status badge
# Image with build status for sha or ref
+ #
+ # This action in DEPRECATED, this is here only for backwards compatibility
+ # with projects migrated from GitLab CI.
+ #
def badge
+ return render_404 unless @project
image = Ci::ImageForBuildService.new.execute(@project, params)
-
send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml"
end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 0bcc78a8bc7..2df6924b13d 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -12,7 +12,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = @projects.search(terms)
end
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
respond_to do |format|
@@ -41,7 +41,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = @projects.search(terms)
end
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if terms.blank?
@last_push = current_user.recent_push
@groups = []
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 2689bf4f1ec..a384f3004db 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -6,19 +6,49 @@ class Explore::ProjectsController < Explore::ApplicationController
@projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present?
@projects = @projects.non_archived
@projects = @projects.search(params[:search]) if params[:search].present?
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.sort(@sort = params[:sort])
- @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE)
+ @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
+ end
+ end
end
def trending
@projects = TrendingProjectsFinder.new.execute(current_user)
@projects = @projects.non_archived
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
+ end
+ end
end
def starred
@projects = ProjectsFinder.new.execute(current_user)
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
@projects = @projects.reorder('star_count DESC')
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
+ end
+ end
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 90475c17c17..ca5ce1e2046 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -14,7 +14,7 @@ class GroupsController < Groups::ApplicationController
# Load group projects
before_action :load_projects, except: [:index, :new, :create, :projects, :edit, :update, :autocomplete]
- before_action :event_filter, only: :show
+ before_action :event_filter, only: [:show, :events]
layout :determine_layout
@@ -41,14 +41,16 @@ class GroupsController < Groups::ApplicationController
def show
@last_push = current_user.recent_push if current_user
@projects = @projects.includes(:namespace)
- @projects = @projects.page(params[:page]).per(PER_PAGE)
+ @projects = @projects.search(params[:filter_projects]) if params[:filter_projects].present?
+ @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank?
respond_to do |format|
format.html
format.json do
- load_events
- pager_json("events/_events", @events.count)
+ render json: {
+ html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ }
end
format.atom do
@@ -58,6 +60,15 @@ class GroupsController < Groups::ApplicationController
end
end
+ def events
+ respond_to do |format|
+ format.json do
+ load_events
+ pager_json("events/_events", @events.count)
+ end
+ end
+ end
+
def edit
end
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index f159a6d6dc6..cfea1266516 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -1,6 +1,6 @@
class Projects::ArtifactsController < Projects::ApplicationController
layout 'project'
- before_action :authorize_read_build_artifacts!
+ before_action :authorize_read_build!
def download
unless artifacts_file.file_storage?
@@ -43,14 +43,4 @@ class Projects::ArtifactsController < Projects::ApplicationController
def artifacts_file
@artifacts_file ||= build.artifacts_file
end
-
- def authorize_read_build_artifacts!
- unless can?(current_user, :read_build_artifacts, @project)
- if current_user.nil?
- return authenticate_user!
- else
- return render_404
- end
- end
- end
end
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
new file mode 100644
index 00000000000..a4dd94b941c
--- /dev/null
+++ b/app/controllers/projects/badges_controller.rb
@@ -0,0 +1,11 @@
+class Projects::BadgesController < Projects::ApplicationController
+ def build
+ respond_to do |format|
+ format.html { render_404 }
+ format.svg do
+ image = Ci::ImageForBuildService.new.execute(project, ref: params[:ref])
+ send_file(image.path, filename: image.name, disposition: 'inline', type: 'image/svg+xml')
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 92d9699fe84..ec379c53b8f 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -1,9 +1,8 @@
class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
-
- before_action :authorize_manage_builds!, except: [:index, :show, :status]
-
- layout "project"
+ before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry]
+ before_action :authorize_update_build!, except: [:index, :show, :status]
+ layout 'project'
def index
@scope = params[:scope]
@@ -23,7 +22,6 @@ class Projects::BuildsController < Projects::ApplicationController
def cancel_all
@project.builds.running_or_pending.each(&:cancel)
-
redirect_to namespace_project_builds_path(project.namespace, project)
end
@@ -46,20 +44,18 @@ class Projects::BuildsController < Projects::ApplicationController
end
build = Ci::Build.retry(@build)
-
redirect_to build_path(build)
end
- def status
- render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
- end
-
def cancel
@build.cancel
-
redirect_to build_path(@build)
end
+ def status
+ render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
+ end
+
private
def build
@@ -69,10 +65,4 @@ class Projects::BuildsController < Projects::ApplicationController
def build_path(build)
namespace_project_build_path(build.project.namespace, build.project, build)
end
-
- def authorize_manage_builds!
- unless can?(current_user, :manage_builds, project)
- return render_404
- end
- end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 5a084e123a1..21f4d9f44ec 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -4,10 +4,10 @@
class Projects::CommitController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
- before_action :authorize_download_code!, except: [:cancel_builds]
- before_action :authorize_manage_builds!, only: [:cancel_builds]
+ before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds]
+ before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
+ before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit
- before_action :authorize_manage_builds!, only: [:cancel_builds, :retry_builds]
before_action :define_show_vars, only: [:show, :builds]
def show
@@ -79,10 +79,4 @@ class Projects::CommitController < Projects::ApplicationController
@statuses = ci_commit.statuses if ci_commit
end
-
- def authorize_manage_builds!
- unless can?(current_user, :manage_builds, project)
- return render_404
- end
- end
end
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index bf5b54c8cb7..1420b96840c 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -21,6 +21,9 @@ class Projects::CommitsController < Projects::ApplicationController
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
+ @merge_request = @project.merge_requests.opened.
+ find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
+
respond_to do |format|
format.html
format.json { pager_json("projects/commits/_commits", @commits.size) }
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 7bbe75b3974..dc5d217f3e4 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -4,24 +4,23 @@ class Projects::CompareController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
+ before_action :assign_ref_vars, only: [:index, :show]
+ before_action :merge_request, only: [:index, :show]
def index
- @ref = Addressable::URI.unescape(params[:to])
end
def show
- base_ref = Addressable::URI.unescape(params[:from])
- @ref = head_ref = Addressable::URI.unescape(params[:to])
diff_options = { ignore_whitespace_change: true } if params[:w] == '1'
compare_result = CompareService.new.
- execute(@project, head_ref, @project, base_ref, diff_options)
+ execute(@project, @head_ref, @project, @base_ref, diff_options)
if compare_result
@commits = Commit.decorate(compare_result.commits, @project)
@diffs = compare_result.diffs
- @commit = @project.commit(head_ref)
- @base_commit = @project.merge_base_commit(base_ref, head_ref)
+ @commit = @project.commit(@head_ref)
+ @base_commit = @project.merge_base_commit(@base_ref, @head_ref)
@diff_refs = [@base_commit, @commit]
@line_notes = []
end
@@ -31,4 +30,16 @@ class Projects::CompareController < Projects::ApplicationController
redirect_to namespace_project_compare_path(@project.namespace, @project,
params[:from], params[:to])
end
+
+ private
+
+ def assign_ref_vars
+ @base_ref = Addressable::URI.unescape(params[:from])
+ @ref = @head_ref = Addressable::URI.unescape(params[:to])
+ end
+
+ def merge_request
+ @merge_request ||= @project.merge_requests.opened.
+ find_by(source_project: @project, source_branch: @head_ref, target_branch: @base_ref)
+ end
end
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index e2785caa2fb..bedeb4a295c 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -1,5 +1,5 @@
class Projects::RunnerProjectsController < Projects::ApplicationController
- before_action :authorize_admin_project!
+ before_action :authorize_admin_build!
layout 'project_settings'
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 4993b2648a5..0dd2d6a99be 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -1,6 +1,6 @@
class Projects::RunnersController < Projects::ApplicationController
+ before_action :authorize_admin_build!
before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
- before_action :authorize_admin_project!
layout 'project_settings'
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index 30adfad1daa..92359745cec 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -1,5 +1,5 @@
class Projects::TriggersController < Projects::ApplicationController
- before_action :authorize_admin_project!
+ before_action :authorize_admin_build!
layout 'project_settings'
diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb
index 10efafea9db..00234654578 100644
--- a/app/controllers/projects/variables_controller.rb
+++ b/app/controllers/projects/variables_controller.rb
@@ -1,5 +1,5 @@
class Projects::VariablesController < Projects::ApplicationController
- before_action :authorize_admin_project!
+ before_action :authorize_admin_build!
layout 'project_settings'
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 4df5095bd94..14ca7426c2f 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -227,6 +227,7 @@ class ProjectsController < ApplicationController
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
+ :public_builds,
)
end
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
index 1ed8c710f77..43a29c96bca 100644
--- a/app/helpers/broadcast_messages_helper.rb
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -3,7 +3,7 @@ module BroadcastMessagesHelper
return unless message.present?
content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do
- icon('bullhorn') << ' ' << message.message
+ icon('bullhorn') << ' ' << render_broadcast_message(message.message)
end
end
@@ -31,4 +31,8 @@ module BroadcastMessagesHelper
'Pending'
end
end
+
+ def render_broadcast_message(message)
+ Banzai.render(message, pipeline: :broadcast_message).html_safe
+ end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index dd49283089d..cc1c61e9ec0 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -141,7 +141,7 @@ module ProjectsHelper
nav_tabs << :merge_requests
end
- if project.builds_enabled? && can?(current_user, :read_build, project)
+ if can?(current_user, :read_build, project)
nav_tabs << :builds
end
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index bc36434f549..41ae4048992 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -33,7 +33,7 @@ module SnippetsHelper
# surrounding code.
#
# @returns Array, unique and sorted.
- def matching_lines(lined_content, surrounding_lines)
+ def matching_lines(lined_content, surrounding_lines, query)
used_lines = []
lined_content.each_with_index do |line, line_number|
used_lines.concat bounded_line_numbers(
@@ -51,9 +51,9 @@ module SnippetsHelper
# surrounding_lines() worth of unmatching lines.
#
# @returns a hash with {snippet_object, snippet_chunks:{data,start_line}}
- def chunk_snippet(snippet, surrounding_lines = 3)
+ def chunk_snippet(snippet, query, surrounding_lines = 3)
lined_content = snippet.content.split("\n")
- used_lines = matching_lines(lined_content, surrounding_lines)
+ used_lines = matching_lines(lined_content, surrounding_lines, query)
snippet_chunk = []
snippet_chunks = []
diff --git a/app/models/ability.rb b/app/models/ability.rb
index ab59a3506a2..a866eadeebb 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -5,17 +5,18 @@ class Ability
return [] unless user.is_a?(User)
return [] if user.blocked?
- case subject.class.name
- when "Project" then project_abilities(user, subject)
- when "Issue" then issue_abilities(user, subject)
- when "Note" then note_abilities(user, subject)
- when "ProjectSnippet" then project_snippet_abilities(user, subject)
- when "PersonalSnippet" then personal_snippet_abilities(user, subject)
- when "MergeRequest" then merge_request_abilities(user, subject)
- when "Group" then group_abilities(user, subject)
- when "Namespace" then namespace_abilities(user, subject)
- when "GroupMember" then group_member_abilities(user, subject)
- when "ProjectMember" then project_member_abilities(user, subject)
+ case subject
+ when CommitStatus then commit_status_abilities(user, subject)
+ when Project then project_abilities(user, subject)
+ when Issue then issue_abilities(user, subject)
+ when Note then note_abilities(user, subject)
+ when ProjectSnippet then project_snippet_abilities(user, subject)
+ when PersonalSnippet then personal_snippet_abilities(user, subject)
+ when MergeRequest then merge_request_abilities(user, subject)
+ when Group then group_abilities(user, subject)
+ when Namespace then namespace_abilities(user, subject)
+ when GroupMember then group_member_abilities(user, subject)
+ when ProjectMember then project_member_abilities(user, subject)
else []
end.concat(global_abilities(user))
end
@@ -25,6 +26,8 @@ class Ability
case true
when subject.is_a?(PersonalSnippet)
anonymous_personal_snippet_abilities(subject)
+ when subject.is_a?(CommitStatus)
+ anonymous_commit_status_abilities(subject)
when subject.is_a?(Project) || subject.respond_to?(:project)
anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group)
@@ -52,16 +55,26 @@ class Ability
:read_project_member,
:read_merge_request,
:read_note,
- :read_build,
+ :read_commit_status,
:download_code
]
+ # Allow to read builds by anonymous user if guests are allowed
+ rules << :read_build if project.public_builds?
+
rules - project_disabled_features_rules(project)
else
[]
end
end
+ def anonymous_commit_status_abilities(subject)
+ rules = anonymous_project_abilities(subject.project)
+ # If subject is Ci::Build which inherits from CommitStatus filter the abilities
+ rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
+ rules
+ end
+
def anonymous_group_abilities(subject)
group = if subject.is_a?(Group)
subject
@@ -113,6 +126,9 @@ class Ability
if project.public? || project.internal?
rules.push(*public_project_rules)
+
+ # Allow to read builds for internal projects
+ rules << :read_build if project.public_builds?
end
if project.owner == user || user.admin?
@@ -134,7 +150,8 @@ class Ability
def public_project_rules
@public_project_rules ||= project_guest_rules + [
:download_code,
- :fork_project
+ :fork_project,
+ :read_commit_status,
]
end
@@ -149,7 +166,6 @@ class Ability
:read_project_member,
:read_merge_request,
:read_note,
- :read_build,
:create_project,
:create_issue,
:create_note
@@ -158,24 +174,26 @@ class Ability
def project_report_rules
@project_report_rules ||= project_guest_rules + [
- :create_commit_status,
- :read_commit_statuses,
- :read_build_artifacts,
:download_code,
:fork_project,
:create_project_snippet,
:update_issue,
:admin_issue,
- :admin_label
+ :admin_label,
+ :read_commit_status,
+ :read_build,
]
end
def project_dev_rules
@project_dev_rules ||= project_report_rules + [
:admin_merge_request,
+ :create_commit_status,
+ :update_commit_status,
+ :create_build,
+ :update_build,
:create_merge_request,
:create_wiki,
- :manage_builds,
:push_code
]
end
@@ -201,7 +219,9 @@ class Ability
:admin_merge_request,
:admin_note,
:admin_wiki,
- :admin_project
+ :admin_project,
+ :admin_commit_status,
+ :admin_build
]
end
@@ -240,6 +260,10 @@ class Ability
rules += named_abilities('wiki')
end
+ unless project.builds_enabled
+ rules += named_abilities('build')
+ end
+
rules
end
@@ -376,6 +400,22 @@ class Ability
rules
end
+ def commit_status_abilities(user, subject)
+ rules = project_abilities(user, subject.project)
+ # If subject is Ci::Build which inherits from CommitStatus filter the abilities
+ rules = filter_build_abilities(rules) if subject.is_a?(Ci::Build)
+ rules
+ end
+
+ def filter_build_abilities(rules)
+ # If we can't read build we should also not have that
+ # ability when looking at this in context of commit_status
+ %w(read create update admin).each do |rule|
+ rules.delete(:"#{rule}_commit_status") unless rules.include?(:"#{rule}_build")
+ end
+ rules
+ end
+
def abilities
@abilities ||= begin
abilities = Six.new
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 9cafc78f761..fa48fe5b9e4 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -43,6 +43,7 @@
# metrics_port :integer default(8089)
# sentry_enabled :boolean default(FALSE)
# sentry_dsn :string
+# email_author_in_body :boolean default(FALSE)
#
class ApplicationSetting < ActiveRecord::Base
@@ -92,6 +93,10 @@ class ApplicationSetting < ActiveRecord::Base
presence: true,
if: :akismet_enabled
+ validates :max_attachment_size,
+ presence: true,
+ numericality: { only_integer: true, greater_than: 0 }
+
validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil?
value.each do |level|
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 04650a9e67a..cf6aa592e2a 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -126,7 +126,7 @@ module Issuable
end
def to_hook_data(user)
- {
+ hook_data = {
object_kind: self.class.name.underscore,
user: user.hook_attrs,
repository: {
@@ -137,6 +137,9 @@ module Issuable
},
object_attributes: hook_attrs
}
+ hook_data.merge!(assignee: assignee.hook_attrs) if assignee
+
+ hook_data
end
def label_names
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index ddc476447ca..345b9a26d4e 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -137,6 +137,7 @@ class MergeRequest < ActiveRecord::Base
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :in_projects, ->(project_ids) { where("source_project_id in (:project_ids) OR target_project_id in (:project_ids)", project_ids: project_ids) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
+ scope :opened, -> { with_state(:opened) }
scope :merged, -> { with_state(:merged) }
scope :closed, -> { with_state(:closed) }
scope :closed_and_merged, -> { with_states(:closed, :merged) }
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index c9a0ad8b9b6..9c4476c768e 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -34,7 +34,7 @@ class Milestone < ActiveRecord::Base
scope :closed, -> { with_state(:closed) }
scope :of_projects, ->(ids) { where(project_id: ids) }
- validates :title, presence: true
+ validates :title, presence: true, uniqueness: { scope: :project_id }
validates :project, presence: true
strip_attributes :title
diff --git a/app/models/project.rb b/app/models/project.rb
index 043f08b9a13..f11c6d7c6be 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -790,6 +790,8 @@ class Project < ActiveRecord::Base
def change_head(branch)
# Cached divergent commit counts are based on repository head
repository.expire_branch_cache
+ repository.expire_root_ref_cache
+
gitlab_shell.update_repository_head(self.path_with_namespace, branch)
reload_default_branch
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index e813c946bc1..7f0047a002e 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -44,7 +44,9 @@ class Repository
end
def empty?
- raw_repository.empty?
+ return @empty unless @empty.nil?
+
+ @empty = cache.fetch(:empty?) { raw_repository.empty? }
end
#
@@ -57,7 +59,11 @@ class Repository
# This method return true if repository contains some content visible in project page.
#
def has_visible_content?
- raw_repository.branch_count > 0
+ return @has_visible_content unless @has_visible_content.nil?
+
+ @has_visible_content = cache.fetch(:has_visible_content?) do
+ raw_repository.branch_count > 0
+ end
end
def commit(id = 'HEAD')
@@ -78,7 +84,8 @@ class Repository
offset: offset,
# --follow doesn't play well with --skip. See:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
- follow: false
+ follow: false,
+ skip_merges: skip_merges
}
commits = Gitlab::Git::Commit.where(options)
@@ -199,12 +206,6 @@ class Repository
readme version contribution_guide changelog license)
end
- def branch_cache_keys
- branches.map do |branch|
- :"diverging_commit_counts_#{branch.name}"
- end
- end
-
def build_cache
cache_keys.each do |key|
unless cache.exist?(key)
@@ -229,20 +230,39 @@ class Repository
@branches = nil
end
- def expire_cache
+ def expire_cache(branch_name = nil)
cache_keys.each do |key|
cache.expire(key)
end
- expire_branch_cache
+ expire_branch_cache(branch_name)
end
- def expire_branch_cache
- branches.each do |branch|
- cache.expire(:"diverging_commit_counts_#{branch.name}")
+ def expire_branch_cache(branch_name = nil)
+ # When we push to the root branch we have to flush the cache for all other
+ # branches as their statistics are based on the commits relative to the
+ # root branch.
+ if !branch_name || branch_name == root_ref
+ branches.each do |branch|
+ cache.expire(:"diverging_commit_counts_#{branch.name}")
+ end
+ # In case a commit is pushed to a non-root branch we only have to flush the
+ # cache for said branch.
+ else
+ cache.expire(:"diverging_commit_counts_#{branch_name}")
end
end
+ def expire_root_ref_cache
+ cache.expire(:root_ref)
+ @root_ref = nil
+ end
+
+ def expire_has_visible_content_cache
+ cache.expire(:has_visible_content?)
+ @has_visible_content = nil
+ end
+
def rebuild_cache
cache_keys.each do |key|
cache.expire(key)
@@ -480,7 +500,7 @@ class Repository
end
def root_ref
- @root_ref ||= raw_repository.root_ref
+ @root_ref ||= cache.fetch(:root_ref) { raw_repository.root_ref }
end
def commit_dir(user, path, message, branch)
diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb
index f469b13e902..005a5c4661c 100644
--- a/app/services/ci/image_for_build_service.rb
+++ b/app/services/ci/image_for_build_service.rb
@@ -1,28 +1,23 @@
module Ci
class ImageForBuildService
- def execute(project, params)
- sha = params[:sha]
- sha ||=
- if params[:ref]
- project.commit(params[:ref]).try(:sha)
- end
+ def execute(project, opts)
+ sha = opts[:sha] || ref_sha(project, opts[:ref])
commit = project.ci_commits.ordered.find_by(sha: sha)
image_name = image_for_commit(commit)
image_path = Rails.root.join('public/ci', image_name)
-
- OpenStruct.new(
- path: image_path,
- name: image_name
- )
+ OpenStruct.new(path: image_path, name: image_name)
end
private
+ def ref_sha(project, ref)
+ project.commit(ref).try(:sha) if ref
+ end
+
def image_for_commit(commit)
return 'build-unknown.svg' unless commit
-
'build-' + commit.status + ".svg"
end
end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index d7ea30bc315..e3bf14966c8 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -18,18 +18,23 @@ class GitPushService
def execute(project, user, oldrev, newrev, ref)
@project, @user = project, user
- project.repository.expire_cache
+ branch_name = Gitlab::Git.ref_name(ref)
+
+ project.repository.expire_cache(branch_name)
if push_remove_branch?(ref, newrev)
+ project.repository.expire_has_visible_content_cache
+
@push_commits = []
elsif push_to_new_branch?(ref, oldrev)
+ project.repository.expire_has_visible_content_cache
+
# Re-find the pushed commits.
if is_default_branch?(ref)
# Initial push to the default branch. Take the full history of that branch as "newly pushed".
@push_commits = project.repository.commits(newrev)
# Ensure HEAD points to the default branch in case it is not master
- branch_name = Gitlab::Git.ref_name(ref)
project.change_head(branch_name)
# Set protection on the default branch if configured
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index b4e3d96d405..b30dfd109ea 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -48,6 +48,16 @@
= f.check_box :version_check_enabled
Version check enabled
.form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :email_author_in_body do
+ = f.check_box :email_author_in_body
+ Include author name in notification email body
+ .help-block
+ Some email servers do not support overriding the email sender name.
+ Enable this option to include the name of the author of the issue,
+ merge request or comment in the email body instead.
+ .form-group
= f.label :admin_notification_email, class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :admin_notification_email, class: 'form-control'
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index 953b8b69368..5c9403fa0c2 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -1,6 +1,7 @@
.broadcast-message-preview{ style: broadcast_message_style(@broadcast_message) }
= icon('bullhorn')
- %span= @broadcast_message.message || "Your message here"
+ .js-broadcast-message-preview
+ = render_broadcast_message(@broadcast_message.message.presence || "Your message here")
= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-requires-input'} do |f|
-if @broadcast_message.errors.any?
@@ -10,7 +11,9 @@
.form-group
= f.label :message, class: 'control-label'
.col-sm-10
- = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true
+ = f.text_area :message, class: "form-control js-quick-submit js-autosize",
+ required: true,
+ data: { preview_path: preview_admin_broadcast_messages_path }
.form-group.js-toggle-colors-container
.col-sm-10.col-sm-offset-2
= link_to 'Customize colors', '#', class: 'js-toggle-colors-link'
diff --git a/app/views/admin/broadcast_messages/preview.js.haml b/app/views/admin/broadcast_messages/preview.js.haml
new file mode 100644
index 00000000000..fbc9453c72e
--- /dev/null
+++ b/app/views/admin/broadcast_messages/preview.js.haml
@@ -0,0 +1 @@
+$('.js-broadcast-message-preview').html("#{j(render_broadcast_message(@message))}");
diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml
index c395bd908c3..34d955568f2 100644
--- a/app/views/admin/builds/_build.html.haml
+++ b/app/views/admin/builds/_build.html.haml
@@ -4,7 +4,7 @@
= ci_status_with_icon(build.status)
%td.build-link
- - if build.target_url
+ - if can?(current_user, :read_build, project) && build.target_url
= link_to build.target_url do
%strong Build ##{build.id}
- else
@@ -60,10 +60,10 @@
%td
.pull-right
- - if current_user && can?(current_user, :read_build_artifacts, project) && build.artifacts?
+ - if can?(current_user, :read_build, project) && build.artifacts?
= link_to build.artifacts_download_url, title: 'Download artifacts' do
%i.fa.fa-download
- - if current_user && can?(current_user, :manage_builds, build.project)
+ - if can?(current_user, :update_build, build.project)
- if build.active?
- if build.cancel_url
= link_to build.cancel_url, method: :post, title: 'Cancel' do
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index d46998ec1e9..4bc761b3738 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -14,7 +14,7 @@
.nav-controls
= form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
- = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short', spellcheck: false, id: 'project-filter-form-field'
+ = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field'
= render 'explore/projects/dropdown'
- if current_user.can_create_project?
= link_to new_project_path, class: 'btn btn-new' do
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 66a4b535ae5..c248dbb695f 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,11 +1,3 @@
-.pull-left
- = form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
- .form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false
- = hidden_field_tag :sort, @sort
- .form-group
- = button_tag 'Search', class: "btn"
-
.pull-right.hidden-sm.hidden-xs
- if current_user
.dropdown.inline.append-right-10
diff --git a/app/views/explore/projects/_projects.html.haml b/app/views/explore/projects/_projects.html.haml
index 669079e9521..999a933390b 100644
--- a/app/views/explore/projects/_projects.html.haml
+++ b/app/views/explore/projects/_projects.html.haml
@@ -1,5 +1,5 @@
- if projects.any?
- .public-projects
+ .projects-list-holder
= render 'shared/projects/list', projects: projects
- else
.nothing-here-block
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index a829479bb38..9c16ab7e30f 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -1,10 +1,11 @@
-.projects-list-holder.prepend-top-default
- .input-group
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
- - if can? current_user, :create_projects, @group
- %span.input-group-btn
- = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new' do
- = icon('plus')
- New Project
+.top-area
+ .nav-controls
+ = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
+ = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'input-short project-filter-form-field form-control projects-list-filter', spellcheck: false, id: 'project-filter-form-field'
+ - if current_user && current_user.can_create_project?
+ = link_to new_project_path, class: 'btn btn-new' do
+ = icon('plus')
+ New Project
+.projects-list-holder
= render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index ebb3df7dca3..a0ba11b11a1 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -47,7 +47,7 @@
= render 'shared/event_filter'
- .content_list
+ .content_list{data: {href: events_group_path}}
= spinner
.tab-pane#projects
diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml
index 00cb4aa24cc..12ded41fbf2 100644
--- a/app/views/notify/_note_message.html.haml
+++ b/app/views/notify/_note_message.html.haml
@@ -1,2 +1,5 @@
+- if current_application_settings.email_author_in_body
+ %div
+ #{link_to @note.author_name, user_url(@note.author)} wrote:
%div
= markdown(@note.note, pipeline: :email)
diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml
index d3b799fca23..ad3ab2525bb 100644
--- a/app/views/notify/new_issue_email.html.haml
+++ b/app/views/notify/new_issue_email.html.haml
@@ -1,3 +1,6 @@
+- if current_application_settings.email_author_in_body
+ %div
+ #{link_to @issue.author_name, user_url(@issue.author)} wrote:
-if @issue.description
= markdown(@issue.description, pipeline: :email)
diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml
index 90ebdfc3fe2..23423e7d981 100644
--- a/app/views/notify/new_merge_request_email.html.haml
+++ b/app/views/notify/new_merge_request_email.html.haml
@@ -1,3 +1,6 @@
+- if current_application_settings.email_author_in_body
+ %div
+ #{link_to @merge_request.author_name, user_url(@merge_request.author)} wrote:
%p.details
!= merge_path_description(@merge_request, '&rarr;')
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 630d0286f25..5e3bd14565e 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -22,7 +22,7 @@
= number_with_delimiter(@all_builds.finished.count(:id))
.nav-controls
- - if can?(current_user, :manage_builds, @project)
+ - if can?(current_user, :update_build, @project)
- if @all_builds.running_or_pending.any?
= link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index ba1fdc6f0e7..ca1441a20d8 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -89,8 +89,7 @@
Test coverage
%h1 #{@build.coverage}%
- - if current_user && can?(current_user, :read_build_artifacts, @project) && @build.artifacts?
-
+ - if can?(current_user, :read_build, @project) && @build.artifacts?
.build-widget.artifacts
%h4.title Build artifacts
.center
@@ -102,7 +101,7 @@
.build-widget
%h4.title
Build ##{@build.id}
- - if current_user && can?(current_user, :manage_builds, @project)
+ - if can?(current_user, :update_build, @project)
.pull-right
- if @build.cancel_url
= link_to "Cancel", @build.cancel_url, class: 'btn btn-sm btn-danger', method: :post
diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml
index 329aaa0bb8b..befad27666c 100644
--- a/app/views/projects/commit/_builds.html.haml
+++ b/app/views/projects/commit/_builds.html.haml
@@ -1,6 +1,6 @@
.gray-content-block.middle-block
.pull-right
- - if can?(current_user, :manage_builds, @ci_commit.project)
+ - if can?(current_user, :update_build, @ci_commit.project)
- if @ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
index 2e3c956ddc4..a3449d1ae05 100644
--- a/app/views/projects/commit_statuses/_commit_status.html.haml
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -1,6 +1,6 @@
%tr.commit_status
%td.status
- - if commit_status.target_url
+ - if can?(current_user, :read_commit_status, commit_status) && commit_status.target_url
= link_to commit_status.target_url, class: "ci-status ci-#{commit_status.status}" do
= ci_icon_for_status(commit_status.status)
= commit_status.status
@@ -8,14 +8,14 @@
= ci_status_with_icon(commit_status.status)
%td.commit_status-link
- - if commit_status.target_url
+ - if can?(current_user, :read_commit_status, commit_status) && commit_status.target_url
= link_to commit_status.target_url do
%strong ##{commit_status.id}
- else
%strong ##{commit_status.id}
- if commit_status.show_warning?
- %i.fa.fa-warning.text-warning
+ %i.fa.fa-warning.text-warning{data: { toggle: "tooltip" }, title: "This build is stuck, open it to know more"}
- if defined?(commit_sha) && commit_sha
%td
@@ -66,10 +66,10 @@
%td
.pull-right
- - if current_user && can?(current_user, :read_build_artifacts, commit_status.project) && commit_status.artifacts_download_url
+ - if can?(current_user, :read_commit_status, commit_status) && commit_status.artifacts_download_url
= link_to commit_status.artifacts_download_url, title: 'Download artifacts' do
%i.fa.fa-download
- - if current_user && can?(current_user, :manage_builds, commit_status.project)
+ - if can?(current_user, :update_commit_status, commit_status)
- if commit_status.active?
- if commit_status.cancel_url
= link_to commit_status.cancel_url, method: :post, title: 'Cancel' do
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index ede64d47ab3..c52cf25d40a 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -11,7 +11,10 @@
= render 'shared/ref_switcher', destination: 'commits'
.block-controls.hidden-xs.hidden-sm
- - if create_mr_button?(@repository.root_ref, @ref)
+ - if @merge_request.present?
+ .control
+ = link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn'
+ - elsif create_mr_button?(@repository.root_ref, @ref)
.control
= link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do
= icon('plus')
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index efc25eda26b..4ab81f3635c 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -13,12 +13,13 @@
= text_field_tag :to, params[:to], class: "form-control", required: true
&nbsp;
= button_tag "Compare", class: "btn btn-create commits-compare-btn"
- - if create_mr_button?
+ - if @merge_request.present?
+ = link_to "View Open Merge Request", namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'prepend-left-10 btn'
+ - elsif create_mr_button?
= link_to create_mr_path, class: 'prepend-left-10 btn' do
= icon("plus")
Create Merge Request
-
:javascript
var availableTags = #{@project.repository.ref_names.to_json};
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 8a99aceef7f..fdcb6987471 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -130,6 +130,7 @@
%strong git fetch
%br
%span.descr Faster
+
.form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label'
.col-sm-10
@@ -158,6 +159,13 @@
phpunit --coverage-text --colors=never (PHP) -
%code ^\s*Lines:\s*\d+.\d+\%
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :public_builds do
+ = f.check_box :public_builds
+ %strong Public builds
+ .help-block Allow everyone to access builds for Public and Internal projects
%fieldset.features
%legend
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index acb2353d3ca..42fa6fdb782 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -6,7 +6,7 @@
== #{pluralize(@all_forks.size, 'fork')}: #{full_count_title}
.nav-controls
- = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter form-control input-short',
+ = search_field_tag :filter_projects, nil, placeholder: 'Search forks', class: 'projects-list-filter project-filter-form-field form-control input-short',
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
@@ -29,14 +29,15 @@
= link_to page_filter_path(sort: sort_value_oldest_updated, without: excluded_filters) do
= sort_title_oldest_updated
- - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
- = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
- = icon('code-fork fw')
- Fork
- - else
- = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
- = icon('code-fork fw')
- Fork
+ - if current_user && can?(current_user, :fork_project, @project)
+ - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
+ = icon('code-fork fw')
+ Fork
+ - else
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
+ = icon('code-fork fw')
+ Fork
.projects-list-holder
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index 3c5edf4b033..baaa2caa6de 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,7 +1,7 @@
%article.file-holder.readme-holder
.file-title
= blob_icon readme.mode, readme.name
- = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, @path, readme.name)) do
%strong
= readme.name
.file-content.wiki
diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml
index e80dffc1ced..efe1e6f24c2 100644
--- a/app/views/projects/variables/show.html.haml
+++ b/app/views/projects/variables/show.html.haml
@@ -3,9 +3,11 @@
Secret Variables
%p.light
- These variables will be set to environment by the runner and will be hidden in the build log.
+ These variables will be set to environment by the runner.
%br
So you can use them for passwords, secret keys or whatever you want.
+ %br
+ The value of the variable can be visible in build log if explicitly asked to do so.
%hr
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index dcd61199717..6b77d24f50c 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -1,46 +1,50 @@
+- snippet_blob = chunk_snippet(snippet_blob, @search_term)
+- snippet = snippet_blob[:snippet_object]
+- snippet_chunks = snippet_blob[:snippet_chunks]
+
.search-result-row
%span
- = snippet_blob[:snippet_object].title
+ = snippet.title
by
- = link_to user_snippets_path(snippet_blob[:snippet_object].author) do
- = image_tag avatar_icon(snippet_blob[:snippet_object].author_email), class: "avatar avatar-inline s16", alt: ''
- = snippet_blob[:snippet_object].author_name
- %span.light #{time_ago_with_tooltip(snippet_blob[:snippet_object].created_at)}
+ = link_to user_snippets_path(snippet.author) do
+ = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: ''
+ = snippet.author_name
+ %span.light #{time_ago_with_tooltip(snippet.created_at)}
%h4.snippet-title
- - snippet_path = reliable_snippet_path(snippet_blob[:snippet_object])
+ - snippet_path = reliable_snippet_path(snippet)
= link_to snippet_path do
.file-holder
.file-title
%i.fa.fa-file
- %strong= snippet_blob[:snippet_object].file_name
- - if markup?(snippet_blob[:snippet_object].file_name)
+ %strong= snippet.file_name
+ - if markup?(snippet.file_name)
.file-content.wiki
- - snippet_blob[:snippet_chunks].each do |snippet|
- - unless snippet[:data].empty?
- = render_markup(snippet_blob[:snippet_object].file_name, snippet[:data])
+ - snippet_chunks.each do |chunk|
+ - unless chunk[:data].empty?
+ = render_markup(snippet.file_name, chunk[:data])
- else
.file-content.code
.nothing-here-block Empty file
- else
.file-content.code.js-syntax-highlight
.line-numbers
- - snippet_blob[:snippet_chunks].each do |snippet|
- - unless snippet[:data].empty?
- - snippet[:data].lines.to_a.size.times do |index|
- - offset = defined?(snippet[:start_line]) ? snippet[:start_line] : 1
+ - snippet_chunks.each do |chunk|
+ - unless chunk[:data].empty?
+ - chunk[:data].lines.to_a.size.times do |index|
+ - offset = defined?(chunk[:start_line]) ? chunk[:start_line] : 1
- i = index + offset
= link_to snippet_path+"#L#{i}", id: "L#{i}", rel: "#L#{i}", class: "diff-line-num" do
%i.fa.fa-link
= i
- - unless snippet == snippet_blob[:snippet_chunks].last
+ - unless snippet == snippet_chunks.last
%a.diff-line-num
= "."
%pre.code
%code
- - snippet_blob[:snippet_chunks].each do |snippet|
- - unless snippet[:data].empty?
- = snippet[:data]
- - unless snippet == snippet_blob[:snippet_chunks].last
+ - snippet_chunks.each do |chunk|
+ - unless chunk[:data].empty?
+ = chunk[:data]
+ - unless chunk == snippet_chunks.last
%a
= "..."
- else
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
index 285af56ad73..627814bcfae 100644
--- a/app/views/shared/_import_form.html.haml
+++ b/app/views/shared/_import_form.html.haml
@@ -11,6 +11,6 @@
%li
If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.
%li
- The import will time out after 4 minutes. For big repositories, use a clone/push combination.
+ The import will time out after 15 minutes. For repositories that take longer, use a clone/push combination.
%li
To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}.
diff --git a/app/views/shared/_logo.svg b/app/views/shared/_logo.svg
index 3d279ec228c..b07f1c5603e 100644
--- a/app/views/shared/_logo.svg
+++ b/app/views/shared/_logo.svg
@@ -1,21 +1,9 @@
-<svg width="36px" height="36px" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="tanuki-logo">
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
- <g id="logo" sketch:type="MSLayerGroup" transform="translate(0.000000, 10.000000)">
- <g id="Page-1" sketch:type="MSShapeGroup">
- <g id="Fill-1-+-Group-24">
- <g id="Group-24">
- <g id="Group">
- <path id="tanuki-right-ear" d="M12.2685,74.7342 L66.4215,74.7342 L43.1485,3.1092 C41.9515,-0.5768 36.7375,-0.5758 35.5405,3.1092 L12.2685,74.7342 L12.2685,74.7342 Z" fill="#E24329" class="tanuki-shape"></path>
- <path id="tanuki-right-cheek" d="M12.2685,74.7341 L12.2685,74.7341 L0.5265,110.8731 C-0.5445,114.1691 0.6285,117.7801 3.4325,119.8171 L105.0615,193.6551 L12.2685,74.7341 L12.2685,74.7341 Z" fill="#FCA326" class="tanuki-shape"></path>
- <path id="tanuki-right-eye" d="M105.0614,193.6548 L66.4214,74.7338 L12.2684,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" fill="#FC6D26" class="tanuki-shape"></path>
- <path id="tanuki-nose" d="M105.0614,193.655 L105.0614,193.655 L143.7014,74.734 L66.4214,74.734 L105.0614,193.655 L105.0614,193.655 Z" fill="#E24329" class="tanuki-shape"></path>
- <path id="tanuki-left-eye" d="M105.0614,193.6548 L143.7014,74.7338 L197.8544,74.7338 L105.0614,193.6548 L105.0614,193.6548 Z" fill="#FC6D26" class="tanuki-shape"></path>
- <path id="tanuki-left-cheek" d="M197.8544,74.7341 L197.8544,74.7341 L209.5964,110.8731 C210.6674,114.1691 209.4944,117.7801 206.6904,119.8171 L105.0614,193.6551 L197.8544,74.7341 L197.8544,74.7341 Z" fill="#FCA326" class="tanuki-shape"></path>
- <path id="tanuki-left-ear" d="M197.8544,74.7342 L143.7014,74.7342 L166.9744,3.1092 C168.1714,-0.5768 173.3854,-0.5758 174.5824,3.1092 L197.8544,74.7342 L197.8544,74.7342 Z" fill="#E24329" class="tanuki-shape"></path>
- </g>
- </g>
- </g>
- </g>
- </g>
- </g>
+<svg width="36" height="36" id="tanuki-logo">
+ <path id="tanuki-right-ear" class="tanuki-shape" fill="#e24329" d="M2 14l9.38 9v-9l-4-12.28c-.205-.632-1.176-.632-1.38 0z"/>
+ <path id="tanuki-left-ear" class="tanuki-shape" fill="#e24329" d="M34 14l-9.38 9v-9l4-12.28c.205-.632 1.176-.632 1.38 0z"/>
+ <path id="tanuki-nose" class="tanuki-shape" fill="#e24329" d="M18,34.38 3,14 33,14 Z"/>
+ <path id="tanuki-right-eye" class="tanuki-shape" fill="#fc6d26" d="M18,34.38 11.38,14 2,14 6,25Z"/>
+ <path id="tanuki-left-eye" class="tanuki-shape" fill="#fc6d26" d="M18,34.38 24.62,14 34,14 30,25Z"/>
+ <path id="tanuki-right-cheek" class="tanuki-shape" fill="#fca326" d="M2 14L.1 20.16c-.18.565 0 1.2.5 1.56l17.42 12.66z"/>
+ <path id="tanuki-left-cheek" class="tanuki-shape" fill="#fca326" d="M34 14l1.9 6.16c.18.565 0 1.2-.5 1.56L18 34.38z"/>
</svg>
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 67edb264b7e..75684b972f1 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -21,9 +21,10 @@
#{projects_limit} of #{pluralize(projects.count, 'project')} displayed.
= link_to '#', class: 'js-expand' do
Show all
- = paginate projects, theme: "gitlab" if !projects.kind_of?(Array)
+ = paginate projects, theme: "gitlab" if projects.respond_to? :total_pages
- else
%h3 No projects found
:javascript
new ProjectsList();
+ Dashboard.init();
diff --git a/config/application.rb b/config/application.rb
index d255ff0719f..1e9ec74cdbf 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -31,7 +31,7 @@ module Gitlab
config.encoding = "utf-8"
# Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters.push(:password, :password_confirmation, :private_token, :otp_attempt)
+ config.filter_parameters.push(:password, :password_confirmation, :private_token, :otp_attempt, :variables)
# Enable escaping HTML in JSON.
config.active_support.escape_html_entities_in_json = true
@@ -52,17 +52,11 @@ module Gitlab
config.action_view.sanitized_allowed_protocols = %w(smb)
- # Relative url support
- # Uncomment and customize the last line to run in a non-root path
- # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this.
- # Note that following settings need to be changed for this to work.
- # 1) In your application.rb file: config.relative_url_root = "/gitlab"
- # 2) In your gitlab.yml file: relative_url_root: /gitlab
- # 3) In your unicorn.rb: ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
- # 4) In ../gitlab-shell/config.yml: gitlab_url: "http://127.0.0.1/gitlab"
- # 5) In lib/support/nginx/gitlab : do not use asset gzipping, remove block starting with "location ~ ^/(assets)/"
- #
- # To update the path, run: sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
+ # Relative URL support
+ # WARNING: We recommend using an FQDN to host GitLab in a root path instead
+ # of using a relative URL.
+ # Documentation: http://doc.gitlab.com/ce/install/relative_url.html
+ # Uncomment and customize the following line to run in a non-root path
#
# config.relative_url_root = "/gitlab"
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index d6e2c9380a5..faf05ecd466 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -38,8 +38,12 @@ production: &base
# Otherwise, ssh host will be set to the `host:` value above
# ssh_host: ssh.host_example.com
- # WARNING: See config/application.rb under "Relative url support" for the list of
- # other files that need to be changed for relative url support
+ # Relative URL support
+ # WARNING: We recommend using an FQDN to host GitLab in a root path instead
+ # of using a relative URL.
+ # Documentation: http://doc.gitlab.com/ce/install/relative_url.html
+ # Uncomment and customize the following line to run in a non-root path
+ #
# relative_url_root: /gitlab
# Uncomment and customize if you can't use the default user to run GitLab (default: 'git')
diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample
index ec182502d4e..2287a76fca7 100644
--- a/config/initializers/smtp_settings.rb.sample
+++ b/config/initializers/smtp_settings.rb.sample
@@ -12,7 +12,7 @@ if Rails.env.production?
ActionMailer::Base.smtp_settings = {
address: "email.server.com",
- port: 456,
+ port: 465,
user_name: "smtp",
password: "123456",
domain: "gitlab.company.com",
diff --git a/config/routes.rb b/config/routes.rb
index 034bfaf1bcd..507bcbc53d7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -227,7 +227,10 @@ Rails.application.routes.draw do
get :test
end
- resources :broadcast_messages, only: [:index, :edit, :create, :update, :destroy]
+ resources :broadcast_messages, only: [:index, :edit, :create, :update, :destroy] do
+ post :preview, on: :collection
+ end
+
resource :logs, only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
@@ -349,6 +352,7 @@ Rails.application.routes.draw do
get :issues
get :merge_requests
get :projects
+ get :events
end
scope module: :groups do
@@ -604,7 +608,7 @@ Rails.application.routes.draw do
resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :destroy]
- resources :builds, only: [:index, :show] do
+ resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do
collection do
post :cancel_all
end
@@ -693,6 +697,12 @@ Rails.application.routes.draw do
end
resources :runner_projects, only: [:create, :destroy]
+ resources :badges, only: [], path: 'badges/*ref',
+ constraints: { ref: Gitlab::Regex.git_reference_regex } do
+ collection do
+ get :build, constraints: { format: /svg/ }
+ end
+ end
end
end
end
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index b937b092789..e5058cebce8 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -10,9 +10,12 @@
# Note: If you change this file in a Merge Request, please also create a
# Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
-#
-# WARNING: See config/application.rb under "Relative url support" for the list of
-# other files that need to be changed for relative url support
+
+# Relative URL support
+# WARNING: We recommend using an FQDN to host GitLab in a root path instead
+# of using a relative URL.
+# Documentation: http://doc.gitlab.com/ce/install/relative_url.html
+# Uncomment and customize the following line to run in a non-root path
#
# ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
diff --git a/db/fixtures/development/14_builds.rb b/db/fixtures/development/14_builds.rb
index 03a12323845..e3ca2b4eea3 100644
--- a/db/fixtures/development/14_builds.rb
+++ b/db/fixtures/development/14_builds.rb
@@ -1,24 +1,13 @@
class Gitlab::Seeder::Builds
- BUILD_STATUSES = %w(running pending success failed canceled)
-
def initialize(project)
@project = project
end
def seed!
ci_commits.each do |ci_commit|
- build = Ci::Build.new(build_attributes_for(ci_commit))
-
- artifacts_cache_file(artifacts_archive_path) do |file|
- build.artifacts_file = file
- end
-
- artifacts_cache_file(artifacts_metadata_path) do |file|
- build.artifacts_metadata = file
- end
-
begin
- build.save!
+ build_create!(ci_commit, name: 'test build 1')
+ build_create!(ci_commit, status: 'success', name: 'test build 2')
print '.'
rescue ActiveRecord::RecordInvalid
print 'F'
@@ -36,6 +25,28 @@ class Gitlab::Seeder::Builds
[]
end
+ def build_create!(ci_commit, opts = {})
+ attributes = build_attributes_for(ci_commit).merge(opts)
+ build = Ci::Build.new(attributes)
+
+ if %w(success failed).include?(build.status)
+ artifacts_cache_file(artifacts_archive_path) do |file|
+ build.artifacts_file = file
+ end
+
+ artifacts_cache_file(artifacts_metadata_path) do |file|
+ build.artifacts_metadata = file
+ end
+ end
+
+ build.save!
+
+ if %w(running success failed).include?(build.status)
+ # We need to set build trace after saving a build (id required)
+ build.trace = FFaker::Lorem.paragraphs(6).join("\n\n")
+ end
+ end
+
def build_attributes_for(ci_commit)
{ name: 'test build', commands: "$ build command",
stage: 'test', stage_idx: 1, ref: 'master',
@@ -49,7 +60,7 @@ class Gitlab::Seeder::Builds
end
def build_status
- BUILD_STATUSES.sample
+ Ci::Build::AVAILABLE_STATUSES.sample
end
def artifacts_archive_path
diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb
index b0c0b6450f6..308b0528c9b 100644
--- a/db/fixtures/production/001_admin.rb
+++ b/db/fixtures/production/001_admin.rb
@@ -6,8 +6,10 @@ else
expire_time = nil
end
+email = ENV['GITLAB_ROOT_EMAIL'].presence || 'admin@example.com'
+
admin = User.create(
- email: "admin@example.com",
+ email: email,
name: "Administrator",
username: 'root',
password: password,
diff --git a/db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb b/db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb
new file mode 100644
index 00000000000..d50791410f9
--- /dev/null
+++ b/db/migrate/20160121030729_add_email_author_in_body_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddEmailAuthorInBodyToApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :email_author_in_body, :boolean, default: false
+ end
+end
diff --git a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
index 091de54978b..d3ea956952e 100644
--- a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
+++ b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb
@@ -48,7 +48,7 @@ class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration
end
def projects_with_dot_atom
- select_all("SELECT p.id, p.path, n.path as namespace_path, n.id as namespace_id FROM projects p inner join namespaces n on n.id = p.namespace_id WHERE lower(p.path) LIKE '%.atom'")
+ select_all("SELECT p.id, p.path, n.path as namespace_path, n.id as namespace_id FROM projects p inner join namespaces n on n.id = p.namespace_id WHERE p.path LIKE '%.atom'")
end
def up
diff --git a/db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb b/db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb
new file mode 100644
index 00000000000..793984343b4
--- /dev/null
+++ b/db/migrate/20160202164642_add_allow_guest_to_access_builds_project.rb
@@ -0,0 +1,5 @@
+class AddAllowGuestToAccessBuildsProject < ActiveRecord::Migration
+ def change
+ add_column :projects, :public_builds, :boolean, default: true, null: false
+ end
+end
diff --git a/db/migrate/20160209130428_add_index_to_snippet.rb b/db/migrate/20160209130428_add_index_to_snippet.rb
new file mode 100644
index 00000000000..95d5719be59
--- /dev/null
+++ b/db/migrate/20160209130428_add_index_to_snippet.rb
@@ -0,0 +1,5 @@
+class AddIndexToSnippet < ActiveRecord::Migration
+ def change
+ add_index :snippets, :updated_at
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d4710346b82..689a8c3ecc5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160129135155) do
+ActiveRecord::Schema.define(version: 20160209130428) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -66,6 +66,7 @@ ActiveRecord::Schema.define(version: 20160129135155) do
t.string "sentry_dsn"
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
+ t.boolean "email_author_in_body", default: false
end
create_table "audit_events", force: :cascade do |t|
@@ -680,6 +681,7 @@ ActiveRecord::Schema.define(version: 20160129135155) do
t.boolean "build_allow_git_fetch", default: true, null: false
t.integer "build_timeout", default: 3600, null: false
t.boolean "pending_delete", default: false
+ t.boolean "public_builds", default: true, null: false
end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
@@ -770,6 +772,7 @@ ActiveRecord::Schema.define(version: 20160129135155) do
add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree
add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree
add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
+ add_index "snippets", ["updated_at"], name: "index_snippets_on_updated_at", using: :btree
add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
create_table "spam_logs", force: :cascade do |t|
diff --git a/doc/api/builds.md b/doc/api/builds.md
index 6e64d096644..4dd4ee8b4bb 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -8,20 +8,16 @@ Get a list of builds in a project.
GET /projects/:id/builds
```
-### Parameters
-
-| Attribute | Type | required | Description |
+| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| scope | string|array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
-
-### Example of request
+| `id` | integer | yes | The ID of a project |
+| `scope` | string **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
```
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds"
```
-### Example of response
+Example of response
```json
[
@@ -112,21 +108,17 @@ Get a list of builds for specific commit in a project.
GET /projects/:id/repository/commits/:sha/builds
```
-### Parameters
-
-| Attribute | Type | required | Description |
+| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| sha | string | yes | The SHA id of a commit |
-| scope | string|array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
-
-### Example of request
+| `id` | integer | yes | The ID of a project |
+| `sha` | string | yes | The SHA id of a commit |
+| `scope` | stringi **or** array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
```
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds"
```
-### Example of response
+Example of response
```json
[
@@ -203,20 +195,16 @@ Get a single build of a project
GET /projects/:id/builds/:build_id
```
-### Parameters
-
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| build\_id | integer | yes | The ID of a build |
-
-### Example of request
+| Attribute | Type | Required | Description |
+|------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a project |
+| `build_id` | integer | yes | The ID of a build |
```
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/8"
```
-### Example of response
+Example of response
```json
{
@@ -267,20 +255,16 @@ Cancel a single build of a project
POST /projects/:id/builds/:build_id/cancel
```
-### Parameters
-
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| build\_id | integer | yes | The ID of a build |
-
-### Example of request
+| Attribute | Type | Required | Description |
+|------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a project |
+| `build_id` | integer | yes | The ID of a build |
```
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/cancel"
```
-### Example of response
+Example of response
```json
{
@@ -317,20 +301,16 @@ Retry a single build of a project
POST /projects/:id/builds/:build_id/retry
```
-### Parameters
-
-| Attribute | Type | required | Description |
-|-----------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
-| build\_id | integer | yes | The ID of a build |
-
-### Example of request
+| Attribute | Type | Required | Description |
+|------------|---------|----------|---------------------|
+| `id` | integer | yes | The ID of a project |
+| `build_id` | integer | yes | The ID of a build |
```
curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/retry"
```
-### Example of response
+Example of response
```json
{
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 7e732b9a2b4..009532b50c1 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -2,7 +2,7 @@
## List merge requests
-Get all merge requests for this project.
+Get all merge requests for this project.
The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
@@ -49,8 +49,24 @@ Parameters:
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
},
+ "source_project_id": "2",
+ "target_project_id": "3",
+ "labels": [ ],
"description":"fixed login page css paddings",
- "work_in_progress": false
+ "work_in_progress": false,
+ "milestone": {
+ "id": 5,
+ "iid": 1,
+ "project_id": 3,
+ "title": "v2.0",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
+ "state": "closed",
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": null
+ },
+ "merge_when_build_succeeds": true,
+ "merge_status": "can_be_merged"
}
]
```
@@ -95,8 +111,24 @@ Parameters:
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
},
+ "source_project_id": "2",
+ "target_project_id": "3",
+ "labels": [ ],
"description":"fixed login page css paddings",
- "work_in_progress": false
+ "work_in_progress": false,
+ "milestone": {
+ "id": 5,
+ "iid": 1,
+ "project_id": 3,
+ "title": "v2.0",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
+ "state": "closed",
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": null
+ },
+ "merge_when_build_succeeds": true,
+ "merge_status": "can_be_merged"
}
```
@@ -156,8 +188,6 @@ Parameters:
"iid": 1,
"project_id": 4,
"title": "Blanditiis beatae suscipit hic assumenda et molestias nisi asperiores repellat et.",
- "description": "Qui voluptatibus placeat ipsa alias quasi. Deleniti rem ut sint. Optio velit qui distinctio.",
- "work_in_progress": false,
"state": "reopened",
"created_at": "2015-02-02T19:49:39.159Z",
"updated_at": "2015-02-02T20:08:49.959Z",
@@ -182,6 +212,8 @@ Parameters:
"source_project_id": 4,
"target_project_id": 4,
"labels": [ ],
+ "description": "Qui voluptatibus placeat ipsa alias quasi. Deleniti rem ut sint. Optio velit qui distinctio.",
+ "work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
@@ -193,6 +225,8 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
+ "merge_when_build_succeeds": true,
+ "merge_status": "can_be_merged",
"changes": [
{
"old_path": "VERSION",
@@ -253,7 +287,24 @@ Parameters:
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
},
- "description":"fixed login page css paddings"
+ "source_project_id": 4,
+ "target_project_id": 4,
+ "labels": [ ],
+ "description":"fixed login page css paddings",
+ "work_in_progress": false,
+ "milestone": {
+ "id": 5,
+ "iid": 1,
+ "project_id": 4,
+ "title": "v2.0",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
+ "state": "closed",
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": null
+ },
+ "merge_when_build_succeeds": true,
+ "merge_status": "can_be_merged"
}
```
@@ -286,7 +337,6 @@ Parameters:
"target_branch": "master",
"project_id": 3,
"title": "test1",
- "description": "description1",
"state": "opened",
"upvotes": 0,
"downvotes": 0,
@@ -305,7 +355,25 @@ Parameters:
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
- }
+ },
+ "source_project_id": 4,
+ "target_project_id": 4,
+ "labels": [ ],
+ "description": "description1",
+ "work_in_progress": false,
+ "milestone": {
+ "id": 5,
+ "iid": 1,
+ "project_id": 4,
+ "title": "v2.0",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
+ "state": "closed",
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": null
+ },
+ "merge_when_build_succeeds": true,
+ "merge_status": "can_be_merged"
}
```
@@ -361,7 +429,25 @@ Parameters:
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
- }
+ },
+ "source_project_id": 4,
+ "target_project_id": 4,
+ "labels": [ ],
+ "description":"fixed login page css paddings",
+ "work_in_progress": false,
+ "milestone": {
+ "id": 5,
+ "iid": 1,
+ "project_id": 4,
+ "title": "v2.0",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
+ "state": "closed",
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": null
+ },
+ "merge_when_build_succeeds": true,
+ "merge_status": "can_be_merged"
}
```
@@ -389,7 +475,7 @@ Parameters:
"source_branch": "test1",
"project_id": 3,
"title": "test1",
- "state": "merged",
+ "state": "opened",
"upvotes": 0,
"downvotes": 0,
"author": {
@@ -407,10 +493,90 @@ Parameters:
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
- }
+ },
+ "source_project_id": 4,
+ "target_project_id": 4,
+ "labels": [ ],
+ "description":"fixed login page css paddings",
+ "work_in_progress": false,
+ "milestone": {
+ "id": 5,
+ "iid": 1,
+ "project_id": 4,
+ "title": "v2.0",
+ "description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
+ "state": "closed",
+ "created_at": "2015-02-02T19:49:26.013Z",
+ "updated_at": "2015-02-02T19:49:26.013Z",
+ "due_date": null
+ },
+ "merge_when_build_succeeds": true,
+ "merge_status": "can_be_merged"
}
```
## Comments on merge requets
Comments are done via the [notes](notes.md) resource.
+
+## List issues that will close on merge
+
+Get all the issues that would be closed by merging the provided merge request.
+
+```
+GET /projects/:id/merge_requests/:merge_request_id/closes_issues
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of a project |
+| `merge_request_id` | integer | yes | The ID of the merge request |
+
+```bash
+curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/76/merge_requests/1/closes_issues
+```
+
+Example response:
+
+```json
+[
+ {
+ "state" : "opened",
+ "description" : "Ratione dolores corrupti mollitia soluta quia.",
+ "author" : {
+ "state" : "active",
+ "id" : 18,
+ "web_url" : "https://gitlab.example.com/u/eileen.lowe",
+ "name" : "Alexandra Bashirian",
+ "avatar_url" : null,
+ "username" : "eileen.lowe"
+ },
+ "milestone" : {
+ "project_id" : 1,
+ "description" : "Ducimus nam enim ex consequatur cumque ratione.",
+ "state" : "closed",
+ "due_date" : null,
+ "iid" : 2,
+ "created_at" : "2016-01-04T15:31:39.996Z",
+ "title" : "v4.0",
+ "id" : 17,
+ "updated_at" : "2016-01-04T15:31:39.996Z"
+ },
+ "project_id" : 1,
+ "assignee" : {
+ "state" : "active",
+ "id" : 1,
+ "name" : "Administrator",
+ "web_url" : "https://gitlab.example.com/u/root",
+ "avatar_url" : null,
+ "username" : "root"
+ },
+ "updated_at" : "2016-01-04T15:31:51.081Z",
+ "id" : 76,
+ "title" : "Consequatur vero maxime deserunt laboriosam est voluptas dolorem.",
+ "created_at" : "2016-01-04T15:31:51.081Z",
+ "iid" : 6,
+ "labels" : []
+ },
+]
+```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 3f372c955d2..9e9486cd87a 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -80,7 +80,9 @@ Parameters:
"avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
"shared_runners_enabled": true,
"forks_count": 0,
- "star_count": 0
+ "star_count": 0,
+ "runners_token": "b8547b1dc37721d05889db52fa2f02",
+ "public_builds": true
},
{
"id": 6,
@@ -137,7 +139,8 @@ Parameters:
"shared_runners_enabled": true,
"forks_count": 0,
"star_count": 0,
- "runners_token": "b8547b1dc37721d05889db52fa2f02"
+ "runners_token": "b8547b1dc37721d05889db52fa2f02",
+ "public_builds": true
}
]
```
@@ -424,6 +427,7 @@ Parameters:
- `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional)
- `import_url` (optional)
+- `public_builds` (optional)
### Create project for user
@@ -446,6 +450,7 @@ Parameters:
- `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional)
- `import_url` (optional)
+- `public_builds` (optional)
### Edit project
@@ -469,6 +474,7 @@ Parameters:
- `snippets_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional)
+- `public_builds` (optional)
On success, method returns 200 with the updated project. If parameters are
invalid, 400 is returned.
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 63fe840b369..bd748f1b986 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -270,7 +270,7 @@ This will forcefully (`-f`) remove the `build` container, the two service
containers as well as all volumes (`-v`) that were created with the container
creation.
-[Docker Fundamentals]: https://docs.docker.com/engine/introduction/understanding-docker/
+[Docker Fundamentals]: https://docs.docker.com/engine/understanding-docker/
[hub]: https://hub.docker.com/
[linking-containers]: https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/
[tutum/wordpress]: https://registry.hub.docker.com/u/tutum/wordpress/
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index a9b36139de9..ae7b760fa67 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -184,6 +184,14 @@ you expected.
You are also able to view the status of any commit in the various pages in
GitLab, such as **Commits** and **Merge Requests**.
+## Builds badge
+
+You can access a builds badge image using following link:
+
+```
+http://example.gitlab.com/namespace/project/badges/branch/build.svg
+```
+
## Next steps
Awesome! You started using CI in GitLab!
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 862cacda586..018d1898594 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -77,9 +77,12 @@ More information about Docker integration can be found in [Using Docker Images](
GitLab CI allows you to define per-project **Secure Variables** that are set in build environment.
The secure variables are stored out of the repository (the `.gitlab-ci.yml`).
-These variables are securely stored in GitLab CI database and are hidden in the build log.
+The variables are securely passed to GitLab Runner and are available in build environment.
It's desired method to use them for storing passwords, secret keys or whatever you want.
+**The value of the variable can be shown in build log if explicitly asked to do so.**
+If your project is public or internal you can make the builds private.
+
Secure Variables can added by going to `Project > Variables > Add Variable`.
They will be available for all subsequent builds.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 3eb9b1767c5..fe614df91fd 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -238,9 +238,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-4-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-5-stable gitlab
-**Note:** You can change `8-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-5-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
@@ -373,9 +373,9 @@ GitLab Shell is an SSH access and repository management software developed speci
# When done you see 'Administrator account created:'
-**Note:** You can set the Administrator/root password by supplying it in environmental variable `GITLAB_ROOT_PASSWORD` as seen below. If you don't set the password (and it is set to the default one) please wait with exposing GitLab to the public internet until the installation is done and you've logged into the server the first time. During the first login you'll be forced to change the default password.
+**Note:** You can set the Administrator/root password and e-mail by supplying them in environmental variables, `GITLAB_ROOT_PASSWORD` and `GITLAB_ROOT_EMAIL` respectively, as seen below. If you don't set the password (and it is set to the default one) please wait with exposing GitLab to the public internet until the installation is done and you've logged into the server the first time. During the first login you'll be forced to change the default password.
- sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=yourpassword
+ sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=yourpassword GITLAB_ROOT_EMAIL=youremail
### Secure secrets.yml
@@ -484,6 +484,11 @@ You can use `sudo service gitlab start` and `sudo service gitlab stop` to start
## Advanced Setup Tips
+### Relative URL support
+
+See the [Relative URL documentation](relative_url.md) for more information on
+how to configure GitLab with a relative URL.
+
### Using HTTPS
To use GitLab with HTTPS:
diff --git a/doc/install/relative_url.md b/doc/install/relative_url.md
new file mode 100644
index 00000000000..b341b6311c6
--- /dev/null
+++ b/doc/install/relative_url.md
@@ -0,0 +1,126 @@
+## Install GitLab under a relative URL
+
+_**Note:**
+This document describes how to run GitLab under a relative URL for installations
+from source. If you are using an Omnibus package,
+[the steps are different][omnibus-rel]. Use this guide along with the
+[installation guide](installation.md) if you are installing GitLab for the
+first time._
+
+---
+
+While it is recommended to install GitLab on its own (sub)domain, sometimes
+this is not possible due to a variety of reasons. In that case, GitLab can also
+be installed under a relative URL, for example `https://example.com/gitlab`.
+
+There is no limit to how deeply nested the relative URL can be. For example you
+could serve GitLab under `/foo/bar/gitlab/git` without any issues.
+
+Note that by changing the URL on an existing GitLab installation, all remote
+URLs will change, so you'll have to manually edit them in any local repository
+that points to your GitLab instance.
+
+---
+
+The TL;DR list of configuration files that you need to change in order to
+serve GitLab under a relative URL is:
+
+- `/home/git/gitlab/config/application.rb`
+- `/home/git/gitlab/config/gitlab.yml`
+- `/home/git/gitlab/config/unicorn.rb`
+- `/home/git/gitlab-shell/config.yml`
+- `/etc/default/gitlab`
+
+After all the changes you need to recompile the assets and [restart GitLab].
+
+### Relative URL requirements
+
+If you configure GitLab with a relative URL, the assets (JavaScript, CSS, fonts,
+images, etc.) will need to be recompiled, which is a task which consumes a lot
+of CPU and memory resources. To avoid out-of-memory errors, you should have at
+least 2GB of RAM available on your system, while we recommend 4GB RAM, and 4 or
+8 CPU cores.
+
+See the [requirements](requirements.md) document for more information.
+
+### Enable relative URL in GitLab
+
+_**Note:**
+Do not make any changes to your web server configuration file regarding
+relative URL. The relative URL support is implemented by GitLab Workhorse._
+
+---
+
+Before following the steps below to enable relative URL in GitLab, some
+assumptions are made:
+
+- GitLab is served under `/gitlab`
+- The directory under which GitLab is installed is `/home/git/`
+
+Make sure to follow all steps below:
+
+1. (Optional) If you run short on resources, you can temporarily free up some
+ memory by shutting down the GitLab service with the following command:
+
+ ```shell
+ sudo service gitlab stop
+ ```
+
+1. Edit `/home/git/gitlab/config/application.rb` and uncomment/change the
+ following line:
+
+ ```ruby
+ config.relative_url_root = "/gitlab"
+ ```
+
+1. Edit `/home/git/gitlab/config/gitlab.yml` and uncomment/change the
+ following line:
+
+ ```yaml
+ relative_url_root: /gitlab
+ ```
+
+1. Edit `/home/git/gitlab/config/unicorn.rb` and uncomment/change the
+ following line:
+
+ ```ruby
+ ENV['RAILS_RELATIVE_URL_ROOT'] = "/gitlab"
+ ```
+
+1. Edit `/home/git/gitlab-shell/config.yml` and append the relative path to
+ the following line:
+
+ ```yaml
+ gitlab_url: http://127.0.0.1/gitlab
+ ```
+
+1. Make sure you have copied the supplied init script and the defaults file
+ as stated in the [installation guide](installation.md#install-init-script).
+ Then, edit `/etc/default/gitlab` and set in `gitlab_workhorse_options` the
+ `-authBackend` setting to read like:
+
+ ```shell
+ -authBackend http://127.0.0.1:8080/gitlab
+ ```
+
+ **Note:**
+ If you are using a custom init script, make sure to edit the above
+ gitlab-workhorse setting as needed.
+
+1. After all the above changes recompile the assets. This is an important task
+ and will take some time to complete depending on the server resources:
+
+ ```
+ cd /home/git/gitlab
+ sudo -u git -H bundle exec rake assets:clean assets:precompile RAILS_ENV=production
+ ```
+
+1. [Restart GitLab][] for the changes to take effect.
+
+### Disable relative URL in GitLab
+
+To disable the relative URL, follow the same steps as above and set up the
+GitLab URL to one that doesn't contain a relative path.
+
+[omnibus-rel]: http://doc.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab"
+[restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 006dae8ca9a..c6a1c20d02f 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -66,7 +66,7 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim
You need at least 2GB of addressable memory (RAM + swap) to install and use GitLab!
With less memory GitLab will give strange errors during the reconfigure run and 500 errors during usage.
-- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advise.
+- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice.
- 1GB RAM + 1GB swap supports up to 100 users but it will be slow
- **2GB RAM** is the **recommended** memory size and supports up to 100 users
- 4GB RAM supports up to 1,000 users
diff --git a/doc/markdown/img/logo.png b/doc/markdown/img/logo.png
new file mode 100644
index 00000000000..7da5f23ed9b
--- /dev/null
+++ b/doc/markdown/img/logo.png
Binary files differ
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index f6bffba9fdb..c400cdac64d 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -424,24 +424,24 @@ will point the link to `wikis/style` when the link is inside of a wiki markdown
Here's our logo (hover to see the title text):
Inline-style:
- ![alt text](assets/logo.svg)
+ ![alt text](img/logo.png)
Reference-style:
![alt text1][logo]
- [logo]: assets/logo.svg
+ [logo]: img/logo.png
Here's our logo:
Inline-style:
-![alt text](/assets/logo.svg)
+![alt text](img/logo.png)
Reference-style:
![alt text][logo]
-[logo]: /assets/logo.svg
+[logo]: img/logo.png
## Blockquotes
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index 1be78ac1823..168e7d143ee 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -18,6 +18,9 @@ documentation](../workflow/add-user/add-user.md).
|---------------------------------------|---------|------------|-------------|----------|--------|
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
+| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
+| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
+| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
| Download project | | ✓ | ✓ | ✓ | ✓ |
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
@@ -31,6 +34,7 @@ documentation](../workflow/add-user/add-user.md).
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ |
| Write a wiki | | | ✓ | ✓ | ✓ |
+| Cancel and retry builds | | | ✓ | ✓ | ✓ |
| Create new milestones | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
@@ -40,12 +44,17 @@ documentation](../workflow/add-user/add-user.md).
| Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ |
| Configure project hooks | | | | ✓ | ✓ |
+| Manage runners | | | | ✓ | ✓ |
+| Manage build triggers | | | | ✓ | ✓ |
+| Manage variables | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
| Force push to protected branches | | | | | |
| Remove protected branches | | | | | |
+[^1]: If **Allow guest to access builds** is enabled in CI settings
+
## Group
In order for a group to appear as public and be browsable, it must contain at
diff --git a/doc/update/8.4-to-8.5.md b/doc/update/8.4-to-8.5.md
new file mode 100644
index 00000000000..6e035047b24
--- /dev/null
+++ b/doc/update/8.4-to-8.5.md
@@ -0,0 +1,106 @@
+# From 8.4 to 8.5
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-5-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-5-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout v2.6.10
+```
+
+### 5. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires
+[Go 1.5](https://golang.org/dl) which should already be on your system from
+GitLab 8.1.
+
+```bash
+cd /home/git/gitlab-workhorse
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout 0.6.3
+sudo -u git -H make
+```
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+```
+
+### 7. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 8. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.4)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.3 to 8.4](8.3-to-8.4.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index c29037e89c2..c556597225c 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -8,8 +8,8 @@ Web hooks can be used to update an external issue tracker, trigger CI builds, up
## SSL Verification
-By default, the SSL certificate of the webhook endpoint is verified based on
-an internal list of Certificate Authorities,
+By default, the SSL certificate of the webhook endpoint is verified based on
+an internal list of Certificate Authorities,
which means the certificate cannot be self-signed.
You can turn this off in the web hook settings in your GitLab projects.
@@ -76,7 +76,6 @@ X-Gitlab-Event: Push Hook
}
],
"total_commits_count": 4
-
}
```
@@ -158,6 +157,11 @@ X-Gitlab-Event: Issue Hook
"iid": 23,
"url": "http://example.com/diaspora/issues/23",
"action": "open"
+ },
+ "assignee": {
+ "name": "User1",
+ "username": "user1",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
}
}
```
@@ -322,7 +326,12 @@ X-Gitlab-Event: Note Hook
"email": "john@example.com"
}
},
- "work_in_progress": false
+ "work_in_progress": false,
+ "assignee": {
+ "name": "User1",
+ "username": "user1",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
+ }
}
}
```
@@ -397,7 +406,7 @@ X-Gitlab-Event: Note Hook
**Request body:**
-```
+```json
{
"object_kind": "note",
"user": {
@@ -510,7 +519,12 @@ X-Gitlab-Event: Merge Request Hook
},
"work_in_progress": false,
"url": "http://example.com/diaspora/merge_requests/1",
- "action": "open"
+ "action": "open",
+ "assignee": {
+ "name": "User1",
+ "username": "user1",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
+ }
}
}
```
diff --git a/features/admin/broadcast_messages.feature b/features/admin/broadcast_messages.feature
index fd3bac77f86..4f9c651561e 100644
--- a/features/admin/broadcast_messages.feature
+++ b/features/admin/broadcast_messages.feature
@@ -25,3 +25,9 @@ Feature: Admin Broadcast Messages
When I remove an existing broadcast message
Then I should be redirected to admin messages page
And I should not see the removed broadcast message
+
+ @javascript
+ Scenario: Live preview a customized broadcast message
+ When I visit admin messages page
+ And I enter a broadcast message with Markdown
+ Then I should see a live preview of the rendered broadcast message
diff --git a/features/project/badges/build.feature b/features/project/badges/build.feature
new file mode 100644
index 00000000000..9417f62d680
--- /dev/null
+++ b/features/project/badges/build.feature
@@ -0,0 +1,22 @@
+Feature: Project Badges Build
+ Background:
+ Given I sign in as a user
+ And I own a project
+ And project has CI enabled
+ And project has a recent build
+
+ Scenario: I want to see a badge for successfully built project
+ Given recent build is successful
+ When I display builds badge for a master branch
+ Then I should see a build success badge
+
+ Scenario: I want to see a badge for project with failed builds
+ Given recent build failed
+ When I display builds badge for a master branch
+ Then I should see a build failed badge
+
+ Scenario: I want to see a badge for project with running builds
+ Given recent build is successful
+ And project has another build that is running
+ When I display builds badge for a master branch
+ Then I should see a build running badge
diff --git a/features/project/builds/permissions.feature b/features/project/builds/permissions.feature
index 1193bcd74f6..3c7f72335d9 100644
--- a/features/project/builds/permissions.feature
+++ b/features/project/builds/permissions.feature
@@ -5,6 +5,41 @@ Feature: Project Builds Permissions
And project has CI enabled
And project has a recent build
+ Scenario: I try to visit build details as guest
+ Given I am member of a project with a guest role
+ When I visit recent build details page
+ Then page status code should be 404
+
+ Scenario: I try to visit project builds page as guest
+ Given I am member of a project with a guest role
+ When I visit project builds page
+ Then page status code should be 404
+
+ Scenario: I try to visit build details of internal project without access to builds
+ Given The project is internal
+ And public access for builds is disabled
+ When I visit recent build details page
+ Then page status code should be 404
+
+ Scenario: I try to visit internal project builds page without access to builds
+ Given The project is internal
+ And public access for builds is disabled
+ When I visit project builds page
+ Then page status code should be 404
+
+ Scenario: I try to visit build details of internal project with access to builds
+ Given The project is internal
+ And public access for builds is enabled
+ When I visit recent build details page
+ Then I see details of a build
+ And I see build trace
+
+ Scenario: I try to visit internal project builds page with access to builds
+ Given The project is internal
+ And public access for builds is enabled
+ When I visit project builds page
+ Then I see the build
+
Scenario: I try to download build artifacts as guest
Given I am member of a project with a guest role
And recent build has artifacts available
diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature
index 01c10721312..a95df038357 100644
--- a/features/project/commits/commits.feature
+++ b/features/project/commits/commits.feature
@@ -7,6 +7,26 @@ Feature: Project Commits
Scenario: I browse commits list for master branch
Then I see project commits
+ And I should not see button to create a new merge request
+ Then I click the "Compare" tab
+ And I should not see button to create a new merge request
+
+ Scenario: I browse commits list for feature branch without a merge request
+ Given I visit commits list page for feature branch
+ Then I see feature branch commits
+ And I see button to create a new merge request
+ Then I click the "Compare" tab
+ And I see button to create a new merge request
+
+ Scenario: I browse commits list for feature branch with an open merge request
+ Given project have an open merge request
+ And I visit commits list page for feature branch
+ Then I see feature branch commits
+ And I should not see button to create a new merge request
+ And I should see button to the merge request
+ Then I click the "Compare" tab
+ And I should not see button to create a new merge request
+ And I should see button to the merge request
Scenario: I browse atom feed of commits list for master branch
Given I click atom feed link
@@ -31,6 +51,22 @@ Feature: Project Commits
Then I see inline diff button
@javascript
+ Scenario: I compare branches without a merge request
+ Given I visit compare refs page
+ And I fill compare fields with branches
+ Then I see compared branches
+ And I see button to create a new merge request
+
+ @javascript
+ Scenario: I compare branches with an open merge request
+ Given project have an open merge request
+ And I visit compare refs page
+ And I fill compare fields with branches
+ Then I see compared branches
+ And I should not see button to create a new merge request
+ And I should see button to the merge request
+
+ @javascript
Scenario: I compare refs
Given I visit compare refs page
And I fill compare fields with refs
diff --git a/features/steps/admin/broadcast_messages.rb b/features/steps/admin/broadcast_messages.rb
index 6cacdf4764c..af2b4a29313 100644
--- a/features/steps/admin/broadcast_messages.rb
+++ b/features/steps/admin/broadcast_messages.rb
@@ -19,7 +19,7 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
end
step 'submit form with new customized broadcast message' do
- fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST'
+ fill_in 'broadcast_message_message', with: 'Application update from **4:00 CST to 5:00 CST**'
fill_in 'broadcast_message_color', with: '#f2dede'
fill_in 'broadcast_message_font', with: '#b94a48'
select Date.today.next_year.year, from: "broadcast_message_ends_at_1i"
@@ -28,6 +28,7 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
step 'I should see a customized broadcast message' do
expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST'
+ expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST'
expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"])
end
@@ -51,4 +52,15 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
step 'I should not see the removed broadcast message' do
expect(page).not_to have_content 'Migration to new server'
end
+
+ step 'I enter a broadcast message with Markdown' do
+ fill_in 'broadcast_message_message', with: "Live **Markdown** previews. :tada:"
+ end
+
+ step 'I should see a live preview of the rendered broadcast message' do
+ page.within('.broadcast-message-preview') do
+ expect(page).to have_selector('strong', text: 'Markdown')
+ expect(page).to have_selector('img.emoji')
+ end
+ end
end
diff --git a/features/steps/project/badges/build.rb b/features/steps/project/badges/build.rb
new file mode 100644
index 00000000000..cbfc35bed65
--- /dev/null
+++ b/features/steps/project/badges/build.rb
@@ -0,0 +1,28 @@
+class Spinach::Features::ProjectBadgesBuild < Spinach::FeatureSteps
+ include SharedAuthentication
+ include SharedProject
+ include SharedBuilds
+ include RepoHelpers
+
+ step 'I display builds badge for a master branch' do
+ visit build_namespace_project_badges_path(@project.namespace, @project, ref: :master, format: :svg)
+ end
+
+ step 'I should see a build success badge' do
+ expect_badge('success')
+ end
+
+ step 'I should see a build failed badge' do
+ expect_badge('failed')
+ end
+
+ step 'I should see a build running badge' do
+ expect_badge('running')
+ end
+
+ def expect_badge(status)
+ svg = Nokogiri::XML.parse(page.body)
+ expect(page.response_headers).to include('Content-Type' => 'image/svg+xml')
+ expect(svg.at(%Q{text:contains("#{status}")})).to be_truthy
+ end
+end
diff --git a/features/steps/project/builds/summary.rb b/features/steps/project/builds/summary.rb
index 4bc670fdfcb..4f94fc96354 100644
--- a/features/steps/project/builds/summary.rb
+++ b/features/steps/project/builds/summary.rb
@@ -4,14 +4,6 @@ class Spinach::Features::ProjectBuildsSummary < Spinach::FeatureSteps
include SharedBuilds
include RepoHelpers
- step 'I see details of a build' do
- expect(page).to have_content "Build ##{@build.id}"
- end
-
- step 'I see build trace' do
- expect(page).to have_css '#build-trace'
- end
-
step 'I see button to CI Lint' do
page.within('.nav-controls') do
ci_lint_tool_link = page.find_link('CI Lint')
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index daf6cdaaac8..f9fd7332464 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -33,6 +33,13 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(page).to have_content "Showing #{sample_commit.files_changed_count} changed files"
end
+ step 'I fill compare fields with branches' do
+ fill_in 'from', with: 'feature'
+ fill_in 'to', with: 'master'
+
+ click_button 'Compare'
+ end
+
step 'I fill compare fields with refs' do
fill_in "from", with: sample_commit.parent_id
fill_in "to", with: sample_commit.id
@@ -56,6 +63,56 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
expect(page).to have_content "Showing 2 changed files"
end
+ step 'I visit commits list page for feature branch' do
+ visit namespace_project_commits_path(@project.namespace, @project, 'feature', { limit: 5 })
+ end
+
+ step 'I see feature branch commits' do
+ commit = @project.repository.commit('0b4bc9a')
+ expect(page).to have_content(@project.name)
+ expect(page).to have_content(commit.message[0..12])
+ expect(page).to have_content(commit.short_id)
+ end
+
+ step 'project have an open merge request' do
+ create(:merge_request,
+ title: 'Feature',
+ source_project: @project,
+ source_branch: 'feature',
+ target_branch: 'master',
+ author: @project.users.first
+ )
+ end
+
+ step 'I click the "Compare" tab' do
+ click_link('Compare')
+ end
+
+ step 'I fill compare fields with branches' do
+ fill_in 'from', with: 'master'
+ fill_in 'to', with: 'feature'
+
+ click_button 'Compare'
+ end
+
+ step 'I see compared branches' do
+ expect(page).to have_content 'Commits (1)'
+ expect(page).to have_content 'Showing 1 changed file with 5 additions and 0 deletions'
+ end
+
+ step 'I see button to create a new merge request' do
+ expect(page).to have_link 'Create Merge Request'
+ end
+
+ step 'I should not see button to create a new merge request' do
+ expect(page).to_not have_link 'Create Merge Request'
+ end
+
+ step 'I should see button to the merge request' do
+ merge_request = MergeRequest.find_by(title: 'Feature')
+ expect(page).to have_link "View Open Merge Request", href: namespace_project_merge_request_path(@project.namespace, @project, merge_request)
+ end
+
step 'I see breadcrumb links' do
expect(page).to have_selector('ul.breadcrumb')
expect(page).to have_selector('ul.breadcrumb a', count: 4)
diff --git a/features/steps/shared/builds.rb b/features/steps/shared/builds.rb
index 92bf362879b..fa54c93df0f 100644
--- a/features/steps/shared/builds.rb
+++ b/features/steps/shared/builds.rb
@@ -6,8 +6,20 @@ module SharedBuilds
end
step 'project has a recent build' do
- ci_commit = create :ci_commit, project: @project, sha: sample_commit.id
- @build = create :ci_build, commit: ci_commit
+ @ci_commit = create(:ci_commit, project: @project, sha: @project.commit.sha)
+ @build = create(:ci_build, commit: @ci_commit)
+ end
+
+ step 'recent build is successful' do
+ @build.update_column(:status, 'success')
+ end
+
+ step 'recent build failed' do
+ @build.update_column(:status, 'failed')
+ end
+
+ step 'project has another build that is running' do
+ create(:ci_build, commit: @ci_commit, name: 'second build', status: 'running')
end
step 'I visit recent build details page' do
@@ -38,4 +50,21 @@ module SharedBuilds
step 'I access artifacts download page' do
visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build)
end
+
+ step 'I see details of a build' do
+ expect(page).to have_content "Build ##{@build.id}"
+ end
+
+ step 'I see build trace' do
+ expect(page).to have_css '#build-trace'
+ end
+
+ step 'I see the build' do
+ page.within('.commit_status') do
+ expect(page).to have_content "##{@build.id}"
+ expect(page).to have_content @build.sha[0..7]
+ expect(page).to have_content @build.ref
+ expect(page).to have_content @build.name
+ end
+ end
end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index d9c75d12238..b13e82f276b 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -240,6 +240,18 @@ module SharedProject
end
end
+ step 'The project is internal' do
+ @project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
+ end
+
+ step 'public access for builds is enabled' do
+ @project.update(public_builds: true)
+ end
+
+ step 'public access for builds is disabled' do
+ @project.update(public_builds: false)
+ end
+
def user_owns_project(user_name:, project_name:, visibility: :private)
user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore)
project = Project.find_by(name: project_name)
diff --git a/lib/api/builds.rb b/lib/api/builds.rb
index d293f988165..a8bd3842ce4 100644
--- a/lib/api/builds.rb
+++ b/lib/api/builds.rb
@@ -13,11 +13,12 @@ module API
# Example Request:
# GET /projects/:id/builds
get ':id/builds' do
+
builds = user_project.builds.order('id DESC')
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build,
- user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+ user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
# Get builds for a specific commit of a project
@@ -30,6 +31,8 @@ module API
# Example Request:
# GET /projects/:id/repository/commits/:sha/builds
get ':id/repository/commits/:sha/builds' do
+ authorize_read_builds!
+
commit = user_project.ci_commits.find_by_sha(params[:sha])
return not_found! unless commit
@@ -37,7 +40,7 @@ module API
builds = filter_builds(builds, params[:scope])
present paginate(builds), with: Entities::Build,
- user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+ user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
# Get a specific build of a project
@@ -48,11 +51,13 @@ module API
# Example Request:
# GET /projects/:id/builds/:build_id
get ':id/builds/:build_id' do
+ authorize_read_builds!
+
build = get_build(params[:build_id])
return not_found!(build) unless build
present build, with: Entities::Build,
- user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+ user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
# Get a trace of a specific build of a project
@@ -67,6 +72,8 @@ module API
# is saved in the DB instead of file). But before that, we need to consider how to replace the value of
# `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
get ':id/builds/:build_id/trace' do
+ authorize_read_builds!
+
build = get_build(params[:build_id])
return not_found!(build) unless build
@@ -86,7 +93,7 @@ module API
# example request:
# post /projects/:id/build/:build_id/cancel
post ':id/builds/:build_id/cancel' do
- authorize_manage_builds!
+ authorize_update_builds!
build = get_build(params[:build_id])
return not_found!(build) unless build
@@ -94,7 +101,7 @@ module API
build.cancel
present build, with: Entities::Build,
- user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+ user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
# Retry a specific build of a project
@@ -105,7 +112,7 @@ module API
# example request:
# post /projects/:id/build/:build_id/retry
post ':id/builds/:build_id/retry' do
- authorize_manage_builds!
+ authorize_update_builds!
build = get_build(params[:build_id])
return forbidden!('Build is not retryable') unless build && build.retryable?
@@ -113,7 +120,7 @@ module API
build = Ci::Build.retry(build)
present build, with: Entities::Build,
- user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+ user_can_download_artifacts: can?(current_user, :read_build, user_project)
end
end
@@ -141,8 +148,12 @@ module API
builds.where(status: available_statuses && scope)
end
- def authorize_manage_builds!
- authorize! :manage_builds, user_project
+ def authorize_read_builds!
+ authorize! :read_build, user_project
+ end
+
+ def authorize_update_builds!
+ authorize! :update_build, user_project
end
end
end
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 1162271f5fc..9422d438d21 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -18,7 +18,7 @@ module API
# Examples:
# GET /projects/:id/repository/commits/:sha/statuses
get ':id/repository/commits/:sha/statuses' do
- authorize! :read_commit_statuses, user_project
+ authorize! :read_commit_status, user_project
sha = params[:sha]
ci_commit = user_project.ci_commit(sha)
not_found! 'Commit' unless ci_commit
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 82a75734de0..a9c09ffdb31 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -72,6 +72,7 @@ module API
expose :star_count, :forks_count
expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
+ expose :public_builds
end
class ProjectMember < UserBasic
@@ -175,6 +176,7 @@ module API
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: Entities::Milestone
expose :merge_when_build_succeeds
+ expose :merge_status
end
class MergeRequestChanges < MergeRequest
@@ -383,7 +385,7 @@ module API
# for downloading of artifacts (see: https://gitlab.com/gitlab-org/gitlab-ce/issues/4255)
expose :download_url do |repo_obj, options|
if options[:user_can_download_artifacts]
- repo_obj.download_url
+ repo_obj.artifacts_download_url
end
end
expose :commit, with: RepoCommit do |repo_obj, _options|
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index c54beb00ac5..c5e5d57ed4d 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -302,6 +302,19 @@ module API
render_api_error!("Failed to save note #{note.errors.messages}", 400)
end
end
+
+ # List issues that will close on merge
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # Examples:
+ # GET /projects/:id/merge_requests/:merge_request_id/closes_issues
+ get "#{path}/closes_issues" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+ issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
+ present paginate(issues), with: Entities::Issue
+ end
end
end
end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 1f991e600e3..6067c8b4a5e 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -99,6 +99,7 @@ module API
# public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - 0 by default
# import_url (optional)
+ # public_builds (optional)
# Example Request
# POST /projects
post do
@@ -115,7 +116,8 @@ module API
:namespace_id,
:public,
:visibility_level,
- :import_url]
+ :import_url,
+ :public_builds]
attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(current_user, attrs).execute
if @project.saved?
@@ -145,6 +147,7 @@ module API
# public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional)
# import_url (optional)
+ # public_builds (optional)
# Example Request
# POST /projects/user/:user_id
post "user/:user_id" do
@@ -161,7 +164,8 @@ module API
:shared_runners_enabled,
:public,
:visibility_level,
- :import_url]
+ :import_url,
+ :public_builds]
attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(user, attrs).execute
if @project.saved?
@@ -205,6 +209,7 @@ module API
# shared_runners_enabled (optional)
# public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - visibility level of a project
+ # public_builds (optional)
# Example Request
# PUT /projects/:id
put ':id' do
@@ -219,7 +224,8 @@ module API
:snippets_enabled,
:shared_runners_enabled,
:public,
- :visibility_level]
+ :visibility_level,
+ :public_builds]
attrs = map_public_to_visibility_level(attrs)
authorize_admin_project
authorize! :rename_project, user_project if attrs[:name].present?
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index 5e4964f446c..d1d07394e92 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -54,7 +54,7 @@ module API
# GET /projects/:id/triggers
get ':id/triggers' do
authenticate!
- authorize_admin_project
+ authorize! :admin_build, user_project
triggers = user_project.triggers.includes(:trigger_requests)
triggers = paginate(triggers)
@@ -71,7 +71,7 @@ module API
# GET /projects/:id/triggers/:token
get ':id/triggers/:token' do
authenticate!
- authorize_admin_project
+ authorize! :admin_build, user_project
trigger = user_project.triggers.find_by(token: params[:token].to_s)
return not_found!('Trigger') unless trigger
@@ -87,7 +87,7 @@ module API
# POST /projects/:id/triggers
post ':id/triggers' do
authenticate!
- authorize_admin_project
+ authorize! :admin_build, user_project
trigger = user_project.triggers.create
@@ -103,7 +103,7 @@ module API
# DELETE /projects/:id/triggers/:token
delete ':id/triggers/:token' do
authenticate!
- authorize_admin_project
+ authorize! :admin_build, user_project
trigger = user_project.triggers.find_by(token: params[:token].to_s)
return not_found!('Trigger') unless trigger
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index d9a055f6c92..f6495071a11 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -2,7 +2,7 @@ module API
# Projects variables API
class Variables < Grape::API
before { authenticate! }
- before { authorize_admin_project }
+ before { authorize! :admin_build, user_project }
resource :projects do
# Get project variables
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 099062eeb8b..4962f5e53ce 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -1,6 +1,9 @@
module Backup
class Manager
def pack
+ # Make sure there is a connection
+ ActiveRecord::Base.connection.reconnect!
+
# saving additional informations
s = {}
s[:db_version] = "#{ActiveRecord::Migrator.current_version}"
diff --git a/lib/banzai/pipeline/broadcast_message_pipeline.rb b/lib/banzai/pipeline/broadcast_message_pipeline.rb
new file mode 100644
index 00000000000..4bb85e24c38
--- /dev/null
+++ b/lib/banzai/pipeline/broadcast_message_pipeline.rb
@@ -0,0 +1,16 @@
+module Banzai
+ module Pipeline
+ class BroadcastMessagePipeline < DescriptionPipeline
+ def self.filters
+ @filters ||= [
+ Filter::MarkdownFilter,
+ Filter::SanitizationFilter,
+
+ Filter::EmojiFilter,
+ Filter::AutolinkFilter,
+ Filter::ExternalLinkFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index f751458ac66..b9bb6e76081 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -36,7 +36,7 @@ module Gitlab
# import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git")
#
def import_repository(name, url)
- output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '240'])
+ output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '900'])
raise Error, output unless status.zero?
true
end
diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb
index 313e6b9fc03..997a22779a0 100644
--- a/lib/gitlab/blame.rb
+++ b/lib/gitlab/blame.rb
@@ -40,6 +40,7 @@ module Gitlab
end
def highlighted_lines
+ @blob.load_all_data!(repository)
@highlighted_lines ||= Gitlab::Highlight.highlight(@blob.name, @blob.data).lines
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 8531c7e87e1..761b63e98f6 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -7,8 +7,8 @@ module Gitlab
settings = nil
if connect_to_db?
- settings = ApplicationSetting.current
- settings ||= ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
+ settings = ::ApplicationSetting.current
+ settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end
settings || fake_application_settings
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index f065cc5e9e9..191bea86ac3 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -1,8 +1,8 @@
module Gitlab
module Git
- BLANK_SHA = '0' * 40
- TAG_REF_PREFIX = "refs/tags/"
- BRANCH_REF_PREFIX = "refs/heads/"
+ BLANK_SHA = ('0' * 40).freeze
+ TAG_REF_PREFIX = "refs/tags/".freeze
+ BRANCH_REF_PREFIX = "refs/heads/".freeze
class << self
def ref_name(ref)
diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb
index 38364a0b151..addda95be2b 100644
--- a/lib/gitlab/snippet_search_results.rb
+++ b/lib/gitlab/snippet_search_results.rb
@@ -12,9 +12,9 @@ module Gitlab
def objects(scope, page = nil)
case scope
when 'snippet_titles'
- Kaminari.paginate_array(snippet_titles).page(page).per(per_page)
+ snippet_titles.page(page).per(per_page)
when 'snippet_blobs'
- Kaminari.paginate_array(snippet_blobs).page(page).per(per_page)
+ snippet_blobs.page(page).per(per_page)
else
super
end
@@ -39,11 +39,7 @@ module Gitlab
end
def snippet_blobs
- search = Snippet.where(id: limit_snippet_ids).search_code(query)
- search = search.order('updated_at DESC').to_a
- snippets = []
- search.each { |e| snippets << chunk_snippet(e) }
- snippets
+ Snippet.where(id: limit_snippet_ids).search_code(query).order('updated_at DESC')
end
def default_scope
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index 9e90a99f15b..d95e7023d2e 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -38,7 +38,7 @@ web_server_pid_path="$pid_path/unicorn.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
mail_room_enabled=false
mail_room_pid_path="$pid_path/mail_room.pid"
-gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse && pwd)
+gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse 2> /dev/null && pwd)
gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $rails_socket -documentRoot $app_root/public"
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
@@ -49,7 +49,7 @@ test -f /etc/default/gitlab && . /etc/default/gitlab
# Switch to the app_user if it is not he/she who is running the script.
if [ `whoami` != "$app_user" ]; then
- eval su - "$app_user" -s $shell_path -c $(echo \")$0 "$@"$(echo \"); exit;
+ eval su - "$app_user" -c $(echo \")$shell_path -l -c \'$0 "$@"\'$(echo \"); exit;
fi
# Switch to the gitlab path, exit on failure.
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index 4e6e56ac2db..cc8617b72ca 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -34,11 +34,16 @@ sidekiq_pid_path="$pid_path/sidekiq.pid"
# /home/git/gitlab-workhorse .
gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse && pwd)
gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
+
# The -listenXxx settings determine where gitlab-workhorse
-# listens for connections from NGINX. To listen on localhost:8181, write
-# '-listenNetwork tcp -listenAddr localhost:8181'.
-# The -authBackend setting tells gitlab-workhorse where it can reach
-# Unicorn.
+# listens for connections from the web server. By default it listens to a
+# socket. To listen on TCP connections (needed by Apache) change to:
+# '-listenNetwork tcp -listenAddr 127.0.0.1:8181'
+#
+# The -authBackend setting tells gitlab-workhorse where it can reach Unicorn.
+# For relative URL support change to:
+# '-authBackend http://127.0.0.1/8080/gitlab'
+# Read more in http://doc.gitlab.com/ce/install/relative_url.html
gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public"
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 2dc2953e328..54d95cd62a5 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -90,24 +90,6 @@ namespace :gitlab do
end
end
- def check_database_is_not_sqlite
- print "Database is SQLite ... "
-
- database_config_file = Rails.root.join("config", "database.yml")
-
- unless File.read(database_config_file) =~ /adapter:\s+sqlite/
- puts "no".green
- else
- puts "yes".red
- puts "Please fix this by removing the SQLite entry from the database.yml".blue
- for_more_information(
- "https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL",
- see_database_guide
- )
- fix_and_rerun
- end
- end
-
def check_gitlab_config_exists
print "GitLab config exists? ... "
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index 5987988dc8e..b6f076a90c3 100755
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -1,15 +1,15 @@
#!/bin/bash
if [ -f /.dockerinit ]; then
- # Docker runners use `/cache` folder which is persisted every build
- if [ ! -e /cache/phantomjs_1.9.8-0jessie_amd64.deb ]; then
+ mkdir -p vendor
+ if [ ! -e vendor/phantomjs_1.9.8-0jessie_amd64.deb ]; then
wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb
- mv phantomjs_1.9.8-0jessie_amd64.deb /cache
+ mv phantomjs_1.9.8-0jessie_amd64.deb vendor/
fi
- dpkg -i /cache/phantomjs_1.9.8-0jessie_amd64.deb
+ dpkg -i vendor/phantomjs_1.9.8-0jessie_amd64.deb
apt-get update -qq
- apt-get -o dir::cache::archives="/cache/apt" install -y -qq --force-yes \
+ apt-get -o dir::cache::archives="vendor/apt" install -y -qq --force-yes \
libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client unzip
cp config/database.yml.mysql config/database.yml
@@ -20,7 +20,7 @@ if [ -f /.dockerinit ]; then
cp config/resque.yml.example config/resque.yml
sed -i 's/localhost/redis/g' config/resque.yml
- export FLAGS=(--path /cache)
+ export FLAGS=(--path vendor)
else
export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
cp config/database.yml.mysql config/database.yml
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index d2db77f6286..c1b6ecd329a 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -1,30 +1,3 @@
-# == Schema Information
-#
-# Table name: builds
-#
-# id :integer not null, primary key
-# project_id :integer
-# status :string(255)
-# finished_at :datetime
-# trace :text
-# created_at :datetime
-# updated_at :datetime
-# started_at :datetime
-# runner_id :integer
-# commit_id :integer
-# coverage :float
-# commands :text
-# job_id :integer
-# name :string(255)
-# deploy :boolean default(FALSE)
-# options :text
-# allow_failure :boolean default(FALSE), not null
-# stage :string(255)
-# trigger_request_id :integer
-#
-
-# Read about factories at https://github.com/thoughtbot/factory_girl
-
FactoryGirl.define do
factory :ci_build, class: Ci::Build do
name 'test'
@@ -65,5 +38,20 @@ FactoryGirl.define do
build.trace = 'BUILD TRACE'
end
end
+
+ trait :artifacts do
+ after(:create) do |build, _|
+ build.artifacts_file =
+ fixture_file_upload(Rails.root +
+ 'spec/fixtures/ci_build_artifacts.zip',
+ 'application/zip')
+
+ build.artifacts_metadata =
+ fixture_file_upload(Rails.root +
+ 'spec/fixtures/ci_build_artifacts_metadata.gz',
+ 'application/x-gzip')
+ build.save!
+ end
+ end
end
end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 5b6f3cb3f15..6da3a857b3f 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -8,7 +8,7 @@ describe "Builds" do
@commit = FactoryGirl.create :ci_commit
@build = FactoryGirl.create :ci_build, commit: @commit
@project = @commit.project
- @project.team << [@user, :master]
+ @project.team << [@user, :developer]
end
describe "GET /:project/builds" do
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 5a62da10619..dacaa96d760 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -8,7 +8,6 @@ describe 'Commits' do
describe 'CI' do
before do
login_as :user
- project.team << [@user, :master]
stub_ci_commit_to_return_yaml_file
end
@@ -19,6 +18,10 @@ describe 'Commits' do
context 'commit status is Generic Commit Status' do
let!(:status) { FactoryGirl.create :generic_commit_status, commit: commit }
+ before do
+ project.team << [@user, :reporter]
+ end
+
describe 'Commit builds' do
before do
visit ci_status_path(commit)
@@ -37,85 +40,126 @@ describe 'Commits' do
context 'commit status is Ci Build' do
let!(:build) { FactoryGirl.create :ci_build, commit: commit }
+ let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
- describe 'Project commits' do
+ context 'when logged as developer' do
before do
- visit namespace_project_commits_path(project.namespace, project, :master)
+ project.team << [@user, :developer]
end
- it 'should show build status' do
- page.within("//li[@id='commit-#{commit.short_sha}']") do
- expect(page).to have_css(".ci-status-link")
+ describe 'Project commits' do
+ before do
+ visit namespace_project_commits_path(project.namespace, project, :master)
end
- end
- end
- describe 'Commit builds' do
- before do
- visit ci_status_path(commit)
+ it 'should show build status' do
+ page.within("//li[@id='commit-#{commit.short_sha}']") do
+ expect(page).to have_css(".ci-status-link")
+ end
+ end
end
- it { expect(page).to have_content commit.sha[0..7] }
- it { expect(page).to have_content commit.git_commit_message }
- it { expect(page).to have_content commit.git_author_name }
- end
-
- context 'Download artifacts' do
- let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
-
- before do
- build.update_attributes(artifacts_file: artifacts_file)
- end
+ describe 'Commit builds' do
+ before do
+ visit ci_status_path(commit)
+ end
- it do
- visit ci_status_path(commit)
- click_on 'Download artifacts'
- expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type)
+ it { expect(page).to have_content commit.sha[0..7] }
+ it { expect(page).to have_content commit.git_commit_message }
+ it { expect(page).to have_content commit.git_author_name }
end
- end
- describe 'Cancel all builds' do
- it 'cancels commit' do
- visit ci_status_path(commit)
- click_on 'Cancel running'
- expect(page).to have_content 'canceled'
- end
- end
+ context 'Download artifacts' do
+ before do
+ build.update_attributes(artifacts_file: artifacts_file)
+ end
- describe 'Cancel build' do
- it 'cancels build' do
- visit ci_status_path(commit)
- click_on 'Cancel'
- expect(page).to have_content 'canceled'
+ it do
+ visit ci_status_path(commit)
+ click_on 'Download artifacts'
+ expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type)
+ end
end
- end
- describe '.gitlab-ci.yml not found warning' do
- context 'ci builds enabled' do
- it "does not show warning" do
+ describe 'Cancel all builds' do
+ it 'cancels commit' do
visit ci_status_path(commit)
- expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
+ click_on 'Cancel running'
+ expect(page).to have_content 'canceled'
end
+ end
- it 'shows warning' do
- stub_ci_commit_yaml_file(nil)
+ describe 'Cancel build' do
+ it 'cancels build' do
visit ci_status_path(commit)
- expect(page).to have_content '.gitlab-ci.yml not found in this commit'
+ click_on 'Cancel'
+ expect(page).to have_content 'canceled'
end
end
- context 'ci builds disabled' do
- before do
- stub_ci_builds_disabled
- stub_ci_commit_yaml_file(nil)
- visit ci_status_path(commit)
+ describe '.gitlab-ci.yml not found warning' do
+ context 'ci builds enabled' do
+ it "does not show warning" do
+ visit ci_status_path(commit)
+ expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
+ end
+
+ it 'shows warning' do
+ stub_ci_commit_yaml_file(nil)
+ visit ci_status_path(commit)
+ expect(page).to have_content '.gitlab-ci.yml not found in this commit'
+ end
end
- it 'does not show warning' do
- expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
+ context 'ci builds disabled' do
+ before do
+ stub_ci_builds_disabled
+ stub_ci_commit_yaml_file(nil)
+ visit ci_status_path(commit)
+ end
+
+ it 'does not show warning' do
+ expect(page).not_to have_content '.gitlab-ci.yml not found in this commit'
+ end
end
end
end
+
+ context "when logged as reporter" do
+ before do
+ project.team << [@user, :reporter]
+ build.update_attributes(artifacts_file: artifacts_file)
+ visit ci_status_path(commit)
+ end
+
+ it do
+ expect(page).to have_content commit.sha[0..7]
+ expect(page).to have_content commit.git_commit_message
+ expect(page).to have_content commit.git_author_name
+ expect(page).to have_link('Download artifacts')
+ expect(page).to_not have_link('Cancel running')
+ expect(page).to_not have_link('Retry failed')
+ end
+ end
+
+ context 'when accessing internal project with disallowed access' do
+ before do
+ project.update(
+ visibility_level: Gitlab::VisibilityLevel::INTERNAL,
+ public_builds: false)
+ build.update_attributes(artifacts_file: artifacts_file)
+ visit ci_status_path(commit)
+ end
+
+ it do
+ expect(page).to have_content commit.sha[0..7]
+ expect(page).to have_content commit.git_commit_message
+ expect(page).to have_content commit.git_author_name
+ expect(page).to_not have_link('Download artifacts')
+ expect(page).to_not have_link('Cancel running')
+ expect(page).to_not have_link('Retry failed')
+ end
+ end
end
end
end
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index 655d2c8b7d9..b98476f854e 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -96,6 +96,60 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_denied_for :visitor }
end
+ describe "GET /:project_path/builds" do
+ subject { namespace_project_builds_path(project.namespace, project) }
+
+ context "when allowed for public" do
+ before { project.update(public_builds: true) }
+
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_allowed_for :visitor }
+ end
+
+ context "when disallowed for public" do
+ before { project.update(public_builds: false) }
+
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
+ end
+
+ describe "GET /:project_path/builds/:id" do
+ let(:commit) { create(:ci_commit, project: project) }
+ let(:build) { create(:ci_build, commit: commit) }
+ subject { namespace_project_build_path(project.namespace, project, build.id) }
+
+ context "when allowed for public" do
+ before { project.update(public_builds: true) }
+
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_allowed_for guest }
+ it { is_expected.to be_allowed_for :user }
+ it { is_expected.to be_allowed_for :visitor }
+ end
+
+ context "when disallowed for public" do
+ before { project.update(public_builds: false) }
+
+ it { is_expected.to be_allowed_for master }
+ it { is_expected.to be_allowed_for reporter }
+ it { is_expected.to be_allowed_for :admin }
+ it { is_expected.to be_denied_for guest }
+ it { is_expected.to be_denied_for :user }
+ it { is_expected.to be_denied_for :visitor }
+ end
+ end
+
describe "GET /:project_path/blob" do
before do
commit = project.repository.commit
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 7289e596ef3..82bd057b16c 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -270,6 +270,17 @@ describe Notify do
it 'contains a link to the new issue' do
is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
end
+
+ context 'when enabled email_author_in_body' do
+ before do
+ allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
+ end
+
+ it 'contains a link to note author' do
+ is_expected.to have_body_text issue.author_name
+ is_expected.to have_body_text /wrote\:/
+ end
+ end
end
describe 'that are new with a description' do
@@ -377,6 +388,17 @@ describe Notify do
it 'has the correct message-id set' do
is_expected.to have_header 'Message-ID', "<merge_request_#{merge_request.id}@#{Gitlab.config.gitlab.host}>"
end
+
+ context 'when enabled email_author_in_body' do
+ before do
+ allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
+ end
+
+ it 'contains a link to note author' do
+ is_expected.to have_body_text merge_request.author_name
+ is_expected.to have_body_text /wrote\:/
+ end
+ end
end
describe 'that are new with a description' do
@@ -550,6 +572,21 @@ describe Notify do
it 'contains the message from the note' do
is_expected.to have_body_text /#{note.note}/
end
+
+ it 'not contains note author' do
+ is_expected.not_to have_body_text /wrote\:/
+ end
+
+ context 'when enabled email_author_in_body' do
+ before do
+ allow(current_application_settings).to receive(:email_author_in_body).and_return(true)
+ end
+
+ it 'contains a link to note author' do
+ is_expected.to have_body_text note.author_name
+ is_expected.to have_body_text /wrote\:/
+ end
+ end
end
describe 'on a commit' do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index f4c58882757..161a32c51e6 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -66,6 +66,14 @@ describe ApplicationSetting, models: true do
it { is_expected.to allow_value(http).for(:after_sign_out_path) }
it { is_expected.to allow_value(https).for(:after_sign_out_path) }
it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) }
+
+ it { is_expected.to validate_presence_of(:max_attachment_size) }
+
+ it do
+ is_expected.to validate_numericality_of(:max_attachment_size)
+ .only_integer
+ .is_greater_than(0)
+ end
end
context 'restricted signup domains' do
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 021d62cdf0c..8f09ff03a78 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -79,6 +79,16 @@ describe Issue, "Issuable" do
expect(hook_data[:repository][:description]).to eq(issue.project.description)
expect(hook_data[:repository][:homepage]).to eq(issue.project.web_url)
expect(hook_data[:object_attributes]).to eq(issue.hook_attrs)
+ expect(hook_data).to_not have_key(:assignee)
+ end
+
+ context "issue is assigned" do
+ before { issue.update_attribute(:assignee, user) }
+
+ it "returns correct hook data" do
+ expect(hook_data[:object_attributes]['assignee_id']).to eq(user.id)
+ expect(hook_data[:assignee]).to eq(user.hook_attrs)
+ end
end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 30a71987d86..1b1380ce4e2 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -33,6 +33,20 @@ describe Milestone, models: true do
let(:milestone) { create(:milestone) }
let(:issue) { create(:issue) }
+ describe "unique milestone title per project" do
+ it "shouldn't accept the same title in a project twice" do
+ new_milestone = Milestone.new(project: milestone.project, title: milestone.title)
+ expect(new_milestone).not_to be_valid
+ end
+
+ it "should accept the same title in another project" do
+ project = build(:project)
+ new_milestone = Milestone.new(project: project, title: milestone.title)
+
+ expect(new_milestone).to be_valid
+ end
+ end
+
describe "#percent_complete" do
it "should not count open issues" do
milestone.issues << issue
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index c484ae8fc8c..e1ee43e64db 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -232,11 +232,132 @@ describe Repository, models: true do
end
describe 'when there are branches' do
- before do
- allow(repository.raw_repository).to receive(:branch_count).and_return(3)
+ it 'returns true' do
+ expect(repository.raw_repository).to receive(:branch_count).and_return(3)
+
+ expect(subject).to eq(true)
end
- it { is_expected.to eq(true) }
+ it 'caches the output' do
+ expect(repository.raw_repository).to receive(:branch_count).
+ once.
+ and_return(3)
+
+ repository.has_visible_content?
+ repository.has_visible_content?
+ end
+ end
+ end
+
+ describe '#empty?' do
+ let(:empty_repository) { create(:project_empty_repo).repository }
+
+ it 'returns true for an empty repository' do
+ expect(empty_repository.empty?).to eq(true)
+ end
+
+ it 'returns false for a non-empty repository' do
+ expect(repository.empty?).to eq(false)
+ end
+
+ it 'caches the output' do
+ expect(repository.raw_repository).to receive(:empty?).
+ once.
+ and_return(false)
+
+ repository.empty?
+ repository.empty?
+ end
+ end
+
+ describe '#root_ref' do
+ it 'returns a branch name' do
+ expect(repository.root_ref).to be_an_instance_of(String)
+ end
+
+ it 'caches the output' do
+ expect(repository.raw_repository).to receive(:root_ref).
+ once.
+ and_return('master')
+
+ repository.root_ref
+ repository.root_ref
+ end
+ end
+
+ describe '#expire_cache' do
+ it 'expires all caches' do
+ expect(repository).to receive(:expire_branch_cache)
+
+ repository.expire_cache
+ end
+
+ it 'expires the caches for a specific branch' do
+ expect(repository).to receive(:expire_branch_cache).with('master')
+
+ repository.expire_cache('master')
end
end
+
+ describe '#expire_root_ref_cache' do
+ it 'expires the root reference cache' do
+ repository.root_ref
+
+ expect(repository.raw_repository).to receive(:root_ref).
+ once.
+ and_return('foo')
+
+ repository.expire_root_ref_cache
+
+ expect(repository.root_ref).to eq('foo')
+ end
+ end
+
+ describe '#expire_has_visible_content_cache' do
+ it 'expires the visible content cache' do
+ repository.has_visible_content?
+
+ expect(repository.raw_repository).to receive(:branch_count).
+ once.
+ and_return(0)
+
+ repository.expire_has_visible_content_cache
+
+ expect(repository.has_visible_content?).to eq(false)
+ end
+ end
+
+ describe '#expire_branch_ache' do
+ # This method is private but we need it for testing purposes. Sadly there's
+ # no other proper way of testing caching operations.
+ let(:cache) { repository.send(:cache) }
+
+ it 'expires the cache for all branches' do
+ expect(cache).to receive(:expire).
+ at_least(repository.branches.length).
+ times
+
+ repository.expire_branch_cache
+ end
+
+ it 'expires the cache for all branches when the root branch is given' do
+ expect(cache).to receive(:expire).
+ at_least(repository.branches.length).
+ times
+
+ repository.expire_branch_cache(repository.root_ref)
+ end
+
+ it 'expires the cache for a specific branch' do
+ expect(cache).to receive(:expire).once
+
+ repository.expire_branch_cache('foo')
+ end
+ end
+
+ describe :skip_merged_commit do
+ subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", nil, 100, 0, true).map{ |k| k.id } }
+
+ it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') }
+ end
end
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index 8c9f5a382b7..6c07802db8b 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -113,7 +113,7 @@ describe API::API, api: true do
describe 'POST /projects/:id/builds/:build_id/cancel' do
context 'authorized user' do
- context 'user with :manage_builds persmission' do
+ context 'user with :update_build persmission' do
it 'should cancel running or pending build' do
post api("/projects/#{project.id}/builds/#{build.id}/cancel", user)
@@ -122,7 +122,7 @@ describe API::API, api: true do
end
end
- context 'user without :manage_builds permission' do
+ context 'user without :update_build permission' do
it 'should not cancel build' do
post api("/projects/#{project.id}/builds/#{build.id}/cancel", user2)
@@ -142,7 +142,7 @@ describe API::API, api: true do
describe 'POST /projects/:id/builds/:build_id/retry' do
context 'authorized user' do
- context 'user with :manage_builds persmission' do
+ context 'user with :update_build persmission' do
it 'should retry non-running build' do
post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user)
@@ -152,7 +152,7 @@ describe API::API, api: true do
end
end
- context 'user without :manage_builds permission' do
+ context 'user without :update_build permission' do
it 'should not retry build' do
post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user2)
diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb
index 21482fc1070..89b554622ef 100644
--- a/spec/requests/api/commit_status_spec.rb
+++ b/spec/requests/api/commit_status_spec.rb
@@ -2,18 +2,17 @@ require 'spec_helper'
describe API::CommitStatus, api: true do
include ApiHelpers
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
- let!(:project) { create(:project, creator_id: user.id) }
- let!(:reporter) { create(:project_member, user: user, project: project, access_level: ProjectMember::REPORTER) }
- let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
+ let!(:project) { create(:project) }
let(:commit) { project.repository.commit }
let!(:ci_commit) { project.ensure_ci_commit(commit.id) }
let(:commit_status) { create(:commit_status, commit: ci_commit) }
+ let(:guest) { create_user(ProjectMember::GUEST) }
+ let(:reporter) { create_user(ProjectMember::REPORTER) }
+ let(:developer) { create_user(ProjectMember::DEVELOPER) }
describe "GET /projects/:id/repository/commits/:sha/statuses" do
it_behaves_like 'a paginated resources' do
- let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) }
+ let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter) }
end
context "reporter user" do
@@ -29,7 +28,7 @@ describe API::CommitStatus, api: true do
end
it "should return latest commit statuses" do
- get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user)
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", reporter)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
@@ -39,7 +38,7 @@ describe API::CommitStatus, api: true do
end
it "should return all commit statuses" do
- get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", user)
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", reporter)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
@@ -47,7 +46,7 @@ describe API::CommitStatus, api: true do
end
it "should return latest commit statuses for specific ref" do
- get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", user)
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", reporter)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
@@ -55,7 +54,7 @@ describe API::CommitStatus, api: true do
end
it "should return latest commit statuses for specific name" do
- get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", user)
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", reporter)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
@@ -65,7 +64,7 @@ describe API::CommitStatus, api: true do
context "guest user" do
it "should not return project commits" do
- get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user2)
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", guest)
expect(response.status).to eq(403)
end
end
@@ -81,10 +80,10 @@ describe API::CommitStatus, api: true do
describe 'POST /projects/:id/statuses/:sha' do
let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" }
- context 'reporter user' do
+ context 'developer user' do
context 'should create commit status' do
it 'with only required parameters' do
- post api(post_url, user), state: 'success'
+ post api(post_url, developer), state: 'success'
expect(response.status).to eq(201)
expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success')
@@ -95,7 +94,7 @@ describe API::CommitStatus, api: true do
end
it 'with all optional parameters' do
- post api(post_url, user), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test'
+ post api(post_url, developer), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test'
expect(response.status).to eq(201)
expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success')
@@ -108,25 +107,32 @@ describe API::CommitStatus, api: true do
context 'should not create commit status' do
it 'with invalid state' do
- post api(post_url, user), state: 'invalid'
+ post api(post_url, developer), state: 'invalid'
expect(response.status).to eq(400)
end
it 'without state' do
- post api(post_url, user)
+ post api(post_url, developer)
expect(response.status).to eq(400)
end
it 'invalid commit' do
- post api("/projects/#{project.id}/statuses/invalid_sha", user), state: 'running'
+ post api("/projects/#{project.id}/statuses/invalid_sha", developer), state: 'running'
expect(response.status).to eq(404)
end
end
end
+ context 'reporter user' do
+ it 'should not create commit status' do
+ post api(post_url, reporter)
+ expect(response.status).to eq(403)
+ end
+ end
+
context 'guest user' do
it 'should not create commit status' do
- post api(post_url, user2)
+ post api(post_url, guest)
expect(response.status).to eq(403)
end
end
@@ -138,4 +144,10 @@ describe API::CommitStatus, api: true do
end
end
end
+
+ def create_user(access_level)
+ user = create(:user)
+ create(:project_member, user: user, project: project, access_level: access_level)
+ user
+ end
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index aa12d456aa9..4fd1df25568 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -116,6 +116,7 @@ describe API::API, api: true do
expect(response.status).to eq(200)
expect(json_response['title']).to eq(merge_request.title)
expect(json_response['iid']).to eq(merge_request.iid)
+ expect(json_response['merge_status']).to eq('can_be_merged')
end
it 'should return merge_request by iid' do
@@ -457,6 +458,28 @@ describe API::API, api: true do
end
end
+ describe 'GET :id/merge_requests/:merge_request_id/closes_issues' do
+ it 'returns the issue that will be closed on merge' do
+ issue = create(:issue, project: project)
+ mr = merge_request.tap do |mr|
+ mr.update_attribute(:description, "Closes #{issue.to_reference(mr.project)}")
+ end
+
+ get api("/projects/#{project.id}/merge_requests/#{mr.id}/closes_issues", user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(issue.id)
+ end
+
+ it 'returns an empty array when there are no issues to be closed' do
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/closes_issues", user)
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+ end
+
def mr_with_later_created_and_updated_at_time
merge_request
merge_request.created_at += 1.hour
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 244947762dd..01b369720ca 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -151,8 +151,8 @@ describe Ci::API::API do
context "Artifacts" do
let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
- let(:commit) { FactoryGirl.create(:ci_commit, project: project) }
- let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) }
+ let(:commit) { create(:ci_commit, project: project) }
+ let(:build) { create(:ci_build, commit: commit, runner_id: runner.id) }
let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") }
let(:post_url) { ci_api("/builds/#{build.id}/artifacts") }
let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") }
@@ -160,12 +160,10 @@ describe Ci::API::API do
let(:headers) { { "GitLab-Workhorse" => "1.0" } }
let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token) }
+ before { build.run! }
+
describe "POST /builds/:id/artifacts/authorize" do
context "should authorize posting artifact to running build" do
- before do
- build.run!
- end
-
it "using token as parameter" do
post authorize_url, { token: build.token }, headers
expect(response.status).to eq(200)
@@ -180,10 +178,6 @@ describe Ci::API::API do
end
context "should fail to post too large artifact" do
- before do
- build.run!
- end
-
it "using token as parameter" do
stub_application_setting(max_artifacts_size: 0)
post authorize_url, { token: build.token, filesize: 100 }, headers
@@ -197,8 +191,8 @@ describe Ci::API::API do
end
end
- context "should get denied" do
- it do
+ context 'token is invalid' do
+ it 'should respond with forbidden'do
post authorize_url, { token: 'invalid', filesize: 100 }
expect(response.status).to eq(403)
end
@@ -206,17 +200,13 @@ describe Ci::API::API do
end
describe "POST /builds/:id/artifacts" do
- context "Disable sanitizer" do
+ context "disable sanitizer" do
before do
# by configuring this path we allow to pass temp file from any path
allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return('/')
end
context "should post artifact to running build" do
- before do
- build.run!
- end
-
it "uses regual file post" do
upload_artifacts(file_upload, headers_with_token, false)
expect(response.status).to eq(201)
@@ -244,10 +234,7 @@ describe Ci::API::API do
let(:stored_artifacts_file) { build.reload.artifacts_file.file }
let(:stored_metadata_file) { build.reload.artifacts_metadata.file }
- before do
- build.run!
- post(post_url, post_data, headers_with_token)
- end
+ before { post(post_url, post_data, headers_with_token) }
context 'post data accelerated by workhorse is correct' do
let(:post_data) do
@@ -257,11 +244,8 @@ describe Ci::API::API do
'metadata.name' => metadata.original_filename }
end
- it 'responds with valid status' do
- expect(response.status).to eq(201)
- end
-
it 'stores artifacts and artifacts metadata' do
+ expect(response.status).to eq(201)
expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename)
expect(stored_metadata_file.original_filename).to eq(metadata.original_filename)
end
@@ -282,56 +266,42 @@ describe Ci::API::API do
end
end
-
- context "should fail to post too large artifact" do
- before do
- build.run!
- end
-
- it do
+ context "artifacts file is too large" do
+ it "should fail to post too large artifact" do
stub_application_setting(max_artifacts_size: 0)
upload_artifacts(file_upload, headers_with_token)
expect(response.status).to eq(413)
end
end
- context "should fail to post artifacts without file" do
- before do
- build.run!
- end
-
- it do
+ context "artifacts post request does not contain file" do
+ it "should fail to post artifacts without file" do
post post_url, {}, headers_with_token
expect(response.status).to eq(400)
end
end
- context "should fail to post artifacts without GitLab-Workhorse" do
- before do
- build.run!
- end
-
- it do
+ context 'GitLab Workhorse is not configured' do
+ it "should fail to post artifacts without GitLab-Workhorse" do
post post_url, { token: build.token }, {}
expect(response.status).to eq(403)
end
end
end
- context "should fail to post artifacts for outside of tmp path" do
+ context "artifacts are being stored outside of tmp path" do
before do
# by configuring this path we allow to pass file from @tmpdir only
# but all temporary files are stored in system tmp directory
@tmpdir = Dir.mktmpdir
allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir)
- build.run!
end
after do
FileUtils.remove_entry @tmpdir
end
- it do
+ it "should fail to post artifacts for outside of tmp path" do
upload_artifacts(file_upload, headers_with_token)
expect(response.status).to eq(400)
end
@@ -349,33 +319,37 @@ describe Ci::API::API do
end
end
- describe "DELETE /builds/:id/artifacts" do
- before do
- build.run!
- post delete_url, token: build.token, file: file_upload
- end
+ describe 'DELETE /builds/:id/artifacts' do
+ let(:build) { create(:ci_build, :artifacts) }
+ before { delete delete_url, token: build.token }
- it "should delete artifact build" do
- build.success
- delete delete_url, token: build.token
+ it 'should remove build artifacts' do
expect(response.status).to eq(200)
+ expect(build.artifacts_file.exists?).to be_falsy
+ expect(build.artifacts_metadata.exists?).to be_falsy
end
end
- describe "GET /builds/:id/artifacts" do
- before do
- build.run!
- end
+ describe 'GET /builds/:id/artifacts' do
+ before { get get_url, token: build.token }
- it "should download artifact" do
- build.update_attributes(artifacts_file: file_upload)
- get get_url, token: build.token
- expect(response.status).to eq(200)
+ context 'build has artifacts' do
+ let(:build) { create(:ci_build, :artifacts) }
+ let(:download_headers) do
+ { 'Content-Transfer-Encoding'=>'binary',
+ 'Content-Disposition'=>'attachment; filename=ci_build_artifacts.zip' }
+ end
+
+ it 'should download artifact' do
+ expect(response.status).to eq(200)
+ expect(response.headers).to include download_headers
+ end
end
- it "should fail to download if no artifact uploaded" do
- get get_url, token: build.token
- expect(response.status).to eq(404)
+ context 'build does not has artifacts' do
+ it 'should respond with not found' do
+ expect(response.status).to eq(404)
+ end
end
end
end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index c1080ef190a..eb3a5fe43f5 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -21,6 +21,18 @@ describe GitPushService, services: true do
end
it { is_expected.to be_truthy }
+
+ it 'flushes general cached data' do
+ expect(project.repository).to receive(:expire_cache).with('master')
+
+ subject
+ end
+
+ it 'flushes the visible content cache' do
+ expect(project.repository).to receive(:expire_has_visible_content_cache)
+
+ subject
+ end
end
context 'existing branch' do
@@ -29,6 +41,12 @@ describe GitPushService, services: true do
end
it { is_expected.to be_truthy }
+
+ it 'flushes general cached data' do
+ expect(project.repository).to receive(:expire_cache).with('master')
+
+ subject
+ end
end
context 'rm branch' do
@@ -37,6 +55,18 @@ describe GitPushService, services: true do
end
it { is_expected.to be_truthy }
+
+ it 'flushes the visible content cache' do
+ expect(project.repository).to receive(:expire_has_visible_content_cache)
+
+ subject
+ end
+
+ it 'flushes general cached data' do
+ expect(project.repository).to receive(:expire_cache).with('master')
+
+ subject
+ end
end
end