Commit ebe14437 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into refactor/ci-config-add-global-entry

* master: (59 commits)
  Improved SVG sanitizer specs to include smoke tests for clean.
  Refactored SVG sanitizer
  Added SVG sanitizer fix to the changelog
  Refactor SVG sanitizer and prevent `xlink:href` to refer to external resources
  Fix SVG whitelisting to allow namespaced attributes
  Fix Error 500 when using closes_issues API with an external issue tracker
  Center layout navigation and remove icons
  Fix preferences_spec test
  Add back sidebar counters and username
  Only create the backup directory if it is local
  Fix safari logo loading animation safari bug
  Revert side nav to full width; remove border under nav; remove tooltips on nav links; stop page content shifting with side nav; put project nav in container
  Update media queries
  Fix profile test
  Fix logo at all screen widths, update sidebar text
  Move tanuki icon to center of nav bar; keep nav closed by default; remove collapsed nav cookie
  Remove unused MergeRequest#gitlab_merge_status method
  Add CHANGELOG item for labels/milestones navigation change
  Render issues link on issues subnav unless you visit merge request controller
  Render only issues/mr in subnav depends on context
  ...
parents 11c0d022 06a99cf7
We’re closing our issue tracker on GitHub so we can focus on the GitLab.com project and respond to issues more quickly.
We encourage you to open an issue on the [GitLab.com issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues). You can log into GitLab.com using your GitHub account.
Thank you for taking the time to contribute back to GitLab!
Please open a merge request [on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests), we look forward to reviewing your contribution! You can log into GitLab.com using your GitHub account.
......@@ -13,7 +13,8 @@ AllCops:
# Exclude some GitLab files
Exclude:
- 'vendor/**/*'
- 'db/**/*'
- 'db/*'
- 'db/fixtures/**/*'
- 'tmp/**/*'
- 'bin/**/*'
- 'lib/backup/**/*'
......
Please view this file on the master branch, on stable branches it's out of date.
v 8.9.0 (unreleased)
- Fix Error 500 when using closes_issues API with an external issue tracker
- Bulk assign/unassign labels to issues.
- Ability to prioritize labels !4009 / !3205 (Thijs Wouters)
- Fix endless redirections when accessing user OAuth applications when they are disabled
- Allow enabling wiki page events from Webhook management UI
- Bump rouge to 1.11.0
- Fix issue with arrow keys not working in search autocomplete dropdown
- Make EmailsOnPushWorker use Sidekiq mailers queue
- Fix wiki page events' webhook to point to the wiki repository
- Fix issue todo not remove when leave project !4150 (Long Nguyen)
- Allow customisable text on the 'nearly there' page after a user signs up
- Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
- Fix SVG sanitizer to allow more elements
- Allow forking projects with restricted visibility level
- Added descriptions to notification settings dropdown
- Improve note validation to prevent errors when creating invalid note via API
......@@ -31,6 +34,7 @@ v 8.9.0 (unreleased)
- Add rake task 'gitlab:db:configure' for conditionally seeding or migrating the database
- Changed the Slack build message to use the singular duration if necessary (Aran Koning)
- Links from a wiki page to other wiki pages should be rewritten as expected
- Add option to project to only allow merge requests to be merged if the build succeeds (Rui Santos)
- Fix issues filter when ordering by milestone
- Todos will display target state if issuable target is 'Closed' or 'Merged'
- Fix bug when sorting issues by milestone due date and filtering by two or more labels
......@@ -54,6 +58,7 @@ v 8.9.0 (unreleased)
- Remove duplicated notification settings
- Put project Files and Commits tabs under Code tab
- Replace Colorize with Rainbow for coloring console output in Rake tasks.
- Add workhorse controller and API helpers
- An indicator is now displayed at the top of the comment field for confidential issues.
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
- Improve issuables APIs performance when accessing notes !4471
......@@ -61,15 +66,17 @@ v 8.9.0 (unreleased)
- Markdown editor now correctly resets the input value on edit cancellation !4175
- Toggling a task list item in a issue/mr description does not creates a Todo for mentions
- Improved UX of date pickers on issue & milestone forms
- Cache on the database if a project has an active external issue tracker.
- Put project Labels and Milestones pages links under Issues and Merge Requests tabs as subnav
v 8.8.5 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds
- Fix issue with arrow keys not working in search autocomplete dropdown
- Fix todos page throwing errors when you have a project pending deletion
- Reduce number of SQL queries when rendering user references
- Import GitHub repositories respecting the API rate limit
- Fix importer for GitHub comments on diff
- Disable Webhooks before proceeding with the GitHub import
- Fix incremental trace upload API when using multi-byte UTF-8 chars in trace
v 8.8.4
- Fix LDAP-based login for users with 2FA enabled. !4493
......
......@@ -162,19 +162,6 @@ $ ->
$el.data('placement') || 'bottom'
)
$('.header-logo .home').tooltip(
placement: (_, el) ->
$el = $(el)
if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom'
container: 'body'
)
$('.page-with-sidebar').tooltip(
selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user'
placement: 'right'
container: 'body'
)
# Form submitter
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
......@@ -207,6 +194,7 @@ $ ->
$('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle()
$('.header-content .header-logo').toggle()
$('.header-content .navbar-collapse').toggle()
$('.navbar-toggle').toggleClass('active')
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
......@@ -241,7 +229,6 @@ $ ->
$this.attr 'value', $this.val()
$sidebarGutterToggle = $('.js-sidebar-toggle')
$navIconToggle = $('.toggle-nav-collapse')
$(document)
.off 'breakpoint:change'
......@@ -251,10 +238,6 @@ $ ->
if $gutterIcon.hasClass('fa-angle-double-right')
$sidebarGutterToggle.trigger('click')
$navIcon = $navIconToggle.find('.fa')
if $navIcon.hasClass('fa-angle-left')
$navIconToggle.trigger('click')
fitSidebarForSize = ->
oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize()
......@@ -267,8 +250,8 @@ $ ->
$(document).trigger('breakpoint:change', [bootstrapBreakpoint])
$(window)
.off "resize"
.on "resize", (e) ->
.off "resize.app"
.on "resize.app", (e) ->
fitSidebarForSize()
gl.awardsHandler = new AwardsHandler()
......
class CiBuild
class @CiBuild
@interval: null
@state: null
constructor: (build_url, build_status, build_state) ->
constructor: (@build_url, @build_status, @state) ->
clearInterval(CiBuild.interval)
@state = build_state
# Init breakpoint checker
@bp = Breakpoints.get()
@hideSidebar()
$('.js-build-sidebar').niceScroll()
$(document)
.off 'click', '.js-sidebar-build-toggle'
.on 'click', '.js-sidebar-build-toggle', @toggleSidebar
$(window)
.off 'resize.build'
.on 'resize.build', @hideSidebar
if $('#build-trace').length
@getInitialBuildTrace()
@initScrollButtonAffix()
if build_status == "running" || build_status == "pending"
if @build_status is "running" or @build_status is "pending"
#
# Bind autoscroll button to follow build output
#
$("#autoscroll-button").bind "click", ->
$('#autoscroll-button').on 'click', ->
state = $(this).data("state")
if "enabled" is state
$(this).data "state", "disabled"
......@@ -27,25 +39,36 @@ class CiBuild
# Only valid for runnig build when output changes during time
#
CiBuild.interval = setInterval =>
if window.location.href.split("#").first() is build_url
last_state = @state
if window.location.href.split("#").first() is @build_url
@getBuildTrace()
, 4000
getInitialBuildTrace: ->
$.ajax
url: build_url + "/trace.json?state=" + encodeURIComponent(@state)
url: @build_url
dataType: 'json'
success: (build_data) ->
$('.js-build-output').html build_data.trace_html
if build_data.status is 'success' or build_data.status is 'failed'
$('.js-build-refresh').remove()
getBuildTrace: ->
$.ajax
url: "#{@build_url}/trace.json?state=#{encodeURIComponent(@state)}"
dataType: "json"
success: (log) =>
return unless last_state is @state
if log.state and log.status is "running"
if log.state
@state = log.state
if log.status is "running"
if log.append
$('.fa-refresh').before log.html
$('.js-build-output').append log.html
else
$('#build-trace code').html log.html
$('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
$('.js-build-output').html log.html
@checkAutoscroll()
else if log.status isnt build_status
Turbolinks.visit build_url
, 4000
else if log.status isnt @build_status
Turbolinks.visit @build_url
checkAutoscroll: ->
$("html,body").scrollTop $("#build-trace").height() if "enabled" is $("#autoscroll-button").data("state")
......@@ -61,4 +84,22 @@ class CiBuild
$body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top)
)
@CiBuild = CiBuild
shouldHideSidebar: ->
bootstrapBreakpoint = @bp.getBreakpointSize()
bootstrapBreakpoint is 'xs' or bootstrapBreakpoint is 'sm'
toggleSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.toggleClass 'right-sidebar-expanded right-sidebar-collapsed'
hideSidebar: =>
if @shouldHideSidebar()
$('.js-build-sidebar')
.removeClass 'right-sidebar-expanded'
.addClass 'right-sidebar-collapsed'
else
$('.js-build-sidebar')
.removeClass 'right-sidebar-collapsed'
.addClass 'right-sidebar-expanded'
......@@ -95,8 +95,11 @@ class @LabelsSelect
$newLabelCreateButton.enable()
if label.message?
errors = _.map label.message, (value, key) ->
"#{key} #{value[0]}"
$newLabelError
.text label.message
.html errors.join("<br/>")
.show()
else
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
......@@ -254,7 +257,7 @@ class @LabelsSelect
search:
fields: ['title']
selectable: true
filterable: true
toggleLabel: (selected, el) ->
selected_labels = $('.js-label-select').siblings('.dropdown-menu-labels').find('.is-active')
......
......@@ -47,4 +47,4 @@ $ ->
# Make logo clickable as part of a workaround for Safari visited
# link behaviour (See !2690).
$('#logo').on 'click', ->
$('#js-shortcuts-home').get(0).click()
Turbolinks.visit('/')
......@@ -7,12 +7,17 @@ class @ProjectNew
@toggleSettingsOnclick()
toggleSettings: ->
checked = $("#project_builds_enabled").prop("checked")
if checked
$('.builds-feature').show()
else
$('.builds-feature').hide()
toggleSettings: =>
@_showOrHide('#project_builds_enabled', '.builds-feature')
@_showOrHide('#project_merge_requests_enabled', '.merge-requests-feature')
toggleSettingsOnclick: ->
$("#project_builds_enabled").on 'click', @toggleSettings
$('#project_builds_enabled, #project_merge_requests_enabled').on 'click', @toggleSettings
_showOrHide: (checkElement, container) ->
$container = $(container)
if $(checkElement).prop('checked')
$container.show()
else
$container.hide()
......@@ -4,8 +4,6 @@ expanded = 'page-sidebar-expanded'
toggleSidebar = ->
$('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}")
$('header').toggleClass("header-collapsed header-expanded")
$('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left")
$.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' })
setTimeout ( ->
niceScrollBars = $('.nicescroll').niceScroll();
......@@ -17,10 +15,3 @@ $(document).on("click", '.toggle-nav-collapse, .side-nav-toggle', (e) ->
toggleSidebar()
)
$ ->
size = bp.getBreakpointSize()
if size is "xs" or size is "sm"
if $('.page-with-sidebar').hasClass(expanded)
toggleSidebar()
......@@ -95,7 +95,7 @@ class @UsersSelect
data: (term, callback) =>
isAuthorFilter = $('.js-author-search')
@users term, term is '' and isAuthorFilter, (users) =>
@users term, (users) =>
if term.length is 0
showDivider = 0
......@@ -221,7 +221,7 @@ class @UsersSelect
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) =>
@users query.term, @projectId?, (users) =>
@users query.term, (users) =>
data = { results: users }
if query.term.length == 0
......@@ -304,7 +304,7 @@ class @UsersSelect
# Return users list. Filtered by query
# Only active users retrieved
users: (query, fromProject, callback) =>
users: (query, callback) =>
url = @buildUrl(@usersPath)
$.ajax(
......@@ -313,7 +313,7 @@ class @UsersSelect
search: query
per_page: 20
active: true
project_id: @projectId if fromProject
project_id: @projectId
group_id: @groupId
current_user: @showCurrentUser
author_id: @authorId
......
......@@ -8,32 +8,14 @@
*/
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
.page-with-sidebar {
.header-logo {
background: $color-darker;
a {
color: $color-light;
h3 {
.collapse-nav a {
color: $color-light;
}
}
background: $color;
&:hover {
background-color: $color-dark;
a {
color: $white-light;
h3 {
color: $white-light;
}
}
}
}
.collapse-nav a {
color: $white-light;
background: $color;
}
.sidebar-wrapper {
......
......@@ -109,10 +109,8 @@ header {
position: relative;
height: $header-height;
padding-right: 40px;
@media (max-width: $screen-xs-min) {
padding-left: 40px;
}
padding-left: 30px;
transition-duration: .3s;
@media (min-width: $screen-sm-min) {
padding-right: 0;
......@@ -122,9 +120,29 @@ header {
margin-top: -5px;
}
.header-logo {
position: absolute;
left: 50%;
margin-left: -18px;
top: 7px;
transition-duration: .3s;
z-index: 999;
&:hover {
cursor: pointer;
}
@media (max-width: $screen-xs-max) {
right: 25px;
left: auto;
}
}
.title {
margin: 0;
font-size: 19px;
max-width: 400px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
color: $gl-text-color;
......@@ -133,6 +151,10 @@ header {
vertical-align: top;
white-space: nowrap;
@media (max-width: $screen-sm-max) {
max-width: 190px;
}
a {
color: $gl-text-color;
&:hover {
......@@ -160,6 +182,10 @@ header {
.navbar-collapse {
float: right;
border-top: none;
@media (max-width: $screen-xs-max) {
float: none;
}
}
}
......@@ -176,17 +202,20 @@ header {
margin-left: 0;
.header-content {
@media (min-width: $screen-sm-max) {
padding-left: 30px;
transition-duration: .3s;
}
}
}
.header-expanded {
margin-left: 0;
.tanuki-shape {
transition: all 0.8s;
.header-content {
padding-left: $sidebar_width;
transition-duration: .3s;
&:hover, &.highlight {
fill: rgb(255, 255, 255);
transition: all 0.1s;
}
}
......
@mixin fade($gradient-direction, $rgba, $gradient-color) {
visibility: visible;
opacity: 1;
z-index: 2;
position: absolute;
bottom: 12px;
width: 43px;
......@@ -68,6 +69,7 @@
}
&.sub-nav {
text-align: center;
background-color: $background-color;
.container-fluid {
......@@ -171,7 +173,6 @@
> form {
display: inline-block;
margin-top: -1px;
margin-bottom: 12px;
}
.icon-label {
......@@ -250,6 +251,7 @@
background: $background-color;
border-bottom: 1px solid $border-color;
transition-duration: .3s;
text-align: center;
.container-fluid {
position: relative;
......@@ -352,7 +354,7 @@
.fade-right {
@media (min-width: $screen-xs-max) {
right: 67px;
right: 68px;
}
@media (max-width: $screen-xs-min) {
right: 0;
......
......@@ -35,24 +35,11 @@
}
.sidebar-wrapper {
.header-logo {
height: $header-height;
padding: 8px 26px;
width: $sidebar_width;
position: fixed;
z-index: 999;
overflow: hidden;
transition-duration: .3s;
&:hover {
background-color: #eee;
}
}
.sidebar-user {
padding: 15px 22px;
position: fixed;
bottom: 40px;
bottom: 0;
width: $sidebar_width;
overflow: hidden;
transition-duration: .3s;
......@@ -97,10 +84,10 @@
}
a {
text-align: center;
padding: 8px;
width: $sidebar_width;
padding: 7px 15px 7px 23px;
font-size: $gl-font-size;
color: $gray;
line-height: 24px;
display: block;
text-decoration: none;
font-weight: normal;
......@@ -118,10 +105,9 @@
font-size: 16px;
}
.nav-link-text {
margin-top: 3px;
font-size: 13px;
line-height: 18px;
i,
svg {
margin-right: 13px;
}
&.back-link i {
......@@ -129,6 +115,12 @@
}
}
}
.count {
float: right;
padding: 0 8px;
@include border-radius(6px);
}
}
.sidebar-subnav {
......@@ -143,11 +135,12 @@
.collapse-nav a {
width: $sidebar_width;
position: fixed;
bottom: 0;
top: 0;
left: 0;
font-size: 13px;
padding: 5px 0;
font-size: 18px;
background: transparent;
height: 40px;
height: 50px;
text-align: center;
line-height: 40px;
transition-duration: .3s;
......@@ -170,25 +163,8 @@
.sidebar-wrapper {
width: 0;
.header-logo {
width: 0;
padding: 8px 0;
a {
padding-left: ($sidebar_collapsed_width - 36) / 2;
.gitlab-text-container {
display: none;
}
}
}
#logo {
display: none;
}
.nav-sidebar {
width: $sidebar_collapsed_width;
width: 0;
li {
width: auto;
......@@ -203,6 +179,10 @@
.collapse-nav a {
width: 0;
i {
display: none;
}
}
.sidebar-user {
......@@ -218,9 +198,8 @@
}
.page-sidebar-expanded {
padding-left: $sidebar_width;
@media (max-width: $screen-xs-min) {
@media (max-width: $screen-sm-max) {
padding-left: 0;
}
......@@ -241,20 +220,6 @@
}
}
}
.layout-nav {
@media (max-width: $screen-xs-min) {
padding-right: 0;
}
@media (min-width: $screen-xs-min) and (max-width: $screen-md-min) {
padding-right: 90px;
}
@media (min-width: $screen-md-min) {
padding-right: $sidebar_width;
}
}
}
.right-sidebar-collapsed {
......@@ -273,8 +238,10 @@
padding-right: 0;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
&:not(.build-sidebar) {
padding-right: $sidebar_collapsed_width;
}
}
@media (min-width: $screen-md-min) {
padding-right: $gutter_width;
......
......@@ -2,7 +2,7 @@
* Layout
*/
$sidebar_collapsed_width: 62px;
$sidebar_width: 90px;
$sidebar_width: 220px;
$gutter_collapsed_width: 62px;
$gutter_width: 290px;
$gutter_inner_width: 258px;
......@@ -260,3 +260,6 @@ $calendar-header-color: #b8b8b8;
$calendar-hover-bg: #ecf3fe;
$calendar-border-color: rgba(#000, .1);
$calendar-unselectable-bg: #faf9f9;
$ci-output-bg: #1d1f21;
$ci-text-color: #c5c8c6;
......@@ -101,13 +101,21 @@
line-height: 20px;
outline: 0;
&:hover,
&.active,
&:active {
background-color: $white-dark;
background-color: $row-hover;
border-color: $row-hover-border;
box-shadow: none;
outline: 0;
}
&.btn {
&:focus {
outline: 0;
}
}
&.is-loading {
.award-control-icon-normal,
.emoji-icon {
......
......@@ -53,37 +53,92 @@
left: 70px;
}
}
}
.build-widget {
padding: 10px;
background: $background-color;
margin-bottom: 20px;
border-radius: 4px;
.build-header {
position: relative;
padding-right: 40px;
.title {
margin-top: 0;
color: #666;
line-height: 1.5;
@media (min-width: $screen-sm-min) {
padding-right: 0;
}
.attr-name {
color: #777;
a {
color: $gl-gray;
&:hover {
color: $gl-link-color;
text-decoration: none;
}
}
.alert-disabled {
background: $background-color;
a {
color: #3084bb !important;
code {
color: $code-color;
}
.avatar {
float: none;
margin-right: 2px;
margin-left: 2px;
}
}
table.builds {
.build-link {
a {
color: $gl-dark-link-color;
}
}
}
.build-trace {
background: $ci-output-bg;
color: $ci-text-color;
white-space: pre;
overflow-x: auto;
font-size: 12px;
.fa-refresh {
font-size: 24px;
}
.bash {
display: block;
}
}
.right-sidebar.build-sidebar {
padding-top: $gl-padding;
padding-bottom: $gl-padding;
&.right-sidebar-collapsed {
display: none;
}
.block {
width: 100%;
}
.build-sidebar-header {
padding-top: 0;
.gutter-toggle {
margin-top: 0;
}
}
}
.build-detail-row {
margin-bottom: 5px;
}
.build-light-text {
color: $gl-placeholder-color;
}
.build-gutter-toggle {
position: absolute;
top: 50%;
right: 0;
margin-top: -17px;
}
......@@ -29,7 +29,7 @@
}
}
.issuable-sidebar {
.right-sidebar {
a {
color: inherit;
}
......@@ -74,6 +74,10 @@
}
}
.block-first {
padding-top: 0;
}
.title {
color: $gl-text-color;
margin-bottom: 10px;
......
......@@ -11,18 +11,15 @@
$magenta: #cd00cd;
$cyan: #00cdcd;
$white: #e5e5e5;
$l-black: #7f7f7f;
$l-red: #f00;
$l-green: #0f0;
$l-yellow: #ff0;
$l-blue: #5c5cff;
$l-magenta: #f0f;
$l-cyan: #0ff;
$l-white: #fff;
$l-black: #373b41;
$l-red: #c66;
$l-green: #b5bd68;
$l-yellow: #f0c674;
$l-blue: #81a2be;
$l-magenta: #b294bb;
$l-cyan: #8abeb7;
$l-white: $ci-text-color;
.term-bold {
font-weight: bold;
}
.term-italic {
font-style: italic;
}
......
......@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
include Gitlab::GonHelper
include GitlabRoutingHelper
include PageLayoutHelper
include WorkhorseHelper
before_action :authenticate_user_from_token!
before_action :authenticate_user!
......
......@@ -10,10 +10,7 @@ class Projects::AvatarsController < Projects::ApplicationController
return if cached_blob?
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type
send_git_blob @repository, @blob
else
render_404
end
......
......@@ -41,7 +41,7 @@ class Projects::BuildsController < Projects::ApplicationController
def trace
respond_to do |format|
format.json do
render json: @build.trace_with_state(params[:state]).merge!(id: @build.id, status: @build.status)
render json: @build.trace_with_state(params[:state].presence).merge!(id: @build.id, status: @build.status)
end
end
end
......
......@@ -61,12 +61,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
format.json { render json: @merge_request }
format.patch { render text: @merge_request.to_patch }
format.diff do
headers.store(*Gitlab::Workhorse.send_git_diff(@project.repository,
@merge_request.diff_base_commit.id,
@merge_request.last_commit.id))
headers['Content-Disposition'] = 'inline'
return render_404 unless @merge_request.diff_refs
head :ok
send_git_diff @project.repository, @merge_request.diff_refs
end
end
end
......
......@@ -18,10 +18,7 @@ class Projects::RawController < Projects::ApplicationController
if @blob.lfs_pointer?
send_lfs_object
else
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob))
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type
send_git_blob @repository, @blob
end
else
render_404
......
......@@ -11,8 +11,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
end
def archive
headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format]))
head :ok
send_git_archive @repository, ref: params[:ref], format: params[:format]
rescue => ex
logger.error("#{self.class.name}: #{ex}")
return git_not_found!
......
......@@ -234,7 +234,7 @@ class ProjectsController < Projects::ApplicationController
: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,
:public_builds, :only_allow_merge_if_build_succeeds
)
end
......
......@@ -67,9 +67,9 @@ module DropdownsHelper
end
end
def dropdown_filter(placeholder)
def dropdown_filter(placeholder, search_id: nil)
content_tag :div, class: "dropdown-input" do
filter_output = search_field_tag nil, nil, class: "dropdown-input-field", placeholder: placeholder
filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder
filter_output << icon('search', class: "dropdown-input-search")
filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button")
......
......@@ -30,17 +30,13 @@ module NavHelper
else
"page-gutter right-sidebar-expanded"
end
elsif current_path?('builds#show')
"page-gutter build-sidebar right-sidebar-expanded"
end
end
def nav_header_class
class_name =
if nav_menu_collapsed?
"header-collapsed"
else
"header-expanded"
end
class_name += " with-horizontal-nav" if defined?(nav) && nav
class_name = " with-horizontal-nav" if defined?(nav) && nav
class_name
end
......
# Helpers to send Git blobs, diffs or archives through Workhorse.
# Workhorse will also serve files when using `send_file`.
module WorkhorseHelper
# Send a Git blob through Workhorse
def send_git_blob(repository, blob)
headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(blob)
head :ok # 'render nothing: true' messes up the Content-Type
end
# Send a Git diff through Workhorse
def send_git_diff(repository, diff_refs)
headers.store(*Gitlab::Workhorse.send_git_diff(repository, diff_refs))
headers['Content-Disposition'] = 'inline'
head :ok
end
# Archive a Git repository and send it through Workhorse
def send_git_archive(repository, ref:, format:)
headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
head :ok
end
end
......@@ -194,7 +194,7 @@ module Ci
def trace_length
if raw_trace
raw_trace.length
raw_trace.bytesize
else
0
end
......@@ -216,7 +216,7 @@ module Ci
recreate_trace_dir
File.truncate(path_to_trace, offset) if File.exist?(path_to_trace)
File.open(path_to_trace, 'a') do |f|
File.open(path_to_trace, 'ab') do |f|
f.write(trace_part)
end
end
......
......@@ -3,7 +3,7 @@ module Ci
extend Ci::Model
belongs_to :trigger, class_name: 'Ci::Trigger'
belongs_to :commit, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
has_many :builds, class_name: 'Ci::Build'
serialize :variables
......
......@@ -260,19 +260,20 @@ class MergeRequest < ActiveRecord::Base
end
def mergeable?
return false unless open? && !work_in_progress? && !broken?
return false unless mergeable_state?
check_if_can_be_merged
can_be_merged?
end
def gitlab_merge_status
if work_in_progress?
"work_in_progress"
else
merge_status_name
end
def mergeable_state?
return false unless open?
return false if work_in_progress?
return false if broken?
return false unless mergeable_ci_state?
true
end
def can_cancel_merge_when_build_succeeds?(current_user)
......@@ -481,6 +482,12 @@ class MergeRequest < ActiveRecord::Base
::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch)
end
def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds?
!pipeline || pipeline.success?
end
def state_human_name
if merged?
"Merged"
......
......@@ -253,20 +253,69 @@ class Project < ActiveRecord::Base
non_archived.where(table[:name].matches(pattern))
end
def find_with_namespace(id)
namespace_path, project_path = id.split('/', 2)
# Finds a single project for the given path.
#
# path - The full project path (including namespace path).
#
# Returns a Project, or nil if no project could be found.
def find_with_namespace(path)
where_paths_in([path]).reorder(nil).take
end
return nil if !namespace_path || !project_path
# Builds a relation to find multiple projects by their full paths.
#
# Each path must be in the following format:
#
# namespace_path/project_path
#
# For example:
#
# gitlab-org/gitlab-ce
#
# Usage:
#
# Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee})
#
# This would return the projects with the full paths matching the values
# given.
#
# paths - An Array of full paths (namespace path + project path) for which
# to find the projects.
#
# Returns an ActiveRecord::Relation.
def where_paths_in(paths)
wheres = []
cast_lower = Gitlab::Database.postgresql?
# Use of unscoped ensures we're not secretly adding any ORDER BYs, which
# have a negative impact on performance (and aren't needed for this
# query).
projects = unscoped.
joins(:namespace).
iwhere('namespaces.path' => namespace_path)
paths.each do |path|
namespace_path, project_path = path.split('/', 2)
next unless namespace_path && project_path
namespace_path = connection.quote(namespace_path)
project_path = connection.quote(project_path)
projects.find_by('projects.path' => project_path) ||
projects.iwhere('projects.path' => project_path).take
where = "(namespaces.path = #{namespace_path}
AND projects.path = #{project_path})"
if cast_lower
where = "(
#{where}
OR (
LOWER(namespaces.path) = LOWER(#{namespace_path})
AND LOWER(projects.path) = LOWER(#{project_path})
)
)"
end
wheres << where
end
if wheres.empty?
none
else
joins(:namespace).where(wheres.join(' OR '))
end
end
def visibility_levels
......@@ -523,9 +572,21 @@ class Project < ActiveRecord::Base
end
def external_issue_tracker
if has_external_issue_tracker.nil? # To populate existing projects
cache_has_external_issue_tracker
end
if has_external_issue_tracker?
return @external_issue_tracker if defined?(@external_issue_tracker)
@external_issue_tracker ||=
services.issue_trackers.active.without_defaults.first
@external_issue_tracker = services.external_issue_trackers.first
else
nil
end
end
def cache_has_external_issue_tracker
update_column(:has_external_issue_tracker, services.external_issue_trackers.any?)
end
def can_have_issues_tracker_id?
......
......@@ -16,6 +16,7 @@ class Service < ActiveRecord::Base
after_initialize :initialize_properties
after_commit :reset_updated_properties
after_commit :cache_project_has_external_issue_tracker
belongs_to :project
has_one :service_hook
......@@ -34,6 +35,7 @@ class Service < ActiveRecord::Base
scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
scope :external_issue_trackers, -> { issue_trackers.active.without_defaults }
default_value_for :category, 'common'
......@@ -192,4 +194,12 @@ class Service < ActiveRecord::Base
service.project_id = project_id
service if service.save
end
private
def cache_project_has_external_issue_tracker
if project && !project.destroyed?
project.cache_has_external_issue_tracker
end
end
end
......@@ -11,7 +11,7 @@ module Ci
trigger_request = trigger.trigger_requests.create!(
variables: variables,
commit: pipeline,
pipeline: pipeline,
)
if pipeline.create_builds(nil, trigger_request)
......
- if nav_menu_collapsed?
= link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
- else
= link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
= link_to icon('bars'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
.page-with-sidebar.page-sidebar-collapsed{ class: "#{page_gutter_class}" }
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
= link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do
.header-logo
#logo
= brand_header_logo
- if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}"
......@@ -16,7 +12,9 @@
= render partial: 'layouts/collapse_button'
- if current_user
= link_to current_user, class: 'sidebar-user', title: "Profile", data: {user: current_user.username} do
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s46'
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
.username
= current_user.username
- if defined?(nav) && nav
.layout-nav
.container-fluid
......
.page-with-sidebar{ class: page_sidebar_class }
= render "layouts/broadcast"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo
%a#logo
= brand_header_logo
= link_to root_path, class: 'gitlab-text-container-link', title: 'Dashboard', id: 'js-shortcuts-home' do
.gitlab-text-container
%h3 GitLab
- if defined?(sidebar) && sidebar
= render "layouts/ci/#{sidebar}"
......
%header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class }
%header.navbar.navbar-fixed-top.navbar-gitlab.header-collapsed{ class: nav_header_class }
%div{ class: fluid_layout ? "container-fluid" : "container-fluid" }
.header-content
%button.side-nav-toggle{type: 'button'}
......@@ -50,6 +50,10 @@
%h1.title= title
.header-logo
#logo
= brand_header_logo
= yield :header_content
= render 'shared/outdated_browser'
......
......@@ -2,102 +2,106 @@
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview' do
= icon('dashboard fw')
.nav-link-text
%span
Overview
= nav_link(controller: [:admin, :projects]) do
= link_to admin_namespaces_projects_path, title: 'Projects' do
= icon('cube fw')
.nav-link-text
%span
Projects
= nav_link(controller: :users) do
= link_to admin_users_path, title: 'Users' do
= icon('user fw')
.nav-link-text
%span
Users
= nav_link(controller: :groups) do
= link_to admin_groups_path, title: 'Groups' do
= icon('group fw')
.nav-link-text
%span
Groups
= nav_link(controller: :deploy_keys) do
= link_to admin_deploy_keys_path, title: 'Deploy Keys' do
= icon('key fw')
.nav-link-text
%span
Deploy Keys
= nav_link path: ['runners#index', 'runners#show'] do
= link_to admin_runners_path, title: 'Runners' do
= icon('cog fw')
.nav-link-text
%span
Runners
%span.count= number_with_delimiter(Ci::Runner.count(:all))
= nav_link path: 'builds#index' do
= link_to admin_builds_path, title: 'Builds' do
= icon('link fw')
.nav-link-text
%span
Builds
%span.count= number_with_delimiter(Ci::Build.count(:all))
= nav_link(controller: :logs) do
= link_to admin_logs_path, title: 'Logs' do
= icon('file-text fw')
.nav-link-text
%span
Logs
= nav_link(controller: :health_check) do
= link_to admin_health_check_path, title: 'Health Check' do
= icon('medkit fw')
.nav-link-text
%span
Health Check
= nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path, title: 'Messages' do
= icon('bullhorn fw')
.nav-link-text
%span
Messages
= nav_link(controller: :hooks) do
= link_to admin_hooks_path, title: 'Hooks' do
= icon('external-link fw')
.nav-link-text
%span
Hooks
= nav_link(controller: :background_jobs) do
= link_to admin_background_jobs_path, title: 'Background Jobs' do
= icon('cog fw')
.nav-link-text
%span
Background Jobs
= nav_link(controller: :appearances) do
= link_to admin_appearances_path, title: 'Appearances' do
= icon('image')
.nav-link-text
%span
Appearance
= nav_link(controller: :applications) do
= link_to admin_applications_path, title: 'Applications' do
= icon('cloud fw')
.nav-link-text
%span
Applications
= nav_link(controller: :services) do
= link_to admin_application_settings_services_path, title: 'Service Templates' do
= icon('copy fw')
.nav-link-text
%span
Service Templates
= nav_link(controller: :labels) do
= link_to admin_labels_path, title: 'Labels' do
= icon('tags fw')
.nav-link-text
%span
Labels
= nav_link(controller: :abuse_reports) do
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
= icon('exclamation-circle fw')
.nav-link-text
%span
Abuse Reports
%span.count= number_with_delimiter(AbuseReport.count(:all))
- if askimet_enabled?
= nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path, title: "Spam Logs" do
= icon('exclamation-triangle fw')
.nav-link-text
%span
Spam Logs
%span.count= number_with_delimiter(SpamLog.count(:all))
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do
= icon('cogs fw')
.nav-link-text
%span
Settings
......@@ -2,50 +2,53 @@
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
= navbar_icon('project')
.nav-link-text
%span
Projects
= nav_link(controller: :todos) do
= link_to dashboard_todos_path, title: 'Todos' do
= icon('bell fw')
.nav-link-text
%span
Todos
%span.count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
= navbar_icon('activity')
.nav-link-text
%span
Activity
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, title: 'Groups' do
= navbar_icon('group')
.nav-link-text
%span
Groups
= nav_link(controller: 'dashboard/milestones') do
= link_to dashboard_milestones_path, title: 'Milestones' do
= navbar_icon('milestones')
.nav-link-text
%span
Milestones
= nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
= navbar_icon('issues')
.nav-link-text
%span
Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
= navbar_icon('mr')
.nav-link-text
%span
Merge Requests
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
= nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
.nav-link-text
%span
Snippets
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
= icon('question-circle fw')
.nav-link-text
%span
Help
= nav_link(html_options: {class: profile_tab_class}) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
= icon('user fw')
.nav-link-text
%span
Profile Settings
......@@ -2,20 +2,20 @@
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do
= icon('bookmark fw')
.nav-link-text
%span
Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to explore_groups_path, title: 'Groups' do
= icon('group fw')
.nav-link-text
%span
Groups
= nav_link(controller: :snippets) do
= link_to explore_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
.nav-link-text
%span
Snippets
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
= icon('question-circle fw')
.nav-link-text
%span
Help
......@@ -5,36 +5,30 @@
.fade-left
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do
= navbar_icon('group')
%span
Group
= nav_link(path: 'groups#activity') do
= link_to activity_group_path(@group), title: 'Activity' do
= navbar_icon('activity')
%span
Activity
= nav_link(controller: [:group, :milestones]) do
= link_to group_milestones_path(@group), title: 'Milestones' do
= navbar_icon('milestones')
%span
Milestones
= nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group), title: 'Issues' do
= navbar_icon('issues')
%span
Issues
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(issues.count)
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
= navbar_icon('mr')
%span
Merge Requests
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(merge_requests.count)
= nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do
= navbar_icon('members')
%span
Members
.fade-right
......@@ -2,51 +2,41 @@
.fade-left
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do
= icon('user fw')
%span
Profile
= nav_link(controller: [:accounts, :two_factor_auths]) do
= link_to profile_account_path, title: 'Account' do
= icon('gear fw')
%span
Account
- if current_application_settings.user_oauth_applications?
= nav_link(controller: 'oauth/applications') do
= link_to applications_profile_path, title: 'Applications' do
= icon('cloud fw')
%span
Applications
= nav_link(controller: :emails) do
= link_to profile_emails_path, title: 'Emails' do
= icon('envelope-o fw')
%span
Emails
- unless current_user.ldap_user?
= nav_link(controller: :passwords) do
= link_to edit_profile_password_path, title: 'Password' do
= icon('lock fw')
%span
Password
= nav_link(controller: :notifications) do
= link_to profile_notifications_path, title: 'Notifications' do
= icon('inbox fw')
%span
Notifications
= nav_link(controller: :keys) do
= link_to profile_keys_path, title: 'SSH Keys' do
= icon('key fw')
%span
SSH Keys
= nav_link(controller: :preferences) do
= link_to profile_preferences_path, title: 'Preferences' do
-# TODO (rspeicher): Better icon?
= icon('image fw')
%span
Preferences
= nav_link(path: 'profiles#audit_log') do
= link_to audit_log_profile_path, title: 'Audit Log' do
= icon('history fw')
%span
Audit Log
.fade-right
......@@ -24,55 +24,41 @@
.fade-left
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
= navbar_icon('project')
%span
Project
= nav_link(path: 'projects#activity') do
= link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do
= navbar_icon('activity')
%span
Activity
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
= link_to project_files_path(@project), title: 'Code', class: 'shortcuts-tree' do
= icon('code fw')
%span
Code
- if project_nav_tab? :pipelines
= nav_link(controller: :pipelines) do
= link_to project_pipelines_path(@project), title: 'Pipelines', class: 'shortcuts-pipelines' do
= navbar_icon('pipelines')
%span
Pipelines
- if project_nav_tab? :container_registry
= nav_link(controller: %w(container_registry)) do
= link_to project_container_registry_path(@project), title: 'Container Registry', class: 'shortcuts-container-registry' do
= icon('hdd-o fw')
%span
Registry
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
= link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do
= icon('area-chart fw')
%span
Graphs
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
= navbar_icon('milestones')
%span
Milestones
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= nav_link(controller: [:issues, :labels, :milestones]) do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do
= navbar_icon('issues')
%span
Issues
- if @project.default_issues_tracker?
......@@ -81,29 +67,19 @@
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
= navbar_icon('mr')
%span
Merge Requests
%span.badge.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count)
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
= icon('tags fw')
%span
Labels
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do
= navbar_icon('wiki')
%span
Wiki
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
= icon('clipboard fw')
%span
Snippets
......@@ -129,5 +105,4 @@
%li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits
.fade-right
%fieldset.builds-feature
%h5.prepend-top-0
Merge Requests
.form-group
.checkbox
= f.label :only_allow_merge_if_build_succeeds do
= f.check_box :only_allow_merge_if_build_succeeds
%strong Only allow merge requests to be merged if the build succeeds
.help-block
Builds need to be configured to enable this feature.
= link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
- header_title project_title(@project, "Builds", project_builds_path(@project))
.top-block.row-content-block.clearfix
.pull-right
......
.content-block.build-header
= ci_status_with_icon(@build.status)
Build
%strong ##{@build.id}
for commit
= link_to ci_status_path(@build.pipeline) do
%strong= @build.pipeline.short_sha
from
= link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
%code
= @build.ref
- if @build.user
= render "user"
= time_ago_with_tooltip(@build.created_at)
%button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
= icon('angle-double-left')
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
Build
%strong ##{@build.id}
%a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
= icon('angle-double-right')
- if @build.coverage
.block.block-first
.title
Test coverage
%p.build-detail-row
#{@build.coverage}%
- if can?(current_user, :read_build, @project) && @build.artifacts?
.block{ class: ("block-first" if !@build.coverage) }
.title
Build artifacts
.btn-group.btn-group-justified{ role: :group }
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Download
- if @build.artifacts_metadata?
= link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
Browse
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && @build.artifacts?)) }
.title
Build details
- if @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right', method: :post
- if @build.merge_request
%p.build-detail-row
%span.build-light-text Merge Request:
= link_to "#{@build.merge_request.to_reference}", merge_request_path(@build.merge_request)
- if @build.duration
%p.build-detail-row
%span.build-light-text Duration:
#{duration_in_words(@build.finished_at, @build.started_at)}
- if @build.finished_at
%p.build-detail-row
%span.build-light-text Finished:
#{time_ago_with_tooltip(@build.finished_at)}
- if @build.erased_at
%p.build-detail-row
%span.build-light-text Erased:
#{time_ago_with_tooltip(@build.erased_at)}
%p.build-detail-row
%span.build-light-text Runner:
- if @build.runner && current_user && current_user.admin
= link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
- elsif @build.runner
\##{@build.runner.id}
.btn-group.btn-group-justified{ role: :group }
- if @build.has_trace?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default'
- if @build.active?
= link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post
- if can?(current_user, :update_build, @project) && @build.erasable?
= link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
class: "btn btn-sm btn-default", method: :post,
data: { confirm: "Are you sure you want to erase this build?" } do
Erase
- if @build.trigger_request
.build-widget
%h4.title
Trigger
%p
%span.build-light-text Token:
#{@build.trigger_request.trigger.short_token}
- if @build.trigger_request.variables
%p
%span.build-light-text Variables:
%code
- @build.trigger_request.variables.each do |key, value|
#{key}=#{value}
.block
.title
Commit message
%p.build-light-text.append-bottom-0
#{@build.pipeline.git_commit_message}
- if @build.tags.any?
.block
.title
Tags
- @build.tag_list.each do |tag|
%span.label.label-primary
= tag
by
%a{ href: user_path(@build.user) }
= image_tag avatar_icon(@build.user, 24), class: "avatar s24"
%strong= @build.user.to_reference
- page_title "#{@build.name} (##{@build.id})", "Builds"
- trace_with_state = @build.trace_with_state
- header_title project_title(@project, "Builds", project_builds_path(@project))
.build-page
.row-content-block.top-block
Build ##{@build.id} for commit
%strong.monospace= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline)
from
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
- merge_request = @build.merge_request
- if merge_request
via
= link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
= render "header"
#up-build-trace
- builds = @build.pipeline.builds.latest.to_a
- if builds.size > 1
%ul.nav-links.no-top.no-bottom
......@@ -33,18 +25,6 @@
&middot;
%i.fa.fa-warning
This build was retried.
.row-content-block.middle-block
.build-head
.clearfix
= ci_status_with_icon(@build.status)
- if @build.duration
%span
%i.fa.fa-time
#{duration_in_words(@build.finished_at, @build.started_at)}
.pull-right
#{time_ago_with_tooltip(@build.finished_at) if @build.finished_at}
- if @build.stuck?
- unless @build.any_runners_online?
.bs-callout.bs-callout-warning
......@@ -64,158 +44,27 @@
= link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
Runners page
.row.prepend-top-default
.col-md-9
.clearfix
.prepend-top-default
- if @build.active?
.autoscroll-container
%button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
.clearfix
#js-build-scroll.scroll-controls
= link_to '#up-build-trace', class: 'btn' do
= link_to '#build-trace', class: 'btn' do
%i.fa.fa-angle-up
= link_to '#down-build-trace', class: 'btn' do
%i.fa.fa-angle-down
- if @build.erased?
.erased.alert.alert-warning
- erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
- else
%pre.trace#build-trace
%code.bash
= preserve do
= raw trace_with_state[:html]
- if @build.active?
%i{:class => "fa fa-refresh fa-spin"}
%div#down-build-trace
.col-md-3
- if @build.coverage
.build-widget
%h4.title
Test coverage
%h1 #{@build.coverage}%
- if can?(current_user, :read_build, @project) && @build.artifacts?
.build-widget.artifacts
%h4.title Build artifacts
.center
.btn-group{ role: :group }
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
= icon('download')
Download
- if @build.artifacts_metadata?
= link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
= icon('folder-open')
Browse
.build-widget.build-controls
%h4.title
Build ##{@build.id}
- if can?(current_user, :update_build, @project)
.center
.btn-group{ role: :group }
- if @build.active?
= link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger', method: :post
- elsif @build.retryable?
= link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post
- if @build.erasable?
= link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
class: 'btn btn-sm btn-warning', method: :post,
data: { confirm: 'Are you sure you want to erase this build?' } do
= icon('eraser')
Erase
- if @build.has_trace?
= link_to 'Raw', raw_namespace_project_build_path(@project.namespace, @project, @build),
class: 'btn btn-sm btn-success', target: '_blank'
.clearfix
- if @build.duration
%p
%span.attr-name Duration:
#{duration_in_words(@build.finished_at, @build.started_at)}
%p
%span.attr-name Created:
#{time_ago_with_tooltip(@build.created_at)}
- if @build.finished_at
%p
%span.attr-name Finished:
#{time_ago_with_tooltip(@build.finished_at)}
- if @build.erased_at
%p
%span.attr-name Erased:
#{time_ago_with_tooltip(@build.erased_at)}
%p
%span.attr-name Runner:
- if @build.runner && current_user && current_user.admin
= link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
- elsif @build.runner
\##{@build.runner.id}
- if @build.trigger_request
.build-widget
%h4.title
Trigger
%p
%span.attr-name Token:
#{@build.trigger_request.trigger.short_token}
- if @build.trigger_request.variables
%p
%span.attr-name Variables:
%code
- @build.trigger_request.variables.each do |key, value|
#{key}=#{value}
.build-widget
%h4.title
Commit
.pull-right
%small
= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
%p
%span.attr-name Branch:
= link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
%p
%span.attr-name Author:
#{@build.pipeline.git_author_name}
%p
%span.attr-name Message:
#{@build.pipeline.git_commit_message}
- if @build.tags.any?
.build-widget
%h4.title
Tags
- @build.tag_list.each do |tag|
%span.label.label-primary
= tag
- if @builds.present?
.build-widget
%h4.title #{pluralize(@builds.count(:id), "other build")} for
= succeed ":" do
= link_to @build.pipeline.short_sha, ci_status_path(@build.pipeline), class: "monospace"
%table.table.builds
- @builds.each_with_index do |build, i|
%tr.build
%td
= ci_icon_for_status(build.status)
%td
= link_to namespace_project_build_path(@project.namespace, @project, build) do
- if build.name
= build.name
- else
%span ##{build.id}
%pre.build-trace#build-trace
%code.bash.js-build-output
= icon("refresh spin", class: "js-build-refresh")
%td.status= build.status
#down-build-trace
= render "sidebar"
:javascript
:javascript
new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}", "#{trace_with_state[:state]}")
......@@ -84,6 +84,8 @@
%br
%span.descr Enable Container Registry for this repository
%hr
= render 'merge_request_settings', f: f
%hr
= render 'builds_settings', f: f
%hr
%fieldset.features.append-bottom-default
......
%ul.nav-links.sub-nav
%div{ class: (container_class) }
- if project_nav_tab?(:issues) && !current_controller?(:merge_requests)
= nav_link(controller: :issues) do
= link_to url_for_project_issues(@project, only_path: true), title: 'Issues' do
%span
Issues
- if project_nav_tab?(:merge_requests) && current_controller?(:merge_requests)
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do
%span
Merge Requests
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
%span
Labels
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
%span
Milestones
- @no_container = true
- page_title "Issues"
= render "projects/issues/head"
= content_for :meta_tags do
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
.top-area
%div{ class: (container_class) }
.top-area
= render 'shared/issuable/nav', type: :issues
.nav-controls
- if current_user
......@@ -17,7 +20,7 @@
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
New Issue
= render 'shared/issuable/filter', type: :issues
= render 'shared/issuable/filter', type: :issues
.issues-holder
.issues-holder
= render "issues"
- @no_container = true
- page_title "Labels"
- hide_class = ''
= render "projects/issues/head"
.top-area
%div{ class: (container_class) }
.top-area
.nav-text
Labels can be applied to issues and merge requests.
.nav-controls
......@@ -9,7 +12,7 @@
= link_to new_namespace_project_label_path(@project.namespace, @project), class: "btn btn-new" do
New label
.labels
.labels
- if can?(current_user, :admin_label, @project)
-# Only show it in the first page
- hide = @project.labels.empty? || (params[:page].present? && params[:page] != '1')
......
.top-tabs
= link_to namespace_project_merge_requests_path(@project.namespace, @project), class: "tab #{'active' if current_page?(namespace_project_merge_requests_path(@project.namespace, @project)) }" do
%span
Merge Requests
- @no_container = true
- page_title "Merge Requests"
= render "projects/issues/head"
= render 'projects/last_push'
.top-area
%div{ class: (container_class) }
.top-area
= render 'shared/issuable/nav', type: :merge_requests
.nav-controls
= render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project)
......@@ -12,7 +14,7 @@
= link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do
New Merge Request
= render 'shared/issuable/filter', type: :merge_requests
= render 'shared/issuable/filter', type: :merge_requests
.merge-requests-holder
.merge-requests-holder
= render 'merge_requests'
......@@ -17,6 +17,8 @@
= render 'projects/merge_requests/widget/open/merge_when_build_succeeds'
- elsif !@merge_request.can_be_merged_by?(current_user)
= render 'projects/merge_requests/widget/open/not_allowed'
- elsif !@merge_request.mergeable_ci_state? && @pipeline && @pipeline.failed?
= render 'projects/merge_requests/widget/open/build_failed'
- elsif @merge_request.can_be_merged?
= render 'projects/merge_requests/widget/open/accept'
......
......@@ -10,6 +10,7 @@
%span.btn-group
= button_tag class: "btn btn-create js-merge-button merge_when_build_succeeds" do
Merge When Build Succeeds
- unless @project.only_allow_merge_if_build_succeeds?
= button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown' do
%span.caret
%span.sr-only
......
%h4
= icon('exclamation-triangle')
The build for this merge request failed
%p
Please retry the build or push a new commit to fix the failure.
- @no_container = true
- page_title "Milestones"
= render "projects/issues/head"
.top-area
%div{ class: (container_class) }
.top-area
= render 'shared/milestones_filter'
.nav-controls
......@@ -8,7 +11,7 @@
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "btn btn-new", title: "New Milestone" do
New Milestone
.milestones
.milestones
%ul.content-list
= render @milestones
......
......@@ -3,13 +3,14 @@
<!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
<title>path-1</title>
<desc>Created with Sketch.</desc>
<defs>
<path d="M5,0 C4.448,0 4,0.448 4,1 L4,3 L1,3 C0.448,3 0,3.448 0,4 L0,9 C0,9.552 0.448,10 1,10 L5,10 L5,8 L11,8 L11,10 L15,10 C15.552,10 16,9.552 16,9 L16,4 C16,3.448 15.552,3 15,3 L12,3 L12,1 C12,0.448 11.552,0 11,0 L5,0 L5,0 L5,0 Z M6,2.5 C6,2.224 6.224,2 6.5,2 L9.5,2 C9.776,2 10,2.224 10,2.5 C10,2.776 9.776,3 9.5,3 L6.5,3 C6.224,3 6,2.776 6,2.5 L6,2.5 L6,2.5 Z M6,11 L10.001,11 L10.001,9 L6,9 L6,11 L6,11 L6,11 Z M11,11 L11,12 L5,12 L5,11 L1,11 C0.448,11 0,11.448 0,12 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,12 C16,11.448 15.552,11 15,11 L11,11 L11,11 L11,11 Z" id="path-1"></path>
</defs>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="path-1" fill="#D8D8D8" xlink:href="#path-1"></use>
<g id="_activity" fill="#7E7D7D">
<g id="Page-1">
<g id="path-1">
<path d="M5,0 C4.448,0 4,0.448 4,1 L4,3 L1,3 C0.448,3 0,3.448 0,4 L0,9 C0,9.552 0.448,10 1,10 L5,10 L5,8 L11,8 L11,10 L15,10 C15.552,10 16,9.552 16,9 L16,4 C16,3.448 15.552,3 15,3 L12,3 L12,1 C12,0.448 11.552,0 11,0 L5,0 L5,0 L5,0 L5,0 Z M6,2.5 C6,2.224 6.224,2 6.5,2 L9.5,2 C9.776,2 10,2.224 10,2.5 C10,2.776 9.776,3 9.5,3 L6.5,3 C6.224,3 6,2.776 6,2.5 L6,2.5 L6,2.5 L6,2.5 Z M6,11 L10.001,11 L10.001,9 L6,9 L6,11 L6,11 L6,11 L6,11 Z M11,11 L11,12 L5,12 L5,11 L1,11 C0.448,11 0,11.448 0,12 L0,15 C0,15.552 0.448,16 1,16 L15,16 C15.552,16 16,15.552 16,15 L16,12 C16,11.448 15.552,11 15,11 L11,11 L11,11 L11,11 L11,11 Z"></path>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
......@@ -4,7 +4,7 @@
- filter_placeholder = local_assigns.fetch(:filter_placeholder, 'Search labels')
.dropdown-page-one
= dropdown_title(title)
= dropdown_filter(filter_placeholder)
= dropdown_filter(filter_placeholder, search_id: "label-name")
= dropdown_content
- if @project && show_footer
= dropdown_footer do
......
......@@ -181,7 +181,7 @@ production: &base
# host: registry.example.com
# port: 5005
# api_url: http://localhost:5000/ # internal address to the registry, will be used by GitLab to directly communicate with API
# key_path: config/registry.key
# key: config/registry.key
# path: shared/registry
# issuer: gitlab-issuer
......
......@@ -2,7 +2,7 @@
<%
require "yaml"
require "json"
require_relative "lib/gitlab/redis"
require_relative "lib/gitlab/redis" unless defined?(Gitlab::Redis)
rails_env = ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
......
# rubocop:disable all
class InitSchema < ActiveRecord::Migration
def up
......
# rubocop:disable all
class RenameOwnerToCreatorForProject < ActiveRecord::Migration
def change
rename_column :projects, :owner_id, :creator_id
......
# rubocop:disable all
class AddPublicToProject < ActiveRecord::Migration
def change
add_column :projects, :public, :boolean, default: false, null: false
......
# rubocop:disable all
class AddIssuesTrackerToProject < ActiveRecord::Migration
def change
add_column :projects, :issues_tracker, :string, default: :gitlab, null: false
......
# rubocop:disable all
class AddUserPermissions < ActiveRecord::Migration
def up
add_column :users, :can_create_group, :boolean, default: true, null: false
......
# rubocop:disable all
class RemovePrivateFlagFromProject < ActiveRecord::Migration
def up
remove_column :projects, :private_flag
......
# rubocop:disable all
class AddDescriptionToNamsespace < ActiveRecord::Migration
def change
add_column :namespaces, :description, :string, default: '', null: false
......
# rubocop:disable all
class AddDescriptionToTeams < ActiveRecord::Migration
def change
add_column :user_teams, :description, :string, default: '', null: false
......
# rubocop:disable all
class AddIssuesTrackerIdToProject < ActiveRecord::Migration
def change
add_column :projects, :issues_tracker_id, :string
......
# rubocop:disable all
class RenameStateToMergeStatusInMilestone < ActiveRecord::Migration
def change
rename_column :merge_requests, :state, :merge_status
......
# rubocop:disable all
class AddStateToIssue < ActiveRecord::Migration
def change
add_column :issues, :state, :string
......
# rubocop:disable all
class AddStateToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :state, :string
......
# rubocop:disable all
class AddStateToMilestone < ActiveRecord::Migration
def change
add_column :milestones, :state, :string
......
# rubocop:disable all
class ConvertClosedToStateInIssue < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class ConvertClosedToStateInMilestone < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class RemoveMergedFromMergeRequest < ActiveRecord::Migration
def up
remove_column :merge_requests, :merged
......
# rubocop:disable all
class RemoveClosedFromIssue < ActiveRecord::Migration
def up
remove_column :issues, :closed
......
# rubocop:disable all
class RemoveClosedFromMergeRequest < ActiveRecord::Migration
def up
remove_column :merge_requests, :closed
......
# rubocop:disable all
class RemoveClosedFromMilestone < ActiveRecord::Migration
def up
remove_column :milestones, :closed
......
# rubocop:disable all
class AddNewMergeStatusToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :new_merge_status, :string
......
# rubocop:disable all
class ConvertMergeStatusInMergeRequest < ActiveRecord::Migration
def up
execute "UPDATE #{table_name} SET new_merge_status = 'unchecked' WHERE merge_status = 1"
......
# rubocop:disable all
class RemoveMergeStatusFromMergeRequest < ActiveRecord::Migration
def up
remove_column :merge_requests, :merge_status
......
# rubocop:disable all
class RenameNewMergeStatusToMergeStatusInMilestone < ActiveRecord::Migration
def change
rename_column :merge_requests, :new_merge_status, :merge_status
......
# rubocop:disable all
class AddStateToUser < ActiveRecord::Migration
def change
add_column :users, :state, :string
......
# rubocop:disable all
class ConvertBlockedToState < ActiveRecord::Migration
def up
User.transaction do
......
# rubocop:disable all
class RemoveBlockedFromUser < ActiveRecord::Migration
def up
remove_column :users, :blocked
......
# rubocop:disable all
class UserColorScheme < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class AddSnippetsToFeatures < ActiveRecord::Migration
def change
add_column :projects, :snippets_enabled, :boolean, null: false, default: true
......
# rubocop:disable all
class CreateForkedProjectLinks < ActiveRecord::Migration
def change
create_table :forked_project_links do |t|
......
# rubocop:disable all
class AddPrivateToSnippets < ActiveRecord::Migration
def change
add_column :snippets, :private, :boolean, null: false, default: true
......
# rubocop:disable all
class AddTypeToSnippets < ActiveRecord::Migration
def change
add_column :snippets, :type, :string
......
# rubocop:disable all
class ChangeProjectIdToNullInSnipepts < ActiveRecord::Migration
def up
change_column :snippets, :project_id, :integer, :null => true
......
# rubocop:disable all
class AddTypeValueForSnippets < ActiveRecord::Migration
def up
Snippet.where("project_id IS NOT NULL").update_all(type: 'ProjectSnippet')
......
# rubocop:disable all
class AddNotificationLevelToUser < ActiveRecord::Migration
def change
add_column :users, :notification_level, :integer, null: false, default: 1
......
# rubocop:disable all
class AddIndexToUsersAuthenticationToken < ActiveRecord::Migration
def change
add_index :users, :authentication_token, unique: true
......
# rubocop:disable all
class AddLastActivityColumnIntoProject < ActiveRecord::Migration
def up
add_column :projects, :last_activity_at, :datetime
......
# rubocop:disable all
class AddNotificationLevelToUserProject < ActiveRecord::Migration
def change
add_column :users_projects, :notification_level, :integer, null: false, default: 3
......
# rubocop:disable all
class RemoveWikiTable < ActiveRecord::Migration
def up
drop_table :wikis
......
# rubocop:disable all
class AllowMergesForForks < ActiveRecord::Migration
def self.up
add_column :merge_requests, :target_project_id, :integer, :null => true
......
# rubocop:disable all
class AddTypeToKey < ActiveRecord::Migration
def change
add_column :keys, :type, :string
......
# rubocop:disable all
class CreateDeployKeysProjects < ActiveRecord::Migration
def change
create_table :deploy_keys_projects do |t|
......
# rubocop:disable all
class RemoveProjectIdFromKey < ActiveRecord::Migration
def up
puts 'Migrate deploy keys: '
......
# rubocop:disable all
class AddMoreFieldsToService < ActiveRecord::Migration
def change
add_column :services, :subdomain, :string
......
# rubocop:disable all
class AddSystemToNotes < ActiveRecord::Migration
class Note < ActiveRecord::Base
end
......
# rubocop:disable all
class IncreaseSnippetTextColumnSize < ActiveRecord::Migration
def up
# MYSQL LARGETEXT for snippet
......
# rubocop:disable all
class AddPasswordExpiresAtToUsers < ActiveRecord::Migration
def change
add_column :users, :password_expires_at, :datetime
......
# rubocop:disable all
class AddCreatedByIdToUser < ActiveRecord::Migration
def change
add_column :users, :created_by_id, :integer
......
# rubocop:disable all
class AddImprotedToProject < ActiveRecord::Migration
def change
add_column :projects, :imported, :boolean, default: false, null: false
......
# rubocop:disable all
class CreateUsersGroups < ActiveRecord::Migration
def change
create_table :users_groups do |t|
......
# rubocop:disable all
class AddNotificationLevelToUserGroup < ActiveRecord::Migration
def change
add_column :users_groups, :notification_level, :integer, null: false, default: 3
......
# rubocop:disable all
class AddMoreDbIndex < ActiveRecord::Migration
def change
add_index :deploy_keys_projects, :project_id
......
# rubocop:disable all
class AddFingerprintToKey < ActiveRecord::Migration
def change
add_column :keys, :fingerprint, :string
......
# rubocop:disable all
class CreateProjectGroupLinks < ActiveRecord::Migration
def change
create_table :project_group_links do |t|
......
# rubocop:disable all
class AddStDiffToNote < ActiveRecord::Migration
def change
add_column :notes, :st_diff, :text, :null => true
......
# rubocop:disable all
class AddPermissionCheckToUser < ActiveRecord::Migration
def change
add_column :users, :last_credential_check_at, :datetime
......
# rubocop:disable all
class AddImportUrlToProject < ActiveRecord::Migration
def change
add_column :projects, :import_url, :string
......
# rubocop:disable all
class AddInternalIdsToIssuesAndMr < ActiveRecord::Migration
def change
add_column :issues, :iid, :integer
......
# rubocop:disable all
class AddAccessToProjectGroupLink < ActiveRecord::Migration
def change
add_column :project_group_links, :group_access, :integer, null: false, default: ProjectGroupLink.default_access
......
# rubocop:disable all
class RemoveDeprecatedTables < ActiveRecord::Migration
def up
drop_table :user_teams
......
# rubocop:disable all
class AddInternalIdsToMilestones < ActiveRecord::Migration
def change
add_column :milestones, :iid, :integer
......
# rubocop:disable all
class AddDescriptionToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :description, :text, null: true
......
# rubocop:disable all
class ChangeOwnerIdForGroup < ActiveRecord::Migration
def up
change_column :namespaces, :owner_id, :integer, null: true
......
# rubocop:disable all
class AddAvatarToUsers < ActiveRecord::Migration
def change
add_column :users, :avatar, :string
......
# rubocop:disable all
class AddConfirmableToUsers < ActiveRecord::Migration
def self.up
add_column :users, :confirmation_token, :string
......
# rubocop:disable all
class RemoveDefaultBranch < ActiveRecord::Migration
def up
remove_column :projects, :default_branch
......
# rubocop:disable all
class CreateBroadcastMessages < ActiveRecord::Migration
def change
create_table :broadcast_messages do |t|
......
# rubocop:disable all
class AddVisibilityLevelToProjects < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class AddArchivedToProjects < ActiveRecord::Migration
def change
add_column :projects, :archived, :boolean, default: false, null: false
......
# rubocop:disable all
class AddColorAndFontToBroadcastMessages < ActiveRecord::Migration
def change
add_column :broadcast_messages, :color, :string
......
# rubocop:disable all
class AddEventFieldsForWebHook < ActiveRecord::Migration
def change
add_column :web_hooks, :push_events, :boolean, default: true, null: false
......
# rubocop:disable all
class AddHideNoSshKeyToUsers < ActiveRecord::Migration
def change
add_column :users, :hide_no_ssh_key, :boolean, :default => false
......
# rubocop:disable all
class AddRecipientsToService < ActiveRecord::Migration
def change
add_column :services, :recipients, :text
......
# rubocop:disable all
class AddWebsiteUrlToUsers < ActiveRecord::Migration
def change
add_column :users, :website_url, :string, {:null => false, :default => ''}
......
# rubocop:disable all
class CreateMergeRequestDiffs < ActiveRecord::Migration
def up
create_table :merge_request_diffs do |t|
......
# rubocop:disable all
class MigrateMrDiffs < ActiveRecord::Migration
def self.up
execute "INSERT INTO merge_request_diffs ( merge_request_id, st_commits, st_diffs ) SELECT id, st_commits, st_diffs FROM merge_requests"
......
# rubocop:disable all
class RemoveMRdiffFields < ActiveRecord::Migration
def up
remove_column :merge_requests, :st_commits
......
# rubocop:disable all
class AddAvatarToProjects < ActiveRecord::Migration
def change
add_column :projects, :avatar, :string
......
# rubocop:disable all
class AddGroupAvatars < ActiveRecord::Migration
def change
add_column :namespaces, :avatar, :string
......
# rubocop:disable all
class CreateEmails < ActiveRecord::Migration
def change
create_table :emails do |t|
......
# rubocop:disable all
class AddApiKeyToServices < ActiveRecord::Migration
def change
add_column :services, :api_key, :string
......
# rubocop:disable all
class AddIndexMergeRequestDiffsOnMergeRequestId < ActiveRecord::Migration
def change
add_index :merge_request_diffs, :merge_request_id, unique: true
......
# rubocop:disable all
class AddTagPushHooksToProjectHook < ActiveRecord::Migration
def change
add_column :web_hooks, :tag_push_events, :boolean, default: false
......
# rubocop:disable all
class AddImportStatusToProject < ActiveRecord::Migration
def change
add_column :projects, :import_status, :string
......
# rubocop:disable all
class MigrateAlreadyImportedProjects < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class FixNamespaces < ActiveRecord::Migration
def up
Namespace.where('name <> path and type is null').each do |namespace|
......
# rubocop:disable all
class ChangeStateToAllowEmptyMergeRequestDiffs < ActiveRecord::Migration
def up
change_column :merge_request_diffs, :state, :string, null: true,
......
# rubocop:disable all
require_relative 'limits_to_mysql'
# rubocop:disable all
class AddIndexOnIid < ActiveRecord::Migration
def change
RemoveDuplicateIid.clean(Issue)
......
# rubocop:disable all
class IndexOnCurrentSignInAt < ActiveRecord::Migration
def change
add_index :users, :current_sign_in_at
......
# rubocop:disable all
class AddNotesIndexUpdatedAt < ActiveRecord::Migration
def change
add_index :notes, :updated_at
......
# rubocop:disable all
class AddRepoSizeToDb < ActiveRecord::Migration
def change
add_column :projects, :repository_size, :float, default: 0
......
# rubocop:disable all
class MigrateRepoSize < ActiveRecord::Migration
def up
project_data = execute('SELECT projects.id, namespaces.path AS namespace_path, projects.path AS project_path FROM projects LEFT JOIN namespaces ON projects.namespace_id = namespaces.id')
......
# rubocop:disable all
class AddPositionToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :position, :integer, default: 0
......
# rubocop:disable all
class CreateUsersStarProjects < ActiveRecord::Migration
def change
create_table :users_star_projects do |t|
......
# rubocop:disable all
class CreateLabels < ActiveRecord::Migration
def change
create_table :labels do |t|
......
# rubocop:disable all
class CreateLabelLinks < ActiveRecord::Migration
def change
create_table :label_links do |t|
......
# rubocop:disable all
class MigrateProjectTags < ActiveRecord::Migration
def up
ActsAsTaggableOn::Tagging.where(taggable_type: 'Project', context: 'labels').update_all(context: 'tags')
......
# rubocop:disable all
class MigrateTaggableLabels < ActiveRecord::Migration
def up
taggings = ActsAsTaggableOn::Tagging.where(taggable_type: ['Issue', 'MergeRequest'], context: 'labels')
......
# rubocop:disable all
class AddIndexToLabels < ActiveRecord::Migration
def change
add_index "labels", :project_id
......
# rubocop:disable all
class MigrateToNewShell < ActiveRecord::Migration
def change
return if Rails.env.test?
......
# rubocop:disable all
class SerializeServiceProperties < ActiveRecord::Migration
def change
unless column_exists?(:services, :properties)
......
# rubocop:disable all
class AddMembersTable < ActiveRecord::Migration
def change
create_table :members do |t|
......
# rubocop:disable all
class MigrateToNewMembersModel < ActiveRecord::Migration
def up
execute "INSERT INTO members ( user_id, source_id, source_type, access_level, notification_level, type ) SELECT user_id, group_id, 'Namespace', group_access, notification_level, 'GroupMember' FROM users_groups"
......
# rubocop:disable all
class RemoveOldMemberTables < ActiveRecord::Migration
def up
drop_table :users_groups
......
# rubocop:disable all
class MoveSlackServiceToWebhook < ActiveRecord::Migration
def change
SlackService.all.each do |slack_service|
......
# rubocop:disable all
class AddVisibilityLevelToSnippet < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class AddAuditEvent < ActiveRecord::Migration
def change
create_table :audit_events do |t|
......
# rubocop:disable all
# In 20140914145549_migrate_to_new_members_model.rb we forgot to set the
# created_at and updated_at times for new records in the 'members' table. This
# became a problem after commit c8e78d972a5a628870eefca0f2ccea0199c55bda which
......
# rubocop:disable all
class AddIdentityTable < ActiveRecord::Migration
def up
create_table :identities do |t|
......
# rubocop:disable all
class AddLockedAtToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :locked_at, :datetime
......
# rubocop:disable all
class CreateDoorkeeperTables < ActiveRecord::Migration
def change
create_table :oauth_applications do |t|
......
# rubocop:disable all
class AddOwnerToApplication < ActiveRecord::Migration
def change
add_column :oauth_applications, :owner_id, :integer, null: true
......
# rubocop:disable all
class AddImportDataToProjectTable < ActiveRecord::Migration
def change
add_column :projects, :import_type, :string
......
# rubocop:disable all
class AddDevelopersCanPushToProtectedBranches < ActiveRecord::Migration
def change
add_column :protected_branches, :developers_can_push, :boolean, default: false, null: false
......
# rubocop:disable all
class CreateApplicationSettings < ActiveRecord::Migration
def change
create_table :application_settings do |t|
......
# rubocop:disable all
class AddHomePageUrlForApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :home_page_url, :string
......
# rubocop:disable all
class AddGitlabAccessTokenToUser < ActiveRecord::Migration
def change
add_column :users, :gitlab_access_token, :string
......
# rubocop:disable all
class AddDefaultBranchProtectionSetting < ActiveRecord::Migration
def change
add_column :application_settings, :default_branch_protection, :integer, :default => 2
......
# rubocop:disable all
class AddTimestampsToIdentities < ActiveRecord::Migration
def change
add_timestamps(:identities)
......
# rubocop:disable all
class AddIndexToCreatedAt < ActiveRecord::Migration
def change
add_index "users", [:created_at, :id]
......
# rubocop:disable all
class AddNotificationEmailToUser < ActiveRecord::Migration
def up
add_column :users, :notification_email, :string
......
# rubocop:disable all
class AddMissingIndex < ActiveRecord::Migration
def change
add_index "services", [:created_at, :id]
......
# rubocop:disable all
class AddTemplateToService < ActiveRecord::Migration
def change
add_column :services, :template, :boolean, default: false
......
# rubocop:disable all
class AllowNullInServicesProjectId < ActiveRecord::Migration
def change
change_column :services, :project_id, :integer, null: true
......
# rubocop:disable all
class AddTwitterSharingEnabledToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :twitter_sharing_enabled, :boolean, default: true
......
# rubocop:disable all
class AddHideNoPasswordToUser < ActiveRecord::Migration
def change
add_column :users, :hide_no_password, :boolean, default: false
......
# rubocop:disable all
class AddPasswordAutomaticallySetToUser < ActiveRecord::Migration
def change
add_column :users, :password_automatically_set, :boolean, default: false
......
# rubocop:disable all
class AddBitbucketAccessTokenAndSecretToUser < ActiveRecord::Migration
def change
add_column :users, :bitbucket_access_token, :string
......
# rubocop:disable all
class AddEventsToServices < ActiveRecord::Migration
def change
add_column :services, :push_events, :boolean, :default => true
......
# rubocop:disable all
class SetMissingLastActivityAt < ActiveRecord::Migration
def up
execute "UPDATE projects SET last_activity_at = updated_at WHERE last_activity_at IS NULL"
......
# rubocop:disable all
class AddNoteEventsToServices < ActiveRecord::Migration
def change
add_column :services, :note_events, :boolean, default: true, null: false
......
# rubocop:disable all
class AddRestrictedVisibilityLevelsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :restricted_visibility_levels, :text
......
# rubocop:disable all
class FixNamespaceDuplication < ActiveRecord::Migration
def up
#fixes path duplication
......
# rubocop:disable all
class AddUniqueIndexToNamespace < ActiveRecord::Migration
def change
remove_index :namespaces, column: :name if index_exists?(:namespaces, :name)
......
# rubocop:disable all
class AddVersionCheckToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :version_check_enabled, :boolean, default: true
......
# rubocop:disable all
class CreateSubscriptionsTable < ActiveRecord::Migration
def change
create_table :subscriptions do |t|
......
# rubocop:disable all
class AddLocationToUser < ActiveRecord::Migration
def change
add_column :users, :location, :string
......
# rubocop:disable all
class SetIncorrectAssigneeIdToNull < ActiveRecord::Migration
def up
execute "UPDATE issues SET assignee_id = NULL WHERE assignee_id = -1"
......
# rubocop:disable all
class AddPublicToKey < ActiveRecord::Migration
def change
add_column :keys, :public, :boolean, default: false, null: false
......
# rubocop:disable all
class AddImportDataToProject < ActiveRecord::Migration
def change
add_column :projects, :import_data, :text
......
# rubocop:disable all
class AddDeviseTwoFactorToUsers < ActiveRecord::Migration
def change
add_column :users, :encrypted_otp_secret, :string
......
# rubocop:disable all
class AddMaxAttachmentSizeToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :max_attachment_size, :integer, default: 10, null: false
......
# rubocop:disable all
class AddDeviseTwoFactorBackupableToUsers < ActiveRecord::Migration
def change
add_column :users, :otp_backup_codes, :text
......
# rubocop:disable all
class AddInviteDataToMember < ActiveRecord::Migration
def up
add_column :members, :created_by_id, :integer
......
# rubocop:disable all
class FixIdentities < ActiveRecord::Migration
def up
# Up until now, legacy 'ldap' references in the database were charitably
......
# rubocop:disable all
class RenameBuildboxService < ActiveRecord::Migration
def up
execute "UPDATE services SET type = 'BuildkiteService' WHERE type = 'BuildboxService';"
......
# rubocop:disable all
class AddPublicEmailToUsers < ActiveRecord::Migration
def change
add_column :users, :public_email, :string, default: "", null: false
......
# rubocop:disable all
class CreateProjectImportData < ActiveRecord::Migration
def change
create_table :project_import_data do |t|
......
# rubocop:disable all
class RemoveImportDataFromProject < ActiveRecord::Migration
def up
remove_column :projects, :import_data
......
# rubocop:disable all
class RemovePeriodsAtEndsOfUsernames < ActiveRecord::Migration
include Gitlab::ShellAdapter
......
# rubocop:disable all
class AddDefaultProjectVisibililtyToApplicationSettings < ActiveRecord::Migration
def up
add_column :application_settings, :default_project_visibility, :integer
......
# rubocop:disable all
# This migration is a duplicate of 20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb
# It shold be applied before the index additions to ensure that `name` is case sensitive.
......
# rubocop:disable all
class RemoveDuplicateTags < ActiveRecord::Migration
def up
select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(id) > 1").each do |tag|
......
# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 2)
class AddMissingUniqueIndices < ActiveRecord::Migration
def self.up
......
# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 3)
class AddTaggingsCounterCacheToTags < ActiveRecord::Migration
def self.up
......
# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 4)
class AddMissingTaggableIndex < ActiveRecord::Migration
def self.up
......
# rubocop:disable all
# This migration comes from acts_as_taggable_on_engine (originally 5)
# This migration is added to circumvent issue #623 and have special characters
# work properly
......
# rubocop:disable all
class AddDefaultSnippetVisibilityToAppSettings < ActiveRecord::Migration
def up
add_column :application_settings, :default_snippet_visibility, :integer
......
# rubocop:disable all
class RemoveAbandonedGroupMembersRecords < ActiveRecord::Migration
def up
execute("DELETE FROM members WHERE type = 'GroupMember' AND source_id NOT IN(\
......
# rubocop:disable all
class AddRestrictedSignupDomainsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :restricted_signup_domains, :text
......
# rubocop:disable all
# Convert legacy Markdown-emphasized notes to the current, non-emphasized format
#
# _mentioned in 54f7727c850972f0401c1312a7c4a6a380de5666_
......
# rubocop:disable all
class AddNoteEventsToWebHooks < ActiveRecord::Migration
def up
add_column :web_hooks, :note_events, :boolean, default: false, null: false
......
# rubocop:disable all
class AddUserOauthApplicationsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :user_oauth_applications, :bool, default: true
......
# rubocop:disable all
class AddAfterSignOutPathForApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :after_sign_out_path, :string
......
# rubocop:disable all
class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
def change
unless column_exists?(:application_settings, :session_expire_delay)
......
# rubocop:disable all
class AddDashboardToUsers < ActiveRecord::Migration
def up
add_column :users, :dashboard, :integer, default: 0
......
# rubocop:disable all
class AddDefaultOtpRequiredForLoginValue < ActiveRecord::Migration
def up
execute %q{UPDATE users SET otp_required_for_login = FALSE WHERE otp_required_for_login IS NULL}
......
# rubocop:disable all
class AddProjectViewToUsers < ActiveRecord::Migration
def change
add_column :users, :project_view, :integer, default: 0
......
# rubocop:disable all
class AddCommitsCountToProject < ActiveRecord::Migration
def change
add_column :projects, :commit_count, :integer, default: 0
......
# rubocop:disable all
class AddUpdatedByToIssuablesAndNotes < ActiveRecord::Migration
def change
add_column :notes, :updated_by_id, :integer
......
# rubocop:disable all
class CreateAbuseReports < ActiveRecord::Migration
def change
create_table :abuse_reports do |t|
......
# rubocop:disable all
require 'yaml'
class AddSettingsImportSources < ActiveRecord::Migration
......
# rubocop:disable all
class RemoveOauthTokensFromUsers < ActiveRecord::Migration
def change
remove_column :users, :github_access_token, :string
......
# rubocop:disable all
class DeduplicateUserIdentities < ActiveRecord::Migration
def change
execute 'DROP TABLE IF EXISTS tt_migration_DeduplicateUserIdentities;'
......
# rubocop:disable all
class AddSentNotifications < ActiveRecord::Migration
def change
create_table :sent_notifications do |t|
......
# rubocop:disable all
class AddEnableSslVerification < ActiveRecord::Migration
def change
add_column :web_hooks, :enable_ssl_verification, :boolean, default: false
......
# rubocop:disable all
class AddCiTables < ActiveRecord::Migration
def change
create_table "ci_application_settings", force: true do |t|
......
# rubocop:disable all
class AddTemplateToLabel < ActiveRecord::Migration
def change
add_column :labels, :template, :boolean, default: false
......
# rubocop:disable all
class AddCiTags < ActiveRecord::Migration
def change
create_table "ci_taggings", force: true do |t|
......
# rubocop:disable all
class EnableSslVerificationByDefault < ActiveRecord::Migration
def change
change_column :web_hooks, :enable_ssl_verification, :boolean, default: true
......
# rubocop:disable all
class EnableSslVerificationForWebHooks < ActiveRecord::Migration
def up
execute("UPDATE web_hooks SET enable_ssl_verification = true")
......
# rubocop:disable all
class AddHelpPageTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :help_page_text, :text
......
# rubocop:disable all
class AddIndexForCommittedAtAndId < ActiveRecord::Migration
def change
add_index :ci_commits, [:project_id, :committed_at, :id]
......
# rubocop:disable all
class AddCiEnabledToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :ci_enabled, :boolean, null: false, default: true
......
# rubocop:disable all
class RemoveInvalidMilestonesFromMergeRequests < ActiveRecord::Migration
def up
execute("UPDATE merge_requests SET milestone_id = NULL where milestone_id NOT IN (SELECT id FROM milestones)")
......
# rubocop:disable all
class AddConsumedTimestepToUsers < ActiveRecord::Migration
def change
add_column :users, :consumed_timestep, :integer
......
# rubocop:disable all
class AddLineCodeToSentNotification < ActiveRecord::Migration
def change
add_column :sent_notifications, :line_code, :string
......
# rubocop:disable all
class AddProjectIdToCiCommit < ActiveRecord::Migration
def up
add_column :ci_commits, :gl_project_id, :integer
......
# rubocop:disable all
class MigrateProjectIdForCiCommits < ActiveRecord::Migration
def up
subquery = 'SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = ci_commits.project_id'
......
# rubocop:disable all
class MergeRequestErrorField < ActiveRecord::Migration
def up
add_column :merge_requests, :merge_error, :string
......
# rubocop:disable all
class AddNullToNameForCiProjects < ActiveRecord::Migration
def up
change_column_null :ci_projects, :name, true
......
# rubocop:disable all
class AddGroupShareLock < ActiveRecord::Migration
def change
add_column :namespaces, :share_with_group_lock, :boolean, default: false
......
# rubocop:disable all
class AddStageIdxToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :stage_idx, :integer
......
# rubocop:disable all
class AddIndexForBuilds < ActiveRecord::Migration
def up
add_index :ci_builds, [:commit_id, :stage_idx, :created_at]
......
# rubocop:disable all
class AddRefAndTagToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :tag, :boolean
......
# rubocop:disable all
class MigrateRefAndTagToBuild < ActiveRecord::Migration
def change
execute('UPDATE ci_builds SET ref=(SELECT ref FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE ref IS NULL')
......
# rubocop:disable all
class AddUserIdToBuild < ActiveRecord::Migration
def change
add_column :ci_builds, :user_id, :integer
......
# rubocop:disable all
class AddLayoutOptionForUsers < ActiveRecord::Migration
def change
add_column :users, :layout, :integer, default: 0
......
# rubocop:disable all
class RemoveCiEnabledFromApplicationSettings < ActiveRecord::Migration
def change
remove_column :application_settings, :ci_enabled, :boolean, null: false, default: true
......
# rubocop:disable all
class NamespacesProjectsPathLowerIndexes < ActiveRecord::Migration
disable_ddl_transaction!
......
# rubocop:disable all
class AddUsersLowerUsernameEmailIndexes < ActiveRecord::Migration
disable_ddl_transaction!
......
# rubocop:disable all
class AddTypeAndDescriptionToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :type, :string
......
# rubocop:disable all
class MigrateNameToDescriptionForBuilds < ActiveRecord::Migration
def change
execute("UPDATE ci_builds SET type='Ci::Build' WHERE type IS NULL")
......
# rubocop:disable all
class AddAdminNotificationEmailSetting < ActiveRecord::Migration
def change
add_column :application_settings, :admin_notification_email, :string
......
# rubocop:disable all
class SetJiraServiceApiUrl < ActiveRecord::Migration
# This migration can be performed online without errors, but some Jira API calls may be missed
# when doing so because api_url is not yet available.
......
# rubocop:disable all
class AddArtifactsFileToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :artifacts_file, :text
......
# rubocop:disable all
class AddCiProjectsGlProjectIdIndex < ActiveRecord::Migration
def change
add_index :ci_commits, :gl_project_id
......
# rubocop:disable all
class AddCiBuildsAndProjectsIndexes < ActiveRecord::Migration
def change
add_index :ci_projects, :gitlab_id
......
# rubocop:disable all
class AddNotesLineCodeIndex < ActiveRecord::Migration
def change
add_index :notes, :line_code
......
# rubocop:disable all
class FixBuildTags < ActiveRecord::Migration
def up
execute("UPDATE taggings SET taggable_type='CommitStatus' WHERE taggable_type='Ci::Build'")
......
# rubocop:disable all
class FailBuildWithoutNames < ActiveRecord::Migration
def up
execute("UPDATE ci_builds SET status='failed' WHERE name IS NULL AND status='pending'")
......
# rubocop:disable all
class AddServicesTemplateIndex < ActiveRecord::Migration
def change
add_index :services, :template
......
# rubocop:disable all
class CiLimitsToMysql < ActiveRecord::Migration
def change
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
......
# rubocop:disable all
class AddCiBuildsIndexForStatus < ActiveRecord::Migration
def change
add_index :ci_builds, [:commit_id, :status, :type]
......
# rubocop:disable all
class FailBuildWithEmptyName < ActiveRecord::Migration
def up
execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'")
......
# rubocop:disable all
require 'fileutils'
class RemoveSatellites < ActiveRecord::Migration
......
# rubocop:disable all
class AddProjectPathIndex < ActiveRecord::Migration
def up
add_index :projects, :path
......
# rubocop:disable all
class AddMergeWhenBuildSucceedsToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :merge_params, :text
......
# rubocop:disable all
class AddPublicToGroup < ActiveRecord::Migration
def change
add_column :namespaces, :public, :boolean, default: false
......
# rubocop:disable all
class AddSharedRunnersSetting < ActiveRecord::Migration
def up
add_column :application_settings, :shared_runners_enabled, :boolean, default: true, null: false
......
# rubocop:disable all
class CreateLfsObjects < ActiveRecord::Migration
def change
create_table :lfs_objects do |t|
......
# rubocop:disable all
class CreateLfsObjectsProjects < ActiveRecord::Migration
def change
create_table :lfs_objects_projects do |t|
......
# rubocop:disable all
class AddFileToLfsObjects < ActiveRecord::Migration
def change
add_column :lfs_objects, :file, :string
......
# rubocop:disable all
class CreateReleases < ActiveRecord::Migration
def change
create_table :releases do |t|
......
# rubocop:disable all
class AddIsAwardToNotes < ActiveRecord::Migration
def change
add_column :notes, :is_award, :boolean, default: false, null: false
......
# rubocop:disable all
class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false
......
# rubocop:disable all
class AddIssuesStateIndex < ActiveRecord::Migration
def change
add_index :issues, :state
......
# rubocop:disable all
class AddProjectsVisibilityLevelIndex < ActiveRecord::Migration
def change
add_index :projects, :visibility_level
......
# rubocop:disable all
class AddImportErrorToProject < ActiveRecord::Migration
def change
add_column :projects, :import_error, :text
......
# rubocop:disable all
class AddIndexForLfsOidAndSize < ActiveRecord::Migration
def change
add_index :lfs_objects, :oid
......
# rubocop:disable all
class AddUniqueForLfsOidIndex < ActiveRecord::Migration
def change
remove_index :lfs_objects, :oid
......
# rubocop:disable all
class AddProjectsPublicIndex < ActiveRecord::Migration
def change
add_index :namespaces, :public
......
# rubocop:disable all
class RaiseHookUrlLimit < ActiveRecord::Migration
def change
change_column :web_hooks, :url, :string, limit: 2000
......
# rubocop:disable all
class AddHideProjectLimitToUsers < ActiveRecord::Migration
def change
add_column :users, :hide_project_limit, :boolean, default: false
......
# rubocop:disable all
class AddBuildEventsToServices < ActiveRecord::Migration
def change
add_column :services, :build_events, :boolean, default: false, null: false
......
# rubocop:disable all
class MigrateCiWebHooks < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class MigrateCiEmails < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class AddUnlockTokenToUser < ActiveRecord::Migration
def change
add_column :users, :unlock_token, :string
......
# rubocop:disable all
class AddRunnersRegistrationTokenToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :runners_registration_token, :string
......
# rubocop:disable all
class MigrateCiSlackService < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class MigrateCiHipChatService < ActiveRecord::Migration
include Gitlab::Database
......
# rubocop:disable all
class AddCiToProject < ActiveRecord::Migration
def change
add_column :projects, :ci_id, :integer
......
# rubocop:disable all
class AddProjectIdToCi < ActiveRecord::Migration
def change
add_column :ci_builds, :gl_project_id, :integer
......
# rubocop:disable all
class MigrateCiToProject < ActiveRecord::Migration
def up
migrate_project_id_for_table('ci_runner_projects')
......
# rubocop:disable all
class AddIndexToCiTables < ActiveRecord::Migration
def change
add_index :ci_builds, :gl_project_id
......
# rubocop:disable all
class DropNullForCiTables < ActiveRecord::Migration
def change
remove_index :ci_variables, :project_id
......
# rubocop:disable all
class AddTfaToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
......
# rubocop:disable all
class AddTfaAdditionalFields < ActiveRecord::Migration
def change
change_table :users do |t|
......
# rubocop:disable all
# Migration type: online without errors (works on previous version and new one)
class RenameEmojis < ActiveRecord::Migration
def up
......
# rubocop:disable all
# Migration type: online
class RemovePublicFromNamespace < ActiveRecord::Migration
def change
......
# rubocop:disable all
class InfluxdbSettings < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_enabled, :boolean, default: false
......
# rubocop:disable all
class AddRecaptchaToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
......
# rubocop:disable all
class InfluxdbUdpPortSetting < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_port, :integer, default: 8089
......
# rubocop:disable all
class InfluxdbRemoteDatabaseSetting < ActiveRecord::Migration
def change
remove_column :application_settings, :metrics_database
......
# rubocop:disable all
class AddArtifactsMetadataToCiBuild < ActiveRecord::Migration
def change
add_column :ci_builds, :artifacts_metadata, :text
......
# rubocop:disable all
class AddAkismetToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
......
# rubocop:disable all
class RemoveAlertTypeFromBroadcastMessages < ActiveRecord::Migration
def change
remove_column :broadcast_messages, :alert_type, :integer
......
# rubocop:disable all
class AddIndexMilestonesTitle < ActiveRecord::Migration
def change
add_index :milestones, :title
......
# rubocop:disable all
class RemoveInfluxdbCredentials < ActiveRecord::Migration
def change
remove_column :application_settings, :metrics_username, :string
......
# rubocop:disable all
class CreateSpamLogs < ActiveRecord::Migration
def change
create_table :spam_logs do |t|
......
# rubocop:disable all
class AddMetricsSampleInterval < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_sample_interval, :integer,
......
# rubocop:disable all
class AddSentryToApplicationSettings < ActiveRecord::Migration
def change
change_table :application_settings do |t|
......
# rubocop:disable all
class AddIpBlockingSettingsToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :ip_blocking_enabled, :boolean, default: false
......
# rubocop:disable all
class AddServicesCategory < ActiveRecord::Migration
def up
add_column :services, :category, :string, default: 'common', null: false
......
# rubocop:disable all
class AddServicesDefault < ActiveRecord::Migration
def up
add_column :services, :default, :boolean, default: false
......
# rubocop:disable all
class AddLdapEmailToUsers < ActiveRecord::Migration
def up
add_column :users, :ldap_email, :boolean, default: false, null: false
......
# rubocop:disable all
class AddBaseCommitShaToMergeRequestDiffs < ActiveRecord::Migration
def change
add_column :merge_request_diffs, :base_commit_sha, :string
......
# rubocop:disable all
class AddEmailAuthorInBodyToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :email_author_in_body, :boolean, default: false
......
# rubocop:disable all
class AddPendingDeleteToProject < ActiveRecord::Migration
def change
add_column :projects, :pending_delete, :boolean, default: false
......
# rubocop:disable all
class RemoveIpBlockingSettingsFromApplicationSettings < ActiveRecord::Migration
def change
remove_column :application_settings, :ip_blocking_enabled, :boolean, default: false
......
# rubocop:disable all
class ChangeLfsObjectsSizeColumn < ActiveRecord::Migration
def change
change_column :lfs_objects, :size, :integer, limit: 8
......
# rubocop:disable all
class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration
include Gitlab::ShellAdapter
......
# rubocop:disable all
class AddMergeCommitShaToMergeRequests < ActiveRecord::Migration
def change
add_column :merge_requests, :merge_commit_sha, :string
......
# rubocop:disable all
class AddErasableToCiBuild < ActiveRecord::Migration
def change
add_reference :ci_builds, :erased_by, references: :users, index: true
......
# rubocop:disable all
class AddAllowGuestToAccessBuildsProject < ActiveRecord::Migration
def change
add_column :projects, :public_builds, :boolean, default: true, null: false
......
# rubocop:disable all
class AddRealSizeToMergeRequestDiffs < ActiveRecord::Migration
def change
add_column :merge_request_diffs, :real_size, :string
......
# rubocop:disable all
class AddIndexToSnippet < ActiveRecord::Migration
def change
add_index :snippets, :updated_at
......
# rubocop:disable all
class CreateTasks < ActiveRecord::Migration
def change
create_table :tasks do |t|
......
# rubocop:disable all
class AddDescriptionToLabel < ActiveRecord::Migration
def change
add_column :labels, :description, :string
......
# rubocop:disable all
class AddNoteToTasks < ActiveRecord::Migration
def change
add_reference :tasks, :note, index: true
......
# rubocop:disable all
class RenameTasksToTodos < ActiveRecord::Migration
def change
rename_table :tasks, :todos
......
# rubocop:disable all
class CreateAppearancesCe < ActiveRecord::Migration
def change
unless table_exists?(:appearances)
......
# rubocop:disable all
class AddConfidentialToIssues < ActiveRecord::Migration
def change
add_column :issues, :confidential, :boolean, default: false
......
# rubocop:disable all
class AddDeleteAtToIssues < ActiveRecord::Migration
def change
add_column :issues, :deleted_at, :datetime
......
# rubocop:disable all
class AddDeleteAtToMergeRequests < ActiveRecord::Migration
def change
add_column :merge_requests, :deleted_at, :datetime
......
# rubocop:disable all
class AddTrigramIndexesForSearching < ActiveRecord::Migration
disable_ddl_transaction!
......
# rubocop:disable all
class AddEventFieldForWebHook < ActiveRecord::Migration
def change
add_column :web_hooks, :wiki_page_events, :boolean, default: false, null: false
......
# rubocop:disable all
class AddEventToServices < ActiveRecord::Migration
def change
add_column :services, :wiki_page_events, :boolean, default: true
......
# rubocop:disable all
class AddMainLanguageToRepository < ActiveRecord::Migration
def change
add_column :projects, :main_language, :string
......
# rubocop:disable all
class AddVisibilityLevelToGroups < ActiveRecord::Migration
def up
add_column :namespaces, :visibility_level, :integer, null: false, default: Gitlab::VisibilityLevel::PUBLIC
......
# rubocop:disable all
class AddImportCredentialsToProjectImportData < ActiveRecord::Migration
def change
add_column :project_import_data, :encrypted_credentials, :text
......
# rubocop:disable all
# Loops through old importer projects that kept a token/password in the import URL
# and encrypts the credentials into a separate field in project#import_data
# #down method not supported
......
# rubocop:disable all
class RemoveExpiresAtFromSnippets < ActiveRecord::Migration
def change
remove_column :snippets, :expires_at, :datetime
......
# rubocop:disable all
class DisallowBlankLineCodeOnNote < ActiveRecord::Migration
def up
execute("UPDATE notes SET line_code = NULL WHERE line_code = ''")
......
# rubocop:disable all
# Create visibility level field on DB
# Sets default_visibility_level to value on settings if not restricted
# If value is restricted takes higher visibility level allowed
......
# rubocop:disable all
class FixTodos < ActiveRecord::Migration
def up
execute <<-SQL
......
# rubocop:disable all
class AddDueDateToIssues < ActiveRecord::Migration
def change
add_column :issues, :due_date, :date
......
# rubocop:disable all
class AddExternalFlagToUsers < ActiveRecord::Migration
def change
add_column :users, :external, :boolean, default: false
......
# rubocop:disable all
class AddPriorityToLabel < ActiveRecord::Migration
def change
add_column :labels, :priority, :integer
......
# rubocop:disable all
class ProjectsAddPushesSinceGc < ActiveRecord::Migration
def change
add_column :projects, :pushes_since_gc, :integer, default: 0
......
# rubocop:disable all
class ProjectAddRepositoryCheck < ActiveRecord::Migration
def change
add_column :projects, :last_repository_check_failed, :boolean
......
# rubocop:disable all
class CiRunnersTokenIndex < ActiveRecord::Migration
disable_ddl_transaction!
......
# rubocop:disable all
class ChangeTargetIdToNullOnTodos < ActiveRecord::Migration
def change
change_column_null :todos, :target_id, true
......
# rubocop:disable all
class AddCommitIdToTodos < ActiveRecord::Migration
def change
add_column :todos, :commit_id, :string
......
# rubocop:disable all
class AddMovedToToIssue < ActiveRecord::Migration
def change
add_reference :issues, :moved_to, references: :issues
......
# rubocop:disable all
class IndexNamespacesOnVisibilityLevel < ActiveRecord::Migration
def change
unless index_exists?(:namespaces, :visibility_level)
......
# rubocop:disable all
class RemoveTodosForDeletedIssues < ActiveRecord::Migration
def up
execute <<-SQL
......
# rubocop:disable all
class CreateNotificationSettings < ActiveRecord::Migration
def change
create_table :notification_settings do |t|
......
# rubocop:disable all
# This migration will create one row of NotificationSetting for each Member row
# It can take long time on big instances.
#
......
# rubocop:disable all
class AddNotificationSettingIndex < ActiveRecord::Migration
def change
add_index :notification_settings, :user_id
......
# rubocop:disable all
class AddIndexOnPendingDeleteProjects < ActiveRecord::Migration
def change
add_index :projects, :pending_delete
......
# rubocop:disable all
class RemoveTodosForDeletedMergeRequests < ActiveRecord::Migration
def up
execute <<-SQL
......
# rubocop:disable all
class RemoveTwitterSharingEnabledFromApplicationSettings < ActiveRecord::Migration
def change
remove_column :application_settings, :twitter_sharing_enabled, :boolean
......
# rubocop:disable all
class AddImagesEnabledForProject < ActiveRecord::Migration
def change
add_column :projects, :container_registry_enabled, :boolean
......
# rubocop:disable all
class AddRepositoryChecksEnabledSetting < ActiveRecord::Migration
def change
add_column :application_settings, :repository_checks_enabled, :boolean, default: true
......
# rubocop:disable all
class AddFieldsToCiCommit < ActiveRecord::Migration
def change
add_column :ci_commits, :status, :string
......
# rubocop:disable all
class UpdateCiCommit < ActiveRecord::Migration
# This migration can be run online, but needs to be executed for the second time after restarting Unicorn workers
# Otherwise Offline migration should be used.
......
# rubocop:disable all
class AddCiCommitIndexes < ActiveRecord::Migration
disable_ddl_transaction!
......
# rubocop:disable all
class AddTokenToWebHooks < ActiveRecord::Migration
def change
add_column :web_hooks, :token, :string
......
# rubocop:disable all
class AddSharedRunnersTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :shared_runners_text, :text
......
# rubocop:disable all
class AddAwardEmoji < ActiveRecord::Migration
def change
create_table :award_emoji do |t|
......
# rubocop:disable all
class ConvertAwardNoteToEmojiAward < ActiveRecord::Migration
def change
def up
......
# rubocop:disable all
class RemoveNoteIsAward < ActiveRecord::Migration
def change
remove_column :notes, :is_award, :boolean
......
# rubocop:disable all
class AddMetricsPacketSize < ActiveRecord::Migration
def change
add_column :application_settings, :metrics_packet_size, :integer, default: 1
......
class AddOnlyAllowMergeIfBuildSucceedsToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
add_column_with_default(:projects,
:only_allow_merge_if_build_succeeds,
:boolean,
default: false)
end
def down
remove_column(:projects, :only_allow_merge_if_build_succeeds)
end
end
# rubocop:disable all
class DisableRepositoryChecks < ActiveRecord::Migration
def up
change_column_default :application_settings, :repository_checks_enabled, false
......
# rubocop:disable all
class CreateU2fRegistrations < ActiveRecord::Migration
def change
create_table :u2f_registrations do |t|
......
# rubocop:disable all
class AddDisabledOauthSignInSourcesToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :disabled_oauth_sign_in_sources, :text
......
# rubocop:disable all
class AddRunUntaggedToCiRunner < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
......
# rubocop:disable all
class RemoveWallEnabledFromProjects < ActiveRecord::Migration
def change
remove_column :projects, :wall_enabled, :boolean, default: true, null: false
......
# rubocop:disable all
class AddTypeToNotes < ActiveRecord::Migration
def change
add_column :notes, :type, :string
......
# rubocop:disable all
class SetTypeOnLegacyDiffNotes < ActiveRecord::Migration
def change
execute "UPDATE notes SET type = 'LegacyDiffNote' WHERE line_code IS NOT NULL"
......
# rubocop:disable all
class AddHealthCheckAccessTokenToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :health_check_access_token, :string
......
# rubocop:disable all
class AddSendUserConfirmationEmailToApplicationSettings < ActiveRecord::Migration
def up
add_column :application_settings, :send_user_confirmation_email, :boolean, default: false
......
# rubocop:disable all
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
......
# rubocop:disable all
class RemoveNotificationSettingsForDeletedProjects < ActiveRecord::Migration
def up
execute <<-SQL
......
# rubocop:disable all
class AddUsersStateIndex < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# rubocop:disable all
# This is ONLINE migration
class AddContainerRegistryTokenExpireDelayToApplicationSettings < ActiveRecord::Migration
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddHasExternalIssueTrackerToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_column(:projects, :has_external_issue_tracker, :boolean)
end
end
# rubocop:disable all
class RemoveDuplicatedNotificationSettings < ActiveRecord::Migration
def up
duplicates = exec_query(%Q{
......
# rubocop:disable all
class AddIndexToNotificationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
......
# rubocop:disable all
class AddAfterSignUpTextToApplicationSettings < ActiveRecord::Migration
def change
add_column :application_settings, :after_sign_up_text, :text
......
# rubocop:disable all
class LimitsToMysql < ActiveRecord::Migration
def up
return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
......
......@@ -779,6 +779,8 @@ ActiveRecord::Schema.define(version: 20160608155312) do
t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at"
t.boolean "container_registry_enabled"
t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false
t.boolean "has_external_issue_tracker"
end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
......
......@@ -62,7 +62,7 @@ registry:
host: registry.gitlab.example.com
port: 5005
api_url: http://localhost:5000/
key_path: config/registry.key
key: config/registry.key
path: shared/registry
issuer: gitlab-issuer
```
......@@ -75,7 +75,7 @@ where:
| `host` | The host URL under which the Registry will run and the users will be able to use. |
| `port` | The port under which the external Registry domain will listen on. |
| `api_url` | The internal API URL under which the Registry is exposed to. It defaults to `http://localhost:5000`. |
| `key_path`| The private key location that is a pair of Registry's `rootcertbundle`. Read the [token auth configuration documentation][token-config]. |
| `key` | The private key location that is a pair of Registry's `rootcertbundle`. Read the [token auth configuration documentation][token-config]. |
| `path` | This should be the same directory like specified in Registry's `rootdirectory`. Read the [storage configuration documentation][storage-config]. This path needs to be readable by the GitLab user, the web-server user and the Registry user. Read more in [#container-registry-storage-path](#container-registry-storage-path). |
| `issuer` | This should be the same value as configured in Registry's `issuer`. Read the [token auth configuration documentation][token-config]. |
......
......@@ -572,7 +572,7 @@ GET /projects/:id/merge_requests/:merge_request_id/closes_issues
curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/76/merge_requests/1/closes_issues
```
Example response:
Example response when the GitLab issue tracker is used:
```json
[
......@@ -618,6 +618,17 @@ Example response:
]
```
Example response when an external issue tracker (e.g. JIRA) is used:
```json
[
{
"id" : "PROJECT-123",
"title" : "Title of this issue"
}
]
```
## Subscribe to a merge request
Subscribes the authenticated user to a merge request to receive notification. If
......
......@@ -2,6 +2,17 @@
Merge requests allow you to exchange changes you made to source code
## Only allow merge requests to be merged if the build succeeds
You can prevent merge requests from being merged if their build did not succeed
in the project settings page.
![only_allow_merge_if_build_succeeds](merge_requests/only_allow_merge_if_build_succeeds.png)
Navigate to project settings page and select the `Only allow merge requests to be merged if the build succeeds` check box.
Please note that you need to have builds configured to enable this feature.
## Checkout merge requests locally
Locate the section for your GitLab remote in the `.git/config` file. It looks like this:
......
......@@ -107,12 +107,16 @@ Feature: Project Active Tab
Scenario: On Project Issues/Milestones
Given I visit my project's issues page
And I click the "Milestones" tab
Then the active main tab should be Milestones
And I click the "Milestones" sub tab
Then the active main tab should be Issues
Then the active sub tab should be Milestones
And no other main tabs should be active
And no other sub tabs should be active
Scenario: On Project Issues/Labels
Given I visit my project's issues page
And I click the "Labels" tab
Then the active main tab should be Labels
And I click the "Labels" sub tab
Then the active main tab should be Issues
Then the active sub tab should be Labels
And no other main tabs should be active
And no other sub tabs should be active
......@@ -155,6 +155,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I click on my profile picture' do
find(:css, '.side-nav-toggle').click
find(:css, '.sidebar-user').click
end
......
......@@ -77,14 +77,14 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
# Sub Tabs: Issues
step 'I click the "Milestones" tab' do
page.within('.layout-nav') do
step 'I click the "Milestones" sub tab' do
page.within('.sub-nav') do
click_link('Milestones')
end
end
step 'I click the "Labels" tab' do
page.within('.layout-nav') do
step 'I click the "Labels" sub tab' do
page.within('.sub-nav') do
click_link('Labels')
end
end
......@@ -93,11 +93,11 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
ensure_active_sub_tab('Issues')
end
step 'the active main tab should be Milestones' do
ensure_active_main_tab('Milestones')
step 'the active sub tab should be Milestones' do
ensure_active_sub_tab('Milestones')
end
step 'the active main tab should be Labels' do
ensure_active_main_tab('Labels')
step 'the active sub tab should be Labels' do
ensure_active_sub_tab('Labels')
end
end
......@@ -5,11 +5,11 @@ class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
include RepoHelpers
step 'I click artifacts download button' do
page.within('.artifacts') { click_link 'Download' }
click_link 'Download'
end
step 'I click artifacts browse button' do
page.within('.artifacts') { click_link 'Browse' }
click_link 'Browse'
end
step 'I should see content of artifacts archive' do
......
......@@ -6,7 +6,7 @@ module SharedActiveTab
end
def ensure_active_sub_tab(content)
expect(find('div.content ul.nav-links li.active')).to have_content(content)
expect(find('.sub-nav li.active')).to have_content(content)
end
def ensure_active_sub_nav(content)
......@@ -18,7 +18,7 @@ module SharedActiveTab
end
step 'no other sub tabs should be active' do
expect(page).to have_selector('div.content ul.nav-links li.active', count: 1)
expect(page).to have_selector('.sub-nav li.active', count: 1)
end
step 'no other sub navs should be active' do
......
......@@ -179,6 +179,11 @@ module API
expose :upvotes, :downvotes
end
class ExternalIssue < Grape::Entity
expose :title
expose :id
end
class MergeRequest < ProjectEntity
expose :target_branch, :source_branch
expose :upvotes, :downvotes
......
......@@ -408,5 +408,23 @@ module API
error!(errors[:access_level], 422) if errors[:access_level].any?
not_found!(errors)
end
def send_git_blob(repository, blob)
env['api.format'] = :txt
content_type 'text/plain'
header(*Gitlab::Workhorse.send_git_blob(repository, blob))
end
def send_git_archive(repository, ref:, format:)
header(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
end
def issue_entity(project)
if project.has_external_issue_tracker?
Entities::ExternalIssue
else
Entities::Issue
end
end
end
end
......@@ -228,11 +228,10 @@ module API
# Merge request can not be merged
# because user dont have permissions to push into target branch
unauthorized! unless merge_request.can_be_merged_by?(current_user)
not_allowed! if !merge_request.open? || merge_request.work_in_progress?
merge_request.check_if_can_be_merged
not_allowed! unless merge_request.mergeable_state?
render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
render_api_error!('Branch cannot be merged', 406) unless merge_request.mergeable?
if params[:sha] && merge_request.source_sha != params[:sha]
render_api_error!("SHA does not match HEAD of source branch: #{merge_request.source_sha}", 409)
......@@ -330,7 +329,7 @@ module API
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, current_user: current_user
present paginate(issues), with: issue_entity(user_project), current_user: current_user
end
end
end
......
......@@ -56,8 +56,7 @@ module API
blob = Gitlab::Git::Blob.find(repo, commit.id, params[:filepath])
not_found! "File" unless blob
content_type 'text/plain'
header(*Gitlab::Workhorse.send_git_blob(repo, blob))
send_git_blob repo, blob
end
# Get a raw blob contents by blob sha
......@@ -80,10 +79,7 @@ module API
not_found! 'Blob' unless blob
env['api.format'] = :txt
content_type blob.mime_type
header(*Gitlab::Workhorse.send_git_blob(repo, blob))
send_git_blob repo, blob
end
# Get a an archive of the repository
......@@ -98,7 +94,7 @@ module API
authorize! :download_code, user_project
begin
header(*Gitlab::Workhorse.send_git_archive(user_project, params[:sha], params[:format]))
send_git_archive user_project.repository, ref: params[:sha], format: params[:format]
rescue
not_found!('File')
end
......
......@@ -38,7 +38,6 @@ module Backup
end
def upload(tar_file)
remote_directory = Gitlab.config.backup.upload.remote_directory
$progress.print "Uploading backup archive to remote storage #{remote_directory} ... "
connection_settings = Gitlab.config.backup.upload.connection
......@@ -47,8 +46,7 @@ module Backup
return
end
connection = ::Fog::Storage.new(connection_settings)
directory = connection.directories.create(key: remote_directory)
directory = connect_to_remote_directory(connection_settings)
if directory.files.create(key: tar_file, body: File.open(tar_file), public: false,
multipart_chunk_size: Gitlab.config.backup.upload.multipart_chunk_size,
......@@ -155,6 +153,23 @@ module Backup
private
def connect_to_remote_directory(connection_settings)
connection = ::Fog::Storage.new(connection_settings)
# We only attempt to create the directory for local backups. For AWS
# and other cloud providers, we cannot guarantee the user will have
# permission to create the bucket.
if connection.service == ::Fog::Storage::Local
connection.directories.create(key: remote_directory)
else
connection.directories.get(remote_directory)
end
end
def remote_directory
Gitlab.config.backup.upload.remote_directory
end
def backup_contents
folders_to_backup + archives_to_backup + ["backup_information.yml"]
end
......
......@@ -56,7 +56,7 @@ module Ci
class TriggerRequest < Grape::Entity
expose :id, :variables
expose :commit, using: Commit
expose :pipeline, using: Commit, as: :commit
end
end
end
......
module Gitlab
module GithubImport
class Client
GITHUB_SAFE_REMAINING_REQUESTS = 100
GITHUB_SAFE_SLEEP_TIME = 500
attr_reader :client, :api
def initialize(access_token)
......@@ -11,7 +14,7 @@ module Gitlab
)
if access_token
::Octokit.auto_paginate = true
::Octokit.auto_paginate = false
@api = ::Octokit::Client.new(
access_token: access_token,
......@@ -36,7 +39,7 @@ module Gitlab
def method_missing(method, *args, &block)
if api.respond_to?(method)
api.send(method, *args, &block)
request { api.send(method, *args, &block) }
else
super(method, *args, &block)
end
......@@ -55,6 +58,34 @@ module Gitlab
def github_options
config["args"]["client_options"].deep_symbolize_keys
end
def rate_limit
api.rate_limit!
end
def rate_limit_exceed?
rate_limit.remaining <= GITHUB_SAFE_REMAINING_REQUESTS
end
def rate_limit_sleep_time
rate_limit.resets_in + GITHUB_SAFE_SLEEP_TIME
end
def request
sleep rate_limit_sleep_time if rate_limit_exceed?
data = yield
last_response = api.last_response
while last_response.rels[:next]
sleep rate_limit_sleep_time if rate_limit_exceed?
last_response = last_response.rels[:next].get
data.concat(last_response.data) if last_response.data.is_a?(Array)
end
data
end
end
end
end
......@@ -3,9 +3,6 @@ module Gitlab
class Importer
include Gitlab::ShellAdapter
GITHUB_SAFE_REMAINING_REQUESTS = 100
GITHUB_SAFE_SLEEP_TIME = 500
attr_reader :client, :project, :repo, :repo_url
def initialize(project)
......@@ -28,52 +25,12 @@ module Gitlab
private
def turn_auto_pagination_off!
client.auto_paginate = false
end
def turn_auto_pagination_on!
client.auto_paginate = true
end
def rate_limit
client.rate_limit!
end
def rate_limit_exceed?
rate_limit.remaining <= GITHUB_SAFE_REMAINING_REQUESTS
end
def rate_limit_sleep_time
rate_limit.resets_in + GITHUB_SAFE_SLEEP_TIME
end
def paginate
turn_auto_pagination_off!
sleep rate_limit_sleep_time if rate_limit_exceed?
data = yield
last_response = client.last_response
while last_response.rels[:next]
sleep rate_limit_sleep_time if rate_limit_exceed?
last_response = last_response.rels[:next].get
data.concat(last_response.data) if last_response.data.is_a?(Array)
end
turn_auto_pagination_on!
data
end
def credentials
@credentials ||= project.import_data.credentials if project.import_data
end
def import_labels
labels = paginate { client.labels(repo, per_page: 100) }
labels = client.labels(repo, per_page: 100)
labels.each { |raw| LabelFormatter.new(project, raw).create! }
true
......@@ -82,7 +39,7 @@ module Gitlab
end
def import_milestones
milestones = paginate { client.milestones(repo, state: :all, per_page: 100) }
milestones = client.milestones(repo, state: :all, per_page: 100)
milestones.each { |raw| MilestoneFormatter.new(project, raw).create! }
true
......@@ -91,9 +48,9 @@ module Gitlab
end
def import_issues
data = paginate { client.issues(repo, state: :all, sort: :created, direction: :asc, per_page: 100) }
issues = client.issues(repo, state: :all, sort: :created, direction: :asc, per_page: 100)
data.each do |raw|
issues.each do |raw|
gh_issue = IssueFormatter.new(project, raw)
if gh_issue.valid?
......@@ -112,7 +69,7 @@ module Gitlab
hooks = client.hooks(repo).map { |raw| HookFormatter.new(raw) }.select(&:valid?)
disable_webhooks(hooks)
pull_requests = paginate { client.pull_requests(repo, state: :all, sort: :created, direction: :asc, per_page: 100) }
pull_requests = client.pull_requests(repo, state: :all, sort: :created, direction: :asc, per_page: 100)
pull_requests = pull_requests.map { |raw| PullRequestFormatter.new(project, raw) }.select(&:valid?)
source_branches_removed = pull_requests.reject(&:source_branch_exists?).map { |pr| [pr.source_branch_name, pr.source_branch_sha] }
......@@ -146,14 +103,12 @@ module Gitlab
def update_webhooks(hooks, options)
hooks.each do |hook|
sleep rate_limit_sleep_time if rate_limit_exceed?
client.edit_hook(repo, hook.id, hook.name, hook.config, options)
end
end
def restore_branches(branches)
branches.each do |name, sha|
sleep rate_limit_sleep_time if rate_limit_exceed?
client.create_ref(repo, "refs/heads/#{name}", sha)
end
......@@ -162,15 +117,12 @@ module Gitlab
def clean_up_restored_branches(branches)
branches.each do |name, _|
sleep rate_limit_sleep_time if rate_limit_exceed?
client.delete_ref(repo, "heads/#{name}")
project.repository.rm_branch(project.creator, name)
end
end
def apply_labels(issuable)
sleep rate_limit_sleep_time if rate_limit_exceed?
issue = client.issue(repo, issuable.iid)
if issue.labels.count > 0
......@@ -183,12 +135,12 @@ module Gitlab
end
def import_comments(issuable)
comments = paginate { client.issue_comments(repo, issuable.iid, per_page: 100) }
comments = client.issue_comments(repo, issuable.iid, per_page: 100)
create_comments(issuable, comments)
end
def import_comments_on_diff(merge_request)
comments = paginate { client.pull_request_comments(repo, merge_request.iid, per_page: 100) }
comments = client.pull_request_comments(repo, merge_request.iid, per_page: 100)
create_comments(merge_request, comments)
end
......
......@@ -12,22 +12,44 @@ module Gitlab
def scrub(node)
unless Whitelist::ALLOWED_ELEMENTS.include?(node.name)
node.unlink
else
node.attributes.each do |attr_name, attr|
return
end
valid_attributes = Whitelist::ALLOWED_ATTRIBUTES[node.name]
return unless valid_attributes
node.attribute_nodes.each do |attr|
attr_name = attribute_name_with_namespace(attr)
unless valid_attributes && valid_attributes.include?(attr_name)
if Whitelist::ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS.include?(node.name) &&
attr_name.start_with?('data-')
# Arbitrary data attributes are allowed. Verify that the attribute
# is a valid data attribute.
attr.unlink unless attr_name =~ DATA_ATTR_PATTERN
if valid_attributes.include?(attr_name)
attr.unlink if unsafe_href?(attr)
else
# Arbitrary data attributes are allowed.
unless allows_data_attribute?(node) && data_attribute?(attr)
attr.unlink
end
end
end
end
def attribute_name_with_namespace(attr)
if attr.namespace
"#{attr.namespace.prefix}:#{attr.name}"
else
attr.name
end
end
def allows_data_attribute?(node)
Whitelist::ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS.include?(node.name)
end
def unsafe_href?(attr)
attribute_name_with_namespace(attr) == 'xlink:href' && !attr.value.start_with?('#')
end
def data_attribute?(attr)
attr.name.start_with?('data-') && attr.name =~ DATA_ATTR_PATTERN && attr.namespace.nil?
end
end
end
......
......@@ -21,27 +21,29 @@ module Gitlab
[
SEND_DATA_HEADER,
"git-blob:#{encode(params)}",
"git-blob:#{encode(params)}"
]
end
def send_git_archive(project, ref, format)
def send_git_archive(repository, ref:, format:)
format ||= 'tar.gz'
format.downcase!
params = project.repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format)
params = repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format)
raise "Repository or ref not found" if params.empty?
[
SEND_DATA_HEADER,
"git-archive:#{encode(params)}",
"git-archive:#{encode(params)}"
]
end
def send_git_diff(repository, from, to)
def send_git_diff(repository, diff_refs)
from, to = diff_refs
params = {
'RepoPath' => repository.path_to_repo,
'ShaFrom' => from,
'ShaTo' => to
'ShaFrom' => from.sha,
'ShaTo' => to.sha
}
[
......
......@@ -91,7 +91,7 @@ describe Projects::MergeRequestsController do
id: merge_request.iid,
format: :diff)
expect(response.headers['Gitlab-Workhorse-Send-Data']).to start_with("git-diff:")
expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-diff:")
end
end
......
......@@ -17,6 +17,7 @@ describe Projects::RawController do
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
expect(response.header['Content-Disposition']).
to eq("inline")
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:")
end
end
......@@ -31,6 +32,7 @@ describe Projects::RawController do
expect(response.status).to eq(200)
expect(response.header['Content-Type']).to eq('image/jpeg')
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:")
end
end
......
......@@ -20,10 +20,11 @@ describe Projects::RepositoriesController do
project.team << [user, :developer]
sign_in(user)
end
it "uses Gitlab::Workhorse" do
expect(Gitlab::Workhorse).to receive(:send_git_archive).with(project, "master", "zip")
it "uses Gitlab::Workhorse" do
get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:")
end
context "when the service raises an error" do
......
......@@ -93,11 +93,9 @@ describe "Builds" do
end
it 'has button to download artifacts' do
page.within('.artifacts') do
expect(page).to have_content 'Download'
end
end
end
context 'Build raw trace' do
before do
......@@ -107,12 +105,10 @@ describe "Builds" do
end
it do
page.within('.build-controls') do
expect(page).to have_link 'Raw'
end
end
end
end
describe "POST /:project/builds/:id/cancel" do
context "Build from project" do
......@@ -165,15 +161,10 @@ describe "Builds" do
end
describe "GET /:project/builds/:id/download" do
context "Build from project" do
before do
@build.update_attributes(artifacts_file: artifacts_file)
visit namespace_project_build_path(@project.namespace, @project, @build)
page.within('.artifacts') { click_link 'Download' }
end
it { expect(page.status_code).to eq(200) }
it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) }
click_link 'Download'
end
context "Build from other project" do
......@@ -193,7 +184,7 @@ describe "Builds" do
@build.run!
@build.trace = 'BUILD TRACE'
visit namespace_project_build_path(@project.namespace, @project, @build)
page.within('.build-controls') { click_link 'Raw' }
page.within('.js-build-sidebar') { click_link 'Raw' }
end
it 'sends the right headers' do
......
......@@ -199,4 +199,19 @@ feature 'Issue filtering by Labels', feature: true do
end
end
end
context 'dropdown filtering', js: true do
it 'should filter by label name' do
page.within '.labels-filter' do
click_button 'Label'
wait_for_ajax
fill_in 'label-name', with: 'bug'
page.within '.dropdown-content' do
expect(page).not_to have_content 'enhancement'
expect(page).to have_content 'bug'
end
end
end
end
end
......@@ -294,40 +294,4 @@ describe 'Filter issues', feature: true do
end
end
end
describe 'filter by any author', js: true do
before do
user2 = create(:user, name: "tester")
create(:issue, project: project, author: user)
create(:issue, project: project, author: user2)
visit namespace_project_issues_path(project.namespace, project)
end
it 'should show filter by any author link' do
click_button "Author"
fill_in "Search authors", with: "tester"
page.within ".dropdown-menu-author" do
expect(page).to have_content "tester"
end
end
it 'should show filter issues by any author' do
page.within '.issues-list' do
expect(page).to have_selector ".issue", count: 2
end
click_button "Author"
fill_in "Search authors", with: "tester"
page.within ".dropdown-menu-author" do
click_link "tester"
end
page.within '.issues-list' do
expect(page).to have_selector ".issue", count: 1
end
end
end
end
require 'spec_helper'
feature 'Only allow merge requests to be merged if the build succeeds', feature: true do
let(:project) { create(:project, :public) }
let(:merge_request) { create(:merge_request_with_diffs, source_project: project) }
before do
login_as merge_request.author
project.team << [merge_request.author, :master]
end
context 'project does not have CI enabled' do
it 'allows MR to be merged' do
visit_merge_request(merge_request)
expect(page).to have_button 'Accept Merge Request'
end
end
context 'when project has CI enabled' do
let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) }
context 'when merge requests can only be merged if the build succeeds' do
before do
project.update_attribute(:only_allow_merge_if_build_succeeds, true)
end
context 'when CI is running' do
before { pipeline.update_column(:status, :running) }
it 'does not allow to merge immediately' do
visit_merge_request(merge_request)
expect(page).to have_button 'Merge When Build Succeeds'
expect(page).not_to have_button 'Select Merge Moment'
end
end
context 'when CI failed' do
before { pipeline.update_column(:status, :failed) }
it 'does not allow MR to be merged' do
visit_merge_request(merge_request)
expect(page).not_to have_button 'Accept Merge Request'
expect(page).to have_content('Please retry the build or push a new commit to fix the failure.')
end
end
context 'when CI succeeded' do
before { pipeline.update_column(:status, :success) }
it 'allows MR to be merged' do
visit_merge_request(merge_request)
expect(page).to have_button 'Accept Merge Request'
end
end
end
context 'when merge requests can be merged when the build failed' do
before do
project.update_attribute(:only_allow_merge_if_build_succeeds, false)
end
context 'when CI is running' do
before { pipeline.update_column(:status, :running) }
it 'allows MR to be merged immediately', js: true do
visit_merge_request(merge_request)
expect(page).to have_button 'Merge When Build Succeeds'
click_button 'Select Merge Moment'
expect(page).to have_content 'Merge Immediately'
end
end
context 'when CI failed' do
before { pipeline.update_column(:status, :failed) }
it 'allows MR to be merged' do
visit_merge_request(merge_request)
expect(page).to have_button 'Accept Merge Request'
end
end
context 'when CI succeeded' do
before { pipeline.update_column(:status, :success) }
it 'allows MR to be merged' do
visit_merge_request(merge_request)
expect(page).to have_button 'Accept Merge Request'
end
end
end
end
def visit_merge_request(merge_request)
visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request)
end
end
......@@ -54,7 +54,7 @@ describe 'Profile > Preferences', feature: true do
end
end
describe 'User changes their default dashboard' do
describe 'User changes their default dashboard', js: true do
it 'creates a flash message' do
select 'Starred Projects', from: 'user_dashboard'
click_button 'Save'
......@@ -66,8 +66,10 @@ describe 'Profile > Preferences', feature: true do
select 'Starred Projects', from: 'user_dashboard'
click_button 'Save'
click_link 'Dashboard'
allowing_for_delay do
find('#logo').click
expect(page.current_path).to eq starred_dashboard_projects_path
end
click_link 'Your Projects'
expect(page.current_path).to eq dashboard_projects_path
......
......@@ -52,8 +52,8 @@ describe Banzai::Pipeline::WikiPipeline do
end
describe "Links" do
let(:namespace) { build_stubbed(:namespace, name: "wiki_link_ns") }
let(:project) { build_stubbed(:empty_project, :public, name: "wiki_link_project", namespace: namespace) }
let(:namespace) { create(:namespace, name: "wiki_link_ns") }
let(:project) { create(:empty_project, :public, name: "wiki_link_project", namespace: namespace) }
let(:project_wiki) { ProjectWiki.new(project, double(:user)) }
let(:page) { build(:wiki_page, wiki: project_wiki, page: OpenStruct.new(url_path: 'nested/twice/start-page')) }
......
......@@ -2,7 +2,7 @@ require 'spec_helper'
describe DisableEmailInterceptor, lib: true do
before do
ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
Mail.register_interceptor(DisableEmailInterceptor)
end
it 'should not send emails' do
......@@ -14,7 +14,7 @@ describe DisableEmailInterceptor, lib: true do
# Removing interceptor from the list because unregister_interceptor is
# implemented in later version of mail gem
# See: https://github.com/mikel/mail/pull/705
Mail.class_variable_set(:@@delivery_interceptors, [])
Mail.unregister_interceptor(DisableEmailInterceptor)
end
def deliver_mail
......
require 'spec_helper'
describe Gitlab::Sanitizers::SVG do
let(:scrubber) { Gitlab::Sanitizers::SVG::Scrubber.new }
let(:namespace) { double(Nokogiri::XML::Namespace, prefix: 'xlink', href: 'http://www.w3.org/1999/xlink') }
let(:namespaced_attr) { double(Nokogiri::XML::Attr, name: 'href', namespace: namespace, value: '#awesome_id') }
describe '.clean' do
let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
let(:data) { open(input_svg_path).read }
let(:sanitized_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
let(:sanitized) { open(sanitized_svg_path).read }
it 'delegates sanitization to scrubber' do
expect_any_instance_of(Gitlab::Sanitizers::SVG::Scrubber).to receive(:scrub).at_least(:once)
described_class.clean(data)
end
it 'returns sanitized data' do
expect(described_class.clean(data)).to eq(sanitized)
end
end
context 'scrubber' do
describe '#scrub' do
let(:invalid_element) { double(Nokogiri::XML::Node, name: 'invalid', value: 'invalid') }
let(:invalid_attribute) { double(Nokogiri::XML::Attr, name: 'invalid', namespace: nil) }
let(:valid_element) { double(Nokogiri::XML::Node, name: 'use') }
it 'removes an invalid element' do
expect(invalid_element).to receive(:unlink)
scrubber.scrub(invalid_element)
end
it 'removes an invalid attribute' do
allow(valid_element).to receive(:attribute_nodes) { [invalid_attribute] }
expect(invalid_attribute).to receive(:unlink)
scrubber.scrub(valid_element)
end
it 'accepts valid element' do
allow(valid_element).to receive(:attribute_nodes) { [namespaced_attr] }
expect(valid_element).not_to receive(:unlink)
scrubber.scrub(valid_element)
end
it 'accepts valid namespaced attributes' do
allow(valid_element).to receive(:attribute_nodes) { [namespaced_attr] }
expect(namespaced_attr).not_to receive(:unlink)
scrubber.scrub(valid_element)
end
end
describe '#attribute_name_with_namespace' do
it 'returns name with prefix when attribute is namespaced' do
expect(scrubber.attribute_name_with_namespace(namespaced_attr)).to eq('xlink:href')
end
end
describe '#unsafe_href?' do
let(:unsafe_attr) { double(Nokogiri::XML::Attr, name: 'href', namespace: namespace, value: 'http://evilsite.example.com/random.svg') }
it 'returns true if href attribute is an external url' do
expect(scrubber.unsafe_href?(unsafe_attr)).to be_truthy
end
it 'returns false if href atttribute is an internal reference' do
expect(scrubber.unsafe_href?(namespaced_attr)).to be_falsey
end
end
describe '#data_attribute?' do
let(:data_attr) { double(Nokogiri::XML::Attr, name: 'data-gitlab', namespace: nil, value: 'gitlab is awesome') }
let(:namespaced_attr) { double(Nokogiri::XML::Attr, name: 'data-gitlab', namespace: namespace, value: 'gitlab is awesome') }
let(:other_attr) { double(Nokogiri::XML::Attr, name: 'something', namespace: nil, value: 'content') }
it 'returns true if is a valid data attribute' do
expect(scrubber.data_attribute?(data_attr)).to be_truthy
end
it 'returns false if attribute is namespaced' do
expect(scrubber.data_attribute?(namespaced_attr)).to be_falsey
end
it 'returns false if not a data attribute' do
expect(scrubber.data_attribute?(other_attr)).to be_falsey
end
end
end
end
......@@ -11,7 +11,7 @@ describe Gitlab::Workhorse, lib: true do
end
it "raises an error" do
expect { subject.send_git_archive(project, "master", "zip") }.to raise_error(RuntimeError)
expect { subject.send_git_archive(project.repository, ref: "master", format: "zip") }.to raise_error(RuntimeError)
end
end
end
......
......@@ -219,7 +219,7 @@ describe Ci::Build, models: true do
context 'and trigger variables' do
let(:trigger) { create(:ci_trigger, project: project) }
let(:trigger_request) { create(:ci_trigger_request_with_variables, commit: pipeline, trigger: trigger) }
let(:trigger_request) { create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger) }
let(:trigger_variables) do
[
{ key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
......
......@@ -455,4 +455,157 @@ describe MergeRequest, models: true do
expect(user2.assigned_open_merge_request_count).to eq(1)
end
end
describe '#check_if_can_be_merged' do
let(:project) { create(:project, only_allow_merge_if_build_succeeds: true) }
subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
context 'when it is not broken and has no conflicts' do
it 'is marked as mergeable' do
allow(subject).to receive(:broken?) { false }
allow(project).to receive_message_chain(:repository, :can_be_merged?) { true }
expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('can_be_merged')
end
end
context 'when broken' do
before { allow(subject).to receive(:broken?) { true } }
it 'becomes unmergeable' do
expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
end
end
context 'when it has conflicts' do
before do
allow(subject).to receive(:broken?) { false }
allow(project).to receive_message_chain(:repository, :can_be_merged?) { false }
end
it 'becomes unmergeable' do
expect { subject.check_if_can_be_merged }.to change { subject.merge_status }.to('cannot_be_merged')
end
end
end
describe '#mergeable?' do
let(:project) { create(:project) }
subject { create(:merge_request, source_project: project) }
it 'returns false if #mergeable_state? is false' do
expect(subject).to receive(:mergeable_state?) { false }
expect(subject.mergeable?).to be_falsey
end
it 'return true if #mergeable_state? is true and the MR #can_be_merged? is true' do
allow(subject).to receive(:mergeable_state?) { true }
expect(subject).to receive(:check_if_can_be_merged)
expect(subject).to receive(:can_be_merged?) { true }
expect(subject.mergeable?).to be_truthy
end
end
describe '#mergeable_state?' do
let(:project) { create(:project) }
subject { create(:merge_request, source_project: project) }
it 'checks if merge request can be merged' do
allow(subject).to receive(:mergeable_ci_state?) { true }
expect(subject).to receive(:check_if_can_be_merged)
subject.mergeable?
end
context 'when not open' do
before { subject.close }
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
end
context 'when working in progress' do
before { subject.title = 'WIP MR' }
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
end
context 'when broken' do
before { allow(subject).to receive(:broken?) { true } }
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
end
context 'when failed' do
before { allow(subject).to receive(:broken?) { false } }
context 'when project settings restrict to merge only if build succeeds and build failed' do
before do
project.only_allow_merge_if_build_succeeds = true
allow(subject).to receive(:mergeable_ci_state?) { false }
end
it 'returns false' do
expect(subject.mergeable_state?).to be_falsey
end
end
end
end
describe '#mergeable_ci_state?' do
let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
let(:pipeline) { create(:ci_empty_pipeline) }
subject { build(:merge_request, target_project: project) }
context 'when it is only allowed to merge when build is green' do
context 'and a failed pipeline is associated' do
before do
pipeline.statuses << create(:commit_status, status: 'failed', project: project)
allow(subject).to receive(:pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_falsey }
end
context 'when no pipeline is associated' do
before do
allow(subject).to receive(:pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
end
end
context 'when merges are not restricted to green builds' do
subject { build(:merge_request, target_project: build(:empty_project, only_allow_merge_if_build_succeeds: false)) }
context 'and a failed pipeline is associated' do
before do
pipeline.statuses << create(:commit_status, status: 'failed', project: project)
allow(subject).to receive(:pipeline) { pipeline }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
end
context 'when no pipeline is associated' do
before do
allow(subject).to receive(:pipeline) { nil }
end
it { expect(subject.mergeable_ci_state?).to be_truthy }
end
end
end
end
......@@ -194,7 +194,7 @@ describe BambooService, models: true do
def service(bamboo_url: 'http://gitlab.com')
described_class.create(
project: build_stubbed(:empty_project),
project: create(:empty_project),
properties: {
bamboo_url: bamboo_url,
username: 'mic',
......
......@@ -182,7 +182,7 @@ describe TeamcityService, models: true do
def service(teamcity_url: 'http://gitlab.com')
described_class.create(
project: build_stubbed(:empty_project),
project: create(:empty_project),
properties: {
teamcity_url: teamcity_url,
username: 'mic',
......
......@@ -258,6 +258,69 @@ describe Project, models: true do
end
end
describe :external_issue_tracker do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
context 'on existing projects with no value for has_external_issue_tracker' do
before(:each) do
project.update_column(:has_external_issue_tracker, nil)
ext_project.update_column(:has_external_issue_tracker, nil)
end
it 'updates the has_external_issue_tracker boolean' do
expect do
project.external_issue_tracker
end.to change { project.reload.has_external_issue_tracker }.to(false)
expect do
ext_project.external_issue_tracker
end.to change { ext_project.reload.has_external_issue_tracker }.to(true)
end
end
it 'returns nil and does not query services when there is no external issue tracker' do
project.build_missing_services
project.reload
expect(project).not_to receive(:services)
expect(project.external_issue_tracker).to eq(nil)
end
it 'retrieves external_issue_tracker querying services and cache it when there is external issue tracker' do
ext_project.reload # Factory returns a project with changed attributes
ext_project.build_missing_services
ext_project.reload
expect(ext_project).to receive(:services).once.and_call_original
2.times { expect(ext_project.external_issue_tracker).to be_a_kind_of(RedmineService) }
end
end
describe :cache_has_external_issue_tracker do
let(:project) { create(:project) }
it 'stores true if there is any external_issue_tracker' do
services = double(:service, external_issue_trackers: [RedmineService.new])
expect(project).to receive(:services).and_return(services)
expect do
project.cache_has_external_issue_tracker
end.to change { project.has_external_issue_tracker}.to(true)
end
it 'stores false if there is no external_issue_tracker' do
services = double(:service, external_issue_trackers: [])
expect(project).to receive(:services).and_return(services)
expect do
project.cache_has_external_issue_tracker
end.to change { project.has_external_issue_tracker}.to(false)
end
end
describe :can_have_issues_tracker_id? do
let(:project) { create(:project) }
let(:ext_project) { create(:redmine_project) }
......@@ -859,4 +922,37 @@ describe Project, models: true do
it { is_expected.to be_falsey }
end
end
describe '.where_paths_in' do
context 'without any paths' do
it 'returns an empty relation' do
expect(Project.where_paths_in([])).to eq([])
end
end
context 'without any valid paths' do
it 'returns an empty relation' do
expect(Project.where_paths_in(%w[foo])).to eq([])
end
end
context 'with valid paths' do
let!(:project1) { create(:project) }
let!(:project2) { create(:project) }
it 'returns the projects matching the paths' do
projects = Project.where_paths_in([project1.path_with_namespace,
project2.path_with_namespace])
expect(projects).to contain_exactly(project1, project2)
end
it 'returns projects regardless of the casing of paths' do
projects = Project.where_paths_in([project1.path_with_namespace.upcase,
project2.path_with_namespace.upcase])
expect(projects).to contain_exactly(project1, project2)
end
end
end
end
......@@ -204,4 +204,37 @@ describe Service, models: true do
expect(service.bamboo_url_was).to be_nil
end
end
describe "callbacks" do
let(:project) { create(:project) }
let!(:service) do
RedmineService.new(
project: project,
active: true,
properties: {
project_url: 'http://redmine/projects/project_name_in_redmine',
issues_url: "http://redmine/#{project.id}/project_name_in_redmine/:id",
new_issue_url: 'http://redmine/projects/project_name_in_redmine/issues/new'
}
)
end
describe "on create" do
it "updates the has_external_issue_tracker boolean" do
expect do
service.save!
end.to change { service.project.has_external_issue_tracker }.from(nil).to(true)
end
end
describe "on update" do
it "updates the has_external_issue_tracker boolean" do
service.save!
expect do
service.update_attributes(active: false)
end.to change { service.project.has_external_issue_tracker }.from(true).to(false)
end
end
end
end
......@@ -419,6 +419,15 @@ describe API::API, api: true do
expect(json_response['message']).to eq('405 Method Not Allowed')
end
it 'returns 405 if the build failed for a merge request that requires success' do
allow_any_instance_of(MergeRequest).to receive(:mergeable_ci_state?).and_return(false)
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
expect(response.status).to eq(405)
expect(json_response['message']).to eq('405 Method Not Allowed')
end
it "should return 401 if user has no permissions to merge" do
user2 = create(:user)
project.team << [user2, :reporter]
......@@ -554,6 +563,21 @@ describe API::API, api: true do
expect(json_response).to be_an Array
expect(json_response.length).to eq(0)
end
it 'handles external issues' do
jira_project = create(:jira_project, :public, name: 'JIR_EXT1')
issue = ExternalIssue.new("#{jira_project.name}-123", jira_project)
merge_request = create(:merge_request, :simple, author: user, assignee: user, source_project: jira_project)
merge_request.update_attribute(:description, "Closes #{issue.to_reference(jira_project)}")
get api("/projects/#{jira_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(1)
expect(json_response.first['title']).to eq(issue.title)
expect(json_response.first['id']).to eq(issue.id)
end
end
describe 'POST :id/merge_requests/:merge_request_id/subscription' do
......
......@@ -85,7 +85,7 @@ describe Ci::API::API do
trigger = FactoryGirl.create(:ci_trigger, project: project)
pipeline = FactoryGirl.create(:ci_pipeline, project: project, ref: 'master')
trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: pipeline, trigger: trigger)
trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger)
pipeline.create_builds(nil, trigger_request)
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment