Commit 57b19769 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'master' into auto-pipelines-vue

* master: (73 commits)
  Refactor JiraService by moving code out of JiraService#execute method
  Rename a label to fix an intermittently-failing spec
  Refactor the Git submodules with CI docs
  Add CHANGELOG entry
  Improve Gitlab::GitAccessWiki spec with download access checks
  Improve ProjectPolicy spec to check permissions when wiki is disabled
  Allow access to the wiki with git when repository feature disabled
  Refactor branch chooser in issuable form
  Improve the `Gitlab::OAuth::User` error message
  Disable the ee_compat_check task on dev
  Make the downtime_check task happy
  Revert bump in rufus-scheduler
  Fix comma-dangle in function's arguments errors
  Improvements after review
  Use created date from last_deployment
  API: Expose branch status
  Grapify the files API
  Move task helpers to a module
  Fixed GFM autocomplete regex
  Add Human Readable Timestamp to backup tar file
  ...
parents 6eb37284 7c66ea94
/coverage/
/coverage-javascript/ /coverage-javascript/
/public/ /public/
/tmp/ /tmp/
......
{ {
"env": { "env": {
"jquery": true,
"browser": true, "browser": true,
"es6": true "es6": true
}, },
"extends": "airbnb", "extends": "airbnb-base",
"globals": { "globals": {
"$": false,
"_": false, "_": false,
"gl": false, "gl": false,
"gon": false, "gon": false
"jQuery": false
}, },
"plugins": [ "plugins": [
"filenames" "filenames"
......
...@@ -229,7 +229,6 @@ rake ee_compat_check: ...@@ -229,7 +229,6 @@ rake ee_compat_check:
<<: *exec <<: *exec
only: only:
- branches@gitlab-org/gitlab-ce - branches@gitlab-org/gitlab-ce
- branches@gitlab/gitlabhq
except: except:
- master - master
- tags - tags
......
...@@ -133,7 +133,7 @@ gem 'acts-as-taggable-on', '~> 4.0' ...@@ -133,7 +133,7 @@ gem 'acts-as-taggable-on', '~> 4.0'
# Background jobs # Background jobs
gem 'sidekiq', '~> 4.2' gem 'sidekiq', '~> 4.2'
gem 'sidekiq-cron', '~> 0.4.0' gem 'sidekiq-cron', '~> 0.4.4'
gem 'redis-namespace', '~> 1.5.2' gem 'redis-namespace', '~> 1.5.2'
gem 'sidekiq-limit_fetch', '~> 3.4' gem 'sidekiq-limit_fetch', '~> 3.4'
...@@ -309,6 +309,8 @@ group :development, :test do ...@@ -309,6 +309,8 @@ group :development, :test do
gem 'knapsack', '~> 1.11.0' gem 'knapsack', '~> 1.11.0'
gem 'activerecord_sane_schema_dumper', '0.2' gem 'activerecord_sane_schema_dumper', '0.2'
gem 'stackprof', '~> 0.2.10'
end end
group :test do group :test do
......
...@@ -650,10 +650,10 @@ GEM ...@@ -650,10 +650,10 @@ GEM
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (~> 1.5) rack-protection (~> 1.5)
redis (~> 3.2, >= 3.2.1) redis (~> 3.2, >= 3.2.1)
sidekiq-cron (0.4.0) sidekiq-cron (0.4.4)
redis-namespace (>= 1.5.2) redis-namespace (>= 1.5.2)
rufus-scheduler (>= 2.0.24) rufus-scheduler (>= 2.0.24)
sidekiq (>= 4.0.0) sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0) sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4) sidekiq (>= 4)
simplecov (0.12.0) simplecov (0.12.0)
...@@ -691,6 +691,7 @@ GEM ...@@ -691,6 +691,7 @@ GEM
actionpack (>= 4.0) actionpack (>= 4.0)
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
stackprof (0.2.10)
state_machines (0.4.0) state_machines (0.4.0)
state_machines-activemodel (0.4.0) state_machines-activemodel (0.4.0)
activemodel (>= 4.1, < 5.1) activemodel (>= 4.1, < 5.1)
...@@ -925,7 +926,7 @@ DEPENDENCIES ...@@ -925,7 +926,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6) sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0) shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2) sidekiq (~> 4.2)
sidekiq-cron (~> 0.4.0) sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4) sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0) simplecov (= 0.12.0)
slack-notifier (~> 1.2.0) slack-notifier (~> 1.2.0)
...@@ -937,6 +938,7 @@ DEPENDENCIES ...@@ -937,6 +938,7 @@ DEPENDENCIES
spring-commands-teaspoon (~> 0.0.2) spring-commands-teaspoon (~> 0.0.2)
sprockets (~> 3.7.0) sprockets (~> 3.7.0)
sprockets-es6 (~> 0.9.2) sprockets-es6 (~> 0.9.2)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0) state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6) sys-filesystem (~> 1.1.6)
teaspoon (~> 1.1.0) teaspoon (~> 1.1.0)
......
# GitLab # GitLab
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master) [![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](http://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) [![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42) [![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
......
...@@ -10,10 +10,15 @@ ...@@ -10,10 +10,15 @@
}, },
template: ` template: `
<span class="total-time"> <span class="total-time">
<template v-if="Object.keys(time).length">
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template> <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template> <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template> <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template> <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
</template>
<template v-else>
--
</template>
</span> </span>
`, `,
}); });
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.environmentsList = window.gl.environmentsList || {}; window.gl.environmentsList = window.gl.environmentsList || {};
window.gl.environmentsList.timeagoInstance = new timeago(); // eslint-disable-line
gl.environmentsList.EnvironmentItem = Vue.component('environment-item', { gl.environmentsList.EnvironmentItem = Vue.component('environment-item', {
...@@ -147,15 +148,26 @@ ...@@ -147,15 +148,26 @@
this.model.last_deployment.deployable; this.model.last_deployment.deployable;
}, },
/**
* Verifies if the date to be shown is present.
*
* @returns {Boolean|Undefined}
*/
canShowDate() {
return this.model.last_deployment &&
this.model.last_deployment.deployable &&
this.model.last_deployment.deployable !== undefined;
},
/** /**
* Human readable date. * Human readable date.
* *
* @returns {String} * @returns {String}
*/ */
createdDate() { createdDate() {
const timeagoInstance = new timeago(); // eslint-disable-line return window.gl.environmentsList.timeagoInstance.format(
this.model.last_deployment.deployable.created_at,
return timeagoInstance.format(this.model.created_at); );
}, },
/** /**
...@@ -453,7 +465,7 @@ ...@@ -453,7 +465,7 @@
<td> <td>
<span <span
v-if="!isFolder && model.last_deployment" v-if="!isFolder && canShowDate"
class="environment-created-date-timeago"> class="environment-created-date-timeago">
{{createdDate}} {{createdDate}}
</span> </span>
......
...@@ -59,12 +59,13 @@ ...@@ -59,12 +59,13 @@
// Tweaked to commands to start without a space only if char before is a non-word character // Tweaked to commands to start without a space only if char before is a non-word character
// https://github.com/ichord/At.js // https://github.com/ichord/At.js
var _a, _y, regexp, match; var _a, _y, regexp, match;
subtext = subtext.split(' ').pop();
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
_a = decodeURI("%C3%80"); _a = decodeURI("%C3%80");
_y = decodeURI("%C3%BF"); _y = decodeURI("%C3%BF");
regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)|([^\\x00-\\xff]*)$", 'gi'); regexp = new RegExp("(?:\\B|\\W|\\s)" + flag + "(?!\\W)([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)|([^\\x00-\\xff]*)$", 'gi');
match = regexp.exec(subtext); match = regexp.exec(subtext);
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */ /* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
(function() { (function() {
this.LabelsSelect = (function() { this.LabelsSelect = (function() {
function LabelsSelect() { function LabelsSelect() {
......
/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */ /* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/*= require autosave */ /*= require autosave */
/*= require autosize */ /*= require autosize */
......
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
border-radius: 0; border-radius: 0;
border: none; border: none;
height: auto; height: auto;
width: 100%;
margin: 0; margin: 0;
align-self: center; align-self: center;
} }
......
...@@ -56,16 +56,9 @@ ...@@ -56,16 +56,9 @@
.md-header { .md-header {
.nav-links { .nav-links {
.active {
a {
border-bottom-color: #000;
}
}
a { a {
padding-top: 0; padding-top: 0;
line-height: 19px; line-height: 19px;
border-bottom: 1px solid $border-color;
&.btn.btn-xs { &.btn.btn-xs {
padding: 2px 5px; padding: 2px 5px;
......
...@@ -94,6 +94,7 @@ ...@@ -94,6 +94,7 @@
&.active a { &.active a {
border-bottom: none; border-bottom: none;
color: $link-underline-blue;
} }
a { a {
...@@ -103,7 +104,6 @@ ...@@ -103,7 +104,6 @@
&:hover, &:hover,
&:active, &:active,
&:focus { &:focus {
color: $black;
border-bottom: none; border-bottom: none;
} }
} }
......
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
display: none; display: none;
} }
.btn-clipboard { .btn-clipboard:hover {
color: $gl-gray; color: $gl-gray;
} }
} }
...@@ -235,6 +235,10 @@ ...@@ -235,6 +235,10 @@
padding-bottom: 10px; padding-bottom: 10px;
color: #999; color: #999;
&:hover {
color: $gl-gray;
}
span { span {
display: block; display: block;
margin-top: 0; margin-top: 0;
...@@ -244,15 +248,17 @@ ...@@ -244,15 +248,17 @@
display: none; display: none;
} }
.avatar:hover {
border-color: #999;
}
.btn-clipboard { .btn-clipboard {
border: none; border: none;
color: #999;
&:hover { &:hover {
background: transparent; background: transparent;
} color: $gl-gray;
i {
color: #999;
} }
} }
} }
......
...@@ -90,14 +90,14 @@ ul.notes { ...@@ -90,14 +90,14 @@ ul.notes {
} }
&.system-note-commit-list { &.system-note-commit-list {
max-height: 63px; max-height: 70px;
overflow: hidden; overflow: hidden;
display: block; display: block;
ul { ul {
margin: 3px 0 3px 15px !important; margin: 3px 0 3px 16px !important;
li { .gfm-commit {
font-family: $monospace_font; font-family: $monospace_font;
font-size: 12px; font-size: 12px;
} }
...@@ -172,6 +172,10 @@ ul.notes { ...@@ -172,6 +172,10 @@ ul.notes {
&.timeline-entry { &.timeline-entry {
padding: 14px 10px; padding: 14px 10px;
} }
.system-note {
padding: 0;
}
} }
&.is-editting { &.is-editting {
......
...@@ -11,7 +11,7 @@ class AutocompleteController < ApplicationController ...@@ -11,7 +11,7 @@ class AutocompleteController < ApplicationController
@users = @users.reorder(:name) @users = @users.reorder(:name)
@users = @users.page(params[:page]) @users = @users.page(params[:page])
if params[:todo_filter].present? if params[:todo_filter].present? && current_user
@users = @users.todo_authors(current_user.id, params[:todo_state_filter]) @users = @users.todo_authors(current_user.id, params[:todo_state_filter])
end end
......
...@@ -325,16 +325,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -325,16 +325,16 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present? if params[:merge_when_build_succeeds].present?
unless @merge_request.pipeline unless @merge_request.head_pipeline
@status = :failed @status = :failed
return return
end end
if @merge_request.pipeline.active? if @merge_request.head_pipeline.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request) .execute(@merge_request)
@status = :merge_when_build_succeeds @status = :merge_when_build_succeeds
elsif @merge_request.pipeline.success? elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while # This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time # the tests finish at about the same time
MergeWorker.perform_async(@merge_request.id, current_user.id, params) MergeWorker.perform_async(@merge_request.id, current_user.id, params)
...@@ -398,7 +398,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -398,7 +398,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def ci_status def ci_status
pipeline = @merge_request.pipeline pipeline = @merge_request.head_pipeline
if pipeline if pipeline
status = pipeline.status status = pipeline.status
coverage = pipeline.try(:coverage) coverage = pipeline.try(:coverage)
...@@ -534,7 +535,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -534,7 +535,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def define_widget_vars def define_widget_vars
@pipeline = @merge_request.pipeline @pipeline = @merge_request.head_pipeline
end end
def define_commit_vars def define_commit_vars
...@@ -563,7 +564,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -563,7 +564,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_pipelines_vars def define_pipelines_vars
@pipelines = @merge_request.all_pipelines @pipelines = @merge_request.all_pipelines
@pipeline = @merge_request.pipeline @pipeline = @merge_request.head_pipeline
@statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0 @statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0
end end
...@@ -631,7 +632,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -631,7 +632,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_when_build_succeeds_active? def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? && params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active? @merge_request.head_pipeline && @merge_request.head_pipeline.active?
end end
def build_merge_request def build_merge_request
......
...@@ -338,7 +338,7 @@ module Ci ...@@ -338,7 +338,7 @@ module Ci
def merge_requests def merge_requests
@merge_requests ||= project.merge_requests @merge_requests ||= project.merge_requests
.where(source_branch: self.ref) .where(source_branch: self.ref)
.select { |merge_request| merge_request.pipeline.try(:id) == self.id } .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end end
private private
......
...@@ -2,6 +2,9 @@ module ProtectedBranchAccess ...@@ -2,6 +2,9 @@ module ProtectedBranchAccess
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
belongs_to :protected_branch
delegate :project, to: :protected_branch
scope :master, -> { where(access_level: Gitlab::Access::MASTER) } scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) } scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
end end
...@@ -9,4 +12,10 @@ module ProtectedBranchAccess ...@@ -9,4 +12,10 @@ module ProtectedBranchAccess
def humanize def humanize
self.class.human_access_levels[self.access_level] self.class.human_access_levels[self.access_level]
end end
def check_access(user)
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level
end
end end
...@@ -678,7 +678,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -678,7 +678,7 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state? def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds? return true unless project.only_allow_merge_if_build_succeeds?
!pipeline || pipeline.success? || pipeline.skipped? !head_pipeline || head_pipeline.success? || head_pipeline.skipped?
end end
def environments def environments
...@@ -774,10 +774,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -774,10 +774,10 @@ class MergeRequest < ActiveRecord::Base
commits.map(&:sha) commits.map(&:sha)
end end
def pipeline def head_pipeline
return unless diff_head_sha && source_project return unless diff_head_sha && source_project
@pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha) @head_pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
end end
def all_pipelines def all_pipelines
......
...@@ -9,6 +9,9 @@ class JiraService < IssueTrackerService ...@@ -9,6 +9,9 @@ class JiraService < IssueTrackerService
before_update :reset_password before_update :reset_password
# This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service
# configuration screen.
def supported_events def supported_events
%w(commit merge_request) %w(commit merge_request)
end end
...@@ -105,18 +108,29 @@ class JiraService < IssueTrackerService ...@@ -105,18 +108,29 @@ class JiraService < IssueTrackerService
"#{url}/secure/CreateIssue.jspa" "#{url}/secure/CreateIssue.jspa"
end end
def execute(push, issue = nil) def execute(push)
if issue.nil? # This method is a no-op, because currently JiraService does not
# No specific issue, that means # support any events.
# we just want to test settings end
test_settings
else
jira_issue = jira_request { client.Issue.find(issue.iid) }
return false unless jira_issue.present? def close_issue(entity, external_issue)
issue = jira_request { client.Issue.find(external_issue.iid) }
close_issue(push, jira_issue) return if issue.nil? || issue.resolution.present? || !jira_issue_transition_id.present?
commit_id = if entity.is_a?(Commit)
entity.id
elsif entity.is_a?(MergeRequest)
entity.diff_head_sha
end end
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
# may or may not be allowed. Refresh the issue after transition and check
# if it is closed, so we don't have one comment for every commit.
issue = jira_request { client.Issue.find(issue.key) } if transition_issue(issue)
add_issue_solved_comment(issue, commit_id, commit_url) if issue.resolution
end end
def create_cross_reference_note(mentioned, noteable, author) def create_cross_reference_note(mentioned, noteable, author)
...@@ -156,6 +170,11 @@ class JiraService < IssueTrackerService ...@@ -156,6 +170,11 @@ class JiraService < IssueTrackerService
"Please fill in Password and Username." "Please fill in Password and Username."
end end
def test(_)
result = test_settings
{ success: result.present?, result: result }
end
def can_test? def can_test?
username.present? && password.present? username.present? && password.present?
end end
...@@ -182,24 +201,6 @@ class JiraService < IssueTrackerService ...@@ -182,24 +201,6 @@ class JiraService < IssueTrackerService
end end
end end
def close_issue(entity, issue)
return if issue.nil? || issue.resolution.present? || !jira_issue_transition_id.present?
commit_id = if entity.is_a?(Commit)
entity.id
elsif entity.is_a?(MergeRequest)
entity.diff_head_sha
end
commit_url = build_entity_url(:commit, commit_id)
# Depending on the JIRA project's workflow, a comment during transition
# may or may not be allowed. Refresh the issue after transition and check
# if it is closed, so we don't have one comment for every commit.
issue = jira_request { client.Issue.find(issue.key) } if transition_issue(issue)
add_issue_solved_comment(issue, commit_id, commit_url) if issue.resolution
end
def transition_issue(issue) def transition_issue(issue)
issue.transitions.build.save(transition: { id: jira_issue_transition_id }) issue.transitions.build.save(transition: { id: jira_issue_transition_id })
end end
......
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess include ProtectedBranchAccess
belongs_to :protected_branch
delegate :project, to: :protected_branch
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER, validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER] } Gitlab::Access::DEVELOPER] }
...@@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base ...@@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
Gitlab::Access::DEVELOPER => "Developers + Masters" Gitlab::Access::DEVELOPER => "Developers + Masters"
}.with_indifferent_access }.with_indifferent_access
end end
def check_access(user)
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level
end
end end
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess include ProtectedBranchAccess
belongs_to :protected_branch
delegate :project, to: :protected_branch
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER, validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER, Gitlab::Access::DEVELOPER,
Gitlab::Access::NO_ACCESS] } Gitlab::Access::NO_ACCESS] }
...@@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base ...@@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
def check_access(user) def check_access(user)
return false if access_level == Gitlab::Access::NO_ACCESS return false if access_level == Gitlab::Access::NO_ACCESS
return true if user.is_admin?
project.team.max_member_access(user.id) >= access_level super
end end
end end
...@@ -50,6 +50,7 @@ class ProjectPolicy < BasePolicy ...@@ -50,6 +50,7 @@ class ProjectPolicy < BasePolicy
def reporter_access! def reporter_access!
can! :download_code can! :download_code
can! :download_wiki_code
can! :fork_project can! :fork_project
can! :create_project_snippet can! :create_project_snippet
can! :update_issue can! :update_issue
...@@ -187,6 +188,7 @@ class ProjectPolicy < BasePolicy ...@@ -187,6 +188,7 @@ class ProjectPolicy < BasePolicy
unless project.feature_available?(:wiki, user) || project.has_external_wiki? unless project.feature_available?(:wiki, user) || project.has_external_wiki?
cannot!(*named_abilities(:wiki)) cannot!(*named_abilities(:wiki))
cannot!(:download_wiki_code)
end end
unless project.feature_available?(:builds, user) && repository_enabled unless project.feature_available?(:builds, user) && repository_enabled
...@@ -226,6 +228,7 @@ class ProjectPolicy < BasePolicy ...@@ -226,6 +228,7 @@ class ProjectPolicy < BasePolicy
can! :read_commit_status can! :read_commit_status
can! :read_container_image can! :read_container_image
can! :download_code can! :download_code
can! :download_wiki_code
can! :read_cycle_analytics can! :read_cycle_analytics
# NOTE: may be overridden by IssuePolicy # NOTE: may be overridden by IssuePolicy
......
...@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity ...@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity
end end
expose :duration, as: :total_time do |build| expose :duration, as: :total_time do |build|
distance_of_time_as_hash(build.duration.to_f) build.duration ? distance_of_time_as_hash(build.duration.to_f) : {}
end end
expose :branch do expose :branch do
......
...@@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity ...@@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity
path_to(:play_namespace_project_build, build) path_to(:play_namespace_project_build, build)
end end
expose :created_at
expose :updated_at
private private
def path_to(route, build) def path_to(route, build)
......
...@@ -2,6 +2,8 @@ module EntityDateHelper ...@@ -2,6 +2,8 @@ module EntityDateHelper
include ActionView::Helpers::DateHelper include ActionView::Helpers::DateHelper
def interval_in_words(diff) def interval_in_words(diff)
return 'Not started' unless diff
"#{distance_of_time_in_words(Time.now, diff)} ago" "#{distance_of_time_in_words(Time.now, diff)} ago"
end end
......
...@@ -45,9 +45,15 @@ module Ci ...@@ -45,9 +45,15 @@ module Ci
return error('No builds for this pipeline.') return error('No builds for this pipeline.')
end end
Ci::Pipeline.transaction do
pipeline.save pipeline.save
pipeline.process!
pipeline Ci::CreatePipelineBuildsService
.new(project, current_user)
.execute(pipeline)
end
pipeline.tap(&:process!)
end end
private private
......
...@@ -5,10 +5,7 @@ module Ci ...@@ -5,10 +5,7 @@ module Ci
def execute(pipeline) def execute(pipeline)
@pipeline = pipeline @pipeline = pipeline
# This method will ensure that our pipeline does have all builds for all stages created ensure_created_builds! # TODO, remove me in 9.0
if created_builds.empty?
create_builds!
end
new_builds = new_builds =
stage_indexes_of_created_builds.map do |index| stage_indexes_of_created_builds.map do |index|
...@@ -22,10 +19,6 @@ module Ci ...@@ -22,10 +19,6 @@ module Ci
private private
def create_builds!
Ci::CreatePipelineBuildsService.new(project, current_user).execute(pipeline)
end
def process_stage(index) def process_stage(index)
current_status = status_for_prior_stages(index) current_status = status_for_prior_stages(index)
...@@ -76,5 +69,18 @@ module Ci ...@@ -76,5 +69,18 @@ module Ci
def created_builds def created_builds
pipeline.builds.created pipeline.builds.created
end end
# This method is DEPRECATED and should be removed in 9.0.
#
# We need it to maintain backwards compatibility with previous versions
# when builds were not created within one transaction with the pipeline.
#
def ensure_created_builds!
return if created_builds.any?
Ci::CreatePipelineBuildsService
.new(project, current_user)
.execute(pipeline)
end
end end
end end
...@@ -17,7 +17,7 @@ module Issues ...@@ -17,7 +17,7 @@ module Issues
# allowed to close the given issue. # allowed to close the given issue.
def close_issue(issue, commit: nil, notifications: true, system_note: true) def close_issue(issue, commit: nil, notifications: true, system_note: true)
if project.jira_tracker? && project.jira_service.active if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue) project.jira_service.close_issue(commit, issue)
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
return issue return issue
end end
......
...@@ -55,7 +55,7 @@ module MergeRequests ...@@ -55,7 +55,7 @@ module MergeRequests
def pipeline_merge_requests(pipeline) def pipeline_merge_requests(pipeline)
merge_requests_for(pipeline.ref).each do |merge_request| merge_requests_for(pipeline.ref).each do |merge_request|
next unless pipeline == merge_request.pipeline next unless pipeline == merge_request.head_pipeline
yield merge_request yield merge_request
end end
...@@ -63,7 +63,7 @@ module MergeRequests ...@@ -63,7 +63,7 @@ module MergeRequests
def commit_status_merge_requests(commit_status) def commit_status_merge_requests(commit_status)
merge_requests_for(commit_status.ref).each do |merge_request| merge_requests_for(commit_status.ref).each do |merge_request|
pipeline = merge_request.pipeline pipeline = merge_request.head_pipeline
next unless pipeline next unless pipeline
next unless pipeline.sha == commit_status.sha next unless pipeline.sha == commit_status.sha
......
...@@ -4,13 +4,13 @@ ...@@ -4,13 +4,13 @@
%ul.nav-links %ul.nav-links
= nav_link(page: [dashboard_projects_path, root_path]) do = nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
Your Projects Your projects
= nav_link(page: starred_dashboard_projects_path) do = nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do = link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
Starred Projects Starred projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do = nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do = link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
Explore Projects Explore projects
.nav-controls .nav-controls
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
......
...@@ -4,6 +4,18 @@ ...@@ -4,6 +4,18 @@
Welcome to GitLab Welcome to GitLab
%p.blank-state-text %p.blank-state-text
Code, test, and deploy together Code, test, and deploy together
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("group", size: 50)
%h3.blank-state-title
You can create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
.blank-state .blank-state
.blank-state-icon .blank-state-icon
= custom_icon("project", size: 50) = custom_icon("project", size: 50)
...@@ -21,17 +33,6 @@ ...@@ -21,17 +33,6 @@
= link_to new_project_path, class: "btn btn-new" do = link_to new_project_path, class: "btn btn-new" do
New project New project
- if current_user.can_create_group?
.blank-state
.blank-state-icon
= custom_icon("group", size: 50)
%h3.blank-state-title
You can create a group for several dependent projects.
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "btn btn-new" do
New group
-if publicish_project_count > 0 -if publicish_project_count > 0
.blank-state .blank-state
.blank-state-icon .blank-state-icon
......
- page_title "Access Denied" - content_for(:title, 'Access Denied')
%h1 403 %img{:alt => "GitLab Logo",
%h3 Access Denied :src => image_path('logo.svg')}
%hr %h1
%p You are not allowed to access this page. 403
%p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"} .container
%h3 Access Denied
%hr
%p You are not allowed to access this page.
%p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"}
- page_title "Encoding Error" - content_for(:title, 'Encoding Error')
%h1 500 %img{:alt => "GitLab Logo",
%h3 Encoding Error :src => image_path('logo.svg')}
%hr %h1
%p Page can't be loaded because of an encoding error. 500
.container
%h3 Encoding Error
%hr
%p Page can't be loaded because of an encoding error.
- page_title "Git Resource Not Found" - content_for(:title, 'Git Resource Not Found')
%h1 404 %img{:alt => "GitLab Logo",
%h3 Git Resource Not found :src => image_path('logo.svg')}
%hr %h1
%p 404
Application can't get access to some branch or commit in your repository. It .container
may have been moved. %h3 Git Resource Not found
%hr
%p Application can't get access to some branch or commit in your repository. It
may have been moved
- page_title "Not Found" - content_for(:title, 'Not Found')
%h1 404 %img{:alt => "GitLab Logo",
%h3 The resource you were looking for doesn't exist. :src => image_path('logo.svg')}
%hr %h1
%p You may have mistyped the address or the page may have moved. 404
.container
%h3 The resource you were looking for doesn't exist.
%hr
%p You may have mistyped the address or the page may have moved.
- page_title "Auth Error" - content_for(:title, 'Auth Error')
%h1 422 %img{:alt => "GitLab Logo",
%h3 Sign-in using #{@provider} auth failed :src => image_path('logo.svg')}
%hr %h1
%p Sign-in failed because #{@error}. 422
%p There are couple of steps you can take: .container
%h3 Sign-in using #{@provider} auth failed
%hr
%p Sign-in failed because #{@error}.
%p There are couple of steps you can take:
%ul %ul
%li Try logging in using your email %li Try logging in using your email
......
...@@ -20,11 +20,18 @@ ...@@ -20,11 +20,18 @@
= link_to group.name, group_url(group) = link_to group.name, group_url(group)
as #{@member.human_access}. as #{@member.human_access}.
- if @member.source.users.include?(current_user) - is_member = @member.source.users.include?(current_user)
- if is_member
%p %p
However, you are already a member of this #{@member.source.is_a?(Group) ? "group" : "project"}. However, you are already a member of this #{@member.source.is_a?(Group) ? "group" : "project"}.
Sign in using a different account to accept the invitation. Sign in using a different account to accept the invitation.
- else
- if @member.invite_email != current_user.email
%p
Note that this invitation was sent to #{mail_to @member.invite_email}, but you are signed in as #{link_to current_user.to_reference, user_url(current_user)} with email #{mail_to current_user.email}.
- unless is_member
.actions .actions
= link_to "Accept invitation", accept_invite_url(@token), method: :post, class: "btn btn-success" = link_to "Accept invitation", accept_invite_url(@token), method: :post, class: "btn btn-success"
= link_to "Decline", decline_invite_url(@token), method: :post, class: "btn btn-danger prepend-left-10" = link_to "Decline", decline_invite_url(@token), method: :post, class: "btn btn-danger prepend-left-10"
!!! 5 !!! 5
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head" %head
%body{class: "#{user_application_theme} application navless"} %meta{:content => "width=device-width, initial-scale=1, maximum-scale=1", :name => "viewport"}
= Gon::Base.render_data %title= yield(:title)
= render "layouts/header/empty" :css
.container.navless-container body {
= render "layouts/flash" color: #666;
.error-page text-align: center;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
font-size: 14px;
}
h1 {
font-size: 56px;
line-height: 100px;
font-weight: normal;
color: #456;
}
h2 {
font-size: 24px;
color: #666;
line-height: 1.5em;
}
h3 {
color: #456;
font-size: 20px;
font-weight: normal;
line-height: 28px;
}
hr {
max-width: 800px;
margin: 18px auto;
border: 0;
border-top: 1px solid #EEE;
border-bottom: 1px solid white;
}
img {
max-width: 40vw;
display: block;
margin: 40px auto;
}
.container {
margin: auto 20px;
}
ul {
margin: auto;
text-align: left;
display:inline-block;
}
%body
= yield = yield
...@@ -66,8 +66,6 @@ ...@@ -66,8 +66,6 @@
%td %td
- if build.project - if build.project
= link_to build.project.name_with_namespace, admin_namespace_project_path(build.project.namespace, build.project) = link_to build.project.name_with_namespace, admin_namespace_project_path(build.project.namespace, build.project)
- if admin
%td %td
- if build.try(:runner) - if build.try(:runner)
= runner_link(build.runner) = runner_link(build.runner)
...@@ -93,8 +91,7 @@ ...@@ -93,8 +91,7 @@
%span #{time_ago_with_tooltip(build.finished_at)} %span #{time_ago_with_tooltip(build.finished_at)}
%td.coverage %td.coverage
- if coverage - if coverage && build.try(:coverage)
- if build.try(:coverage)
#{build.coverage}% #{build.coverage}%
%td %td
......
%tr.generic_commit_status - admin = local_assigns.fetch(:admin, false)
- ref = local_assigns.fetch(:ref, nil)
- commit_sha = local_assigns.fetch(:commit_sha, nil)
- retried = local_assigns.fetch(:retried, false)
- pipeline_link = local_assigns.fetch(:pipeline_link, false)
- stage = local_assigns.fetch(:stage, false)
- coverage = local_assigns.fetch(:coverage, false)
%tr.generic_commit_status{class: ('retried' if retried)}
%td.status %td.status
- if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url - if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url
= ci_status_with_icon(generic_commit_status.status, generic_commit_status.target_url) = ci_status_with_icon(generic_commit_status.status, generic_commit_status.target_url)
...@@ -8,14 +16,35 @@ ...@@ -8,14 +16,35 @@
%td.generic_commit_status-link %td.generic_commit_status-link
- if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url - if can?(current_user, :read_commit_status, generic_commit_status) && generic_commit_status.target_url
= link_to generic_commit_status.target_url do = link_to generic_commit_status.target_url do
%strong ##{generic_commit_status.id} %span.build-link ##{generic_commit_status.id}
- else - else
%strong ##{generic_commit_status.id} %span.build-link ##{generic_commit_status.id}
- if ref
- if generic_commit_status.ref
.icon-container
= generic_commit_status.tags.any? ? icon('tag') : icon('code-fork')
= link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref)
- else
.light none
.icon-container.commit-icon
= custom_icon("icon_commit")
- if commit_sha
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "commit-id monospace"
- if defined?(retried) && retried - if retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Status was retried.') = icon('warning', class: 'text-warning has-tooltip', title: 'Status was retried.')
- if defined?(pipeline_link) && pipeline_link .label-container
- if generic_commit_status.tags.any?
- generic_commit_status.tags.each do |tag|
%span.label.label-primary
= tag
- if retried
%span.label.label-warning retried
- if pipeline_link
%td %td
= link_to pipeline_path(generic_commit_status.pipeline) do = link_to pipeline_path(generic_commit_status.pipeline) do
%span.pipeline-id ##{generic_commit_status.pipeline.id} %span.pipeline-id ##{generic_commit_status.pipeline.id}
...@@ -25,25 +54,17 @@ ...@@ -25,25 +54,17 @@
- else - else
%span.monospace API %span.monospace API
- if defined?(commit_sha) && commit_sha - if admin
%td
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "monospace"
- if defined?(ref) && ref
%td %td
- if generic_commit_status.ref - if generic_commit_status.project
= link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref) = link_to generic_commit_status.project.name_with_namespace, admin_namespace_project_path(generic_commit_status.project.namespace, generic_commit_status.project)
- else
.light none
- if defined?(runner) && runner
%td %td
- if generic_commit_status.try(:runner) - if generic_commit_status.try(:runner)
= runner_link(generic_commit_status.runner) = runner_link(generic_commit_status.runner)
- else - else
.light none .light none
- if defined?(stage) && stage - if stage
%td %td
= generic_commit_status.stage = generic_commit_status.stage
...@@ -51,24 +72,19 @@ ...@@ -51,24 +72,19 @@
= generic_commit_status.name = generic_commit_status.name
%td %td
- if generic_commit_status.tags.any?
- generic_commit_status.tags.each do |tag|
%span.label.label-primary
= tag
- if defined?(retried) && retried
%span.label.label-warning retried
%td.duration
- if generic_commit_status.duration - if generic_commit_status.duration
= icon("clock-o") %p.duration
= time_interval_in_words(generic_commit_status.duration) = custom_icon("icon_timer")
= duration_in_numbers(generic_commit_status.duration)
%td.timestamp
- if generic_commit_status.finished_at - if generic_commit_status.finished_at
%p.finished-at
= icon("calendar") = icon("calendar")
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)} %span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
- if defined?(coverage) && coverage
%td.coverage %td.coverage
- if generic_commit_status.try(:coverage) - if coverage && generic_commit_status.try(:coverage)
#{generic_commit_status.coverage}% #{generic_commit_status.coverage}%
%td
-# empty column to match number of columns in ci/builds/_build.html.haml
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
%h2.merge-requests-title %h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request') = pluralize(@merge_requests.count, 'Related Merge Request')
%ul.unstyled-list.related-merge-requests %ul.unstyled-list.related-merge-requests
- has_any_ci = @merge_requests.any?(&:pipeline) - has_any_ci = @merge_requests.any?(&:head_pipeline)
- @merge_requests.each do |merge_request| - @merge_requests.each do |merge_request|
%li %li
%span.merge-request-ci-status %span.merge-request-ci-status
- if merge_request.pipeline - if merge_request.head_pipeline
= render_pipeline_status(merge_request.pipeline) = render_pipeline_status(merge_request.head_pipeline)
- elsif has_any_ci - elsif has_any_ci
= icon('blank fw') = icon('blank fw')
%span.merge-request-id %span.merge-request-id
......
...@@ -15,9 +15,9 @@ ...@@ -15,9 +15,9 @@
= icon('ban') = icon('ban')
CLOSED CLOSED
- if merge_request.pipeline - if merge_request.head_pipeline
%li %li
= render_pipeline_status(merge_request.pipeline) = render_pipeline_status(merge_request.head_pipeline)
- if merge_request.open? && merge_request.broken? - if merge_request.open? && merge_request.broken?
%li %li
......
- if @pipeline - if @pipeline
.mr-widget-heading .mr-widget-heading
- %w[success success_with_warnings skipped canceled failed running pending].each do |status| - %w[success success_with_warnings skipped canceled failed running pending].each do |status|
.ci_widget{ class: "ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) } .ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) }
= ci_icon_for_status(status) = ci_icon_for_status(status)
%span %span
Pipeline Pipeline
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
ci_environments_status_url: "#{ci_environments_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", ci_environments_status_url: "#{ci_environments_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
gitlab_icon: "#{asset_path 'gitlab_logo.png'}", gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
ci_status: "#{@merge_request.pipeline ? @merge_request.pipeline.status : ''}", ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
ci_message: { ci_message: {
normal: "Build {{status}} for \"{{title}}\"", normal: "Build {{status}} for \"{{title}}\"",
preparing: "{{status}} build for \"{{title}}\"" preparing: "{{status}} build for \"{{title}}\""
......
...@@ -40,30 +40,7 @@ ...@@ -40,30 +40,7 @@
title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' } title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' }
= icon('question-circle') = icon('question-circle')
- if issuable.is_a?(MergeRequest) && !issuable.closed_without_fork? = render 'shared/issuable/form/branch_chooser', issuable: issuable, form: form
%hr
- if @merge_request.new_record?
.form-group
= form.label :source_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.form-group
= form.label :target_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
- if @merge_request.new_record?
&nbsp;
= link_to 'Change branches', mr_change_branches_path(@merge_request)
- if @merge_request.can_remove_source_branch?(current_user)
.form-group
.col-sm-10.col-sm-offset-2
.checkbox
= label_tag 'merge_request[force_remove_source_branch]' do
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', @merge_request.force_remove_source_branch?
Remove source branch when merge request is accepted.
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) - is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
.row-content-block{class: (is_footer ? "footer-block" : "middle-block")} .row-content-block{class: (is_footer ? "footer-block" : "middle-block")}
......
- issuable = local_assigns.fetch(:issuable)
- form = local_assigns.fetch(:form)
- return unless issuable.is_a?(MergeRequest)
- return if issuable.closed_without_fork?
%hr
- if issuable.new_record?
.form-group
= form.label :source_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:source_branch, [issuable.source_branch], {}, { class: 'source_branch select2 span2', disabled: true })
.form-group
= form.label :target_branch, class: 'control-label'
.col-sm-10
.issuable-form-select-holder
= form.select(:target_branch, issuable.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: issuable.new_record?, data: { placeholder: "Select branch" }})
- if issuable.new_record?
&nbsp;
= link_to 'Change branches', mr_change_branches_path(issuable)
- if issuable.can_remove_source_branch?(current_user)
.form-group
.col-sm-10.col-sm-offset-2
.checkbox
= label_tag 'merge_request[force_remove_source_branch]' do
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?
Remove source branch when merge request is accepted.
#!/usr/bin/env ruby
require 'stackprof'
$:.unshift 'spec'
require 'rails_helper'
filename = ARGV[0].split('/').last
interval = ENV.fetch('INTERVAL', 1000).to_i
limit = ENV.fetch('LIMIT', 20)
output_file = "tmp/#{filename}.dump"
StackProf.run(mode: :wall, out: output_file, interval: interval) do
RSpec::Core::Runner.run(ARGV, $stderr, $stdout)
end
system("stackprof #{output_file} --text --limit #{limit}")
---
title: New `gitlab:workhorse:install` rake task
merge_request: 6574
author:
---
title: Add Human Readable format for rake backup
merge_request: 7188
author: David Gerő
---
title: Moved new projects button below new group button on the welcome screen
merge_request: 7770
author:
---
title: Update generic/external build status to match normal build status template
merge_request: 7811
author:
---
title: Fixes Environments displaying incorrect date since 8.14 upgrade
merge_request:
author:
---
title: Fixes system note style in commit discussion
merge_request: 7721
author:
---
title: Adjust the width of project avatars to fix alignment within their container
merge_request:
author: Ryan Harris
---
title: Sentence cased the nav tab headers on the project dashboard page
merge_request:
author: Ryan Harris
---
title: Adds hoverstates for collapsed Issue/Merge Request sidebar
merge_request: !7777
author:
---
title: Do not raise error in AutocompleteController#users when not authorized
merge_request: 7817
author: Semyon Pupkov
---
title: Fix pipelines info being hidden in merge request widget
merge_request: 7808
author:
---
title: 'API: Expose merge status for branch API'
merge_request:
author: Robert Schilling
---
title: Refactor JiraService by moving code out of JiraService#execute method
merge_request: 7756
author:
---
title: Fix for error thrown in cycle analytics events if build has not started
merge_request:
author:
---
title: Create builds in transaction to avoid empty pipelines
merge_request: 7742
author:
---
title: Allow access to the wiki with git when repository feature disabled
merge_request:
author:
---
title: Add note to the invite page when the logged in user email is not the same as the invitation
merge_request:
author:
---
title: Fix appearance in error pages
merge_request:
author: Luis Alonso Chavez Armendariz
---
title: Changed eslint airbnb config to the base airbnb config and corrected eslintrc
plugins and envs
merge_request: 7470
author: Luke "Jared" Bennett
---
title: Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1
merge_request:
author:
# rubocop:disable all # rubocop:disable all
class CreateForkedProjectLinks < ActiveRecord::Migration class CreateForkedProjectLinks < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :forked_project_links do |t| create_table :forked_project_links do |t|
t.integer :forked_to_project_id, null: false t.integer :forked_to_project_id, null: false
t.integer :forked_from_project_id, null: false t.integer :forked_from_project_id, null: false
t.timestamps t.timestamps null: true
end end
add_index :forked_project_links, :forked_to_project_id, unique: true add_index :forked_project_links, :forked_to_project_id, unique: true
end end
......
# rubocop:disable all # rubocop:disable all
class CreateDeployKeysProjects < ActiveRecord::Migration class CreateDeployKeysProjects < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :deploy_keys_projects do |t| create_table :deploy_keys_projects do |t|
t.integer :deploy_key_id, null: false t.integer :deploy_key_id, null: false
t.integer :project_id, null: false t.integer :project_id, null: false
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateUsersGroups < ActiveRecord::Migration class CreateUsersGroups < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :users_groups do |t| create_table :users_groups do |t|
t.integer :group_access, null: false t.integer :group_access, null: false
t.integer :group_id, null: false t.integer :group_id, null: false
t.integer :user_id, null: false t.integer :user_id, null: false
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateProjectGroupLinks < ActiveRecord::Migration class CreateProjectGroupLinks < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :project_group_links do |t| create_table :project_group_links do |t|
t.integer :project_id, null: false t.integer :project_id, null: false
t.integer :group_id, null: false t.integer :group_id, null: false
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateBroadcastMessages < ActiveRecord::Migration class CreateBroadcastMessages < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :broadcast_messages do |t| create_table :broadcast_messages do |t|
t.text :message, null: false t.text :message, null: false
...@@ -7,7 +9,7 @@ class CreateBroadcastMessages < ActiveRecord::Migration ...@@ -7,7 +9,7 @@ class CreateBroadcastMessages < ActiveRecord::Migration
t.datetime :ends_at t.datetime :ends_at
t.integer :alert_type t.integer :alert_type
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateMergeRequestDiffs < ActiveRecord::Migration class CreateMergeRequestDiffs < ActiveRecord::Migration
DOWNTIME = false
def up def up
create_table :merge_request_diffs do |t| create_table :merge_request_diffs do |t|
t.string :state, null: false, default: 'collected' t.string :state, null: false, default: 'collected'
...@@ -7,7 +9,7 @@ class CreateMergeRequestDiffs < ActiveRecord::Migration ...@@ -7,7 +9,7 @@ class CreateMergeRequestDiffs < ActiveRecord::Migration
t.text :st_diffs, null: true t.text :st_diffs, null: true
t.integer :merge_request_id, null: false t.integer :merge_request_id, null: false
t.timestamps t.timestamps null: true
end end
if ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/ if ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
......
# rubocop:disable all # rubocop:disable all
class CreateEmails < ActiveRecord::Migration class CreateEmails < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :emails do |t| create_table :emails do |t|
t.integer :user_id, null: false t.integer :user_id, null: false
t.string :email, null: false t.string :email, null: false
t.timestamps t.timestamps null: true
end end
add_index :emails, :user_id add_index :emails, :user_id
......
# rubocop:disable all # rubocop:disable all
class CreateUsersStarProjects < ActiveRecord::Migration class CreateUsersStarProjects < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :users_star_projects do |t| create_table :users_star_projects do |t|
t.integer :project_id, null: false t.integer :project_id, null: false
t.integer :user_id, null: false t.integer :user_id, null: false
t.timestamps t.timestamps null: true
end end
add_index :users_star_projects, :user_id add_index :users_star_projects, :user_id
add_index :users_star_projects, :project_id add_index :users_star_projects, :project_id
......
# rubocop:disable all # rubocop:disable all
class CreateLabels < ActiveRecord::Migration class CreateLabels < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :labels do |t| create_table :labels do |t|
t.string :title t.string :title
t.string :color t.string :color
t.integer :project_id t.integer :project_id
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateLabelLinks < ActiveRecord::Migration class CreateLabelLinks < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :label_links do |t| create_table :label_links do |t|
t.integer :label_id t.integer :label_id
t.integer :target_id t.integer :target_id
t.string :target_type t.string :target_type
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class AddMembersTable < ActiveRecord::Migration class AddMembersTable < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :members do |t| create_table :members do |t|
t.integer :access_level, null: false t.integer :access_level, null: false
...@@ -9,7 +11,7 @@ class AddMembersTable < ActiveRecord::Migration ...@@ -9,7 +11,7 @@ class AddMembersTable < ActiveRecord::Migration
t.integer :notification_level, null: false t.integer :notification_level, null: false
t.string :type t.string :type
t.timestamps t.timestamps null: true
end end
add_index :members, :type add_index :members, :type
......
# rubocop:disable all # rubocop:disable all
class RemoveOldMemberTables < ActiveRecord::Migration class RemoveOldMemberTables < ActiveRecord::Migration
DOWNTIME = false
def up def up
drop_table :users_groups drop_table :users_groups
drop_table :users_projects drop_table :users_projects
...@@ -12,7 +14,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration ...@@ -12,7 +14,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration
t.integer :user_id, null: false t.integer :user_id, null: false
t.integer :notification_level, null: false, default: 3 t.integer :notification_level, null: false, default: 3
t.timestamps t.timestamps null: true
end end
create_table :users_projects do |t| create_table :users_projects do |t|
...@@ -21,7 +23,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration ...@@ -21,7 +23,7 @@ class RemoveOldMemberTables < ActiveRecord::Migration
t.integer :user_id, null: false t.integer :user_id, null: false
t.integer :notification_level, null: false, default: 3 t.integer :notification_level, null: false, default: 3
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class AddAuditEvent < ActiveRecord::Migration class AddAuditEvent < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :audit_events do |t| create_table :audit_events do |t|
t.integer :author_id, null: false t.integer :author_id, null: false
...@@ -13,7 +15,7 @@ class AddAuditEvent < ActiveRecord::Migration ...@@ -13,7 +15,7 @@ class AddAuditEvent < ActiveRecord::Migration
# Details for the event # Details for the event
t.text :details t.text :details
t.timestamps t.timestamps null: true
end end
add_index :audit_events, :author_id add_index :audit_events, :author_id
......
# rubocop:disable all # rubocop:disable all
class CreateDoorkeeperTables < ActiveRecord::Migration class CreateDoorkeeperTables < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :oauth_applications do |t| create_table :oauth_applications do |t|
t.string :name, null: false t.string :name, null: false
...@@ -7,7 +9,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration ...@@ -7,7 +9,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration
t.string :secret, null: false t.string :secret, null: false
t.text :redirect_uri, null: false t.text :redirect_uri, null: false
t.string :scopes, null: false, default: '' t.string :scopes, null: false, default: ''
t.timestamps t.timestamps null: true
end end
add_index :oauth_applications, :uid, unique: true add_index :oauth_applications, :uid, unique: true
......
# rubocop:disable all # rubocop:disable all
class CreateApplicationSettings < ActiveRecord::Migration class CreateApplicationSettings < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :application_settings do |t| create_table :application_settings do |t|
t.integer :default_projects_limit t.integer :default_projects_limit
...@@ -8,7 +10,7 @@ class CreateApplicationSettings < ActiveRecord::Migration ...@@ -8,7 +10,7 @@ class CreateApplicationSettings < ActiveRecord::Migration
t.boolean :gravatar_enabled t.boolean :gravatar_enabled
t.text :sign_in_text t.text :sign_in_text
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateSubscriptionsTable < ActiveRecord::Migration class CreateSubscriptionsTable < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :subscriptions do |t| create_table :subscriptions do |t|
t.integer :user_id t.integer :user_id
t.references :subscribable, polymorphic: true t.references :subscribable, polymorphic: true
t.boolean :subscribed t.boolean :subscribed
t.timestamps t.timestamps null: true
end end
add_index :subscriptions, add_index :subscriptions,
......
# rubocop:disable all # rubocop:disable all
class CreateAbuseReports < ActiveRecord::Migration class CreateAbuseReports < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :abuse_reports do |t| create_table :abuse_reports do |t|
t.integer :reporter_id t.integer :reporter_id
t.integer :user_id t.integer :user_id
t.text :message t.text :message
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateLfsObjects < ActiveRecord::Migration class CreateLfsObjects < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :lfs_objects do |t| create_table :lfs_objects do |t|
t.string :oid, null: false, unique: true t.string :oid, null: false, unique: true
t.integer :size, null: false t.integer :size, null: false
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class CreateLfsObjectsProjects < ActiveRecord::Migration class CreateLfsObjectsProjects < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :lfs_objects_projects do |t| create_table :lfs_objects_projects do |t|
t.integer :lfs_object_id, null: false t.integer :lfs_object_id, null: false
t.integer :project_id, null: false t.integer :project_id, null: false
t.timestamps t.timestamps null: true
end end
add_index :lfs_objects_projects, :project_id add_index :lfs_objects_projects, :project_id
......
# rubocop:disable all # rubocop:disable all
class CreateReleases < ActiveRecord::Migration class CreateReleases < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :releases do |t| create_table :releases do |t|
t.string :tag t.string :tag
t.text :description t.text :description
t.integer :project_id t.integer :project_id
t.timestamps t.timestamps null: true
end end
add_index :releases, :project_id add_index :releases, :project_id
......
# rubocop:disable all # rubocop:disable all
class CreateTasks < ActiveRecord::Migration class CreateTasks < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :tasks do |t| create_table :tasks do |t|
t.references :user, null: false, index: true t.references :user, null: false, index: true
...@@ -9,7 +11,7 @@ class CreateTasks < ActiveRecord::Migration ...@@ -9,7 +11,7 @@ class CreateTasks < ActiveRecord::Migration
t.integer :action, null: false t.integer :action, null: false
t.string :state, null: false, index: true t.string :state, null: false, index: true
t.timestamps t.timestamps null: true
end end
end end
end end
# rubocop:disable all # rubocop:disable all
class AddAwardEmoji < ActiveRecord::Migration class AddAwardEmoji < ActiveRecord::Migration
DOWNTIME = false
def change def change
create_table :award_emoji do |t| create_table :award_emoji do |t|
t.string :name t.string :name
t.references :user t.references :user
t.references :awardable, polymorphic: true t.references :awardable, polymorphic: true
t.timestamps t.timestamps null: true
end end
add_index :award_emoji, :user_id add_index :award_emoji, :user_id
......
...@@ -10,7 +10,7 @@ class CreateProjectFeatures < ActiveRecord::Migration ...@@ -10,7 +10,7 @@ class CreateProjectFeatures < ActiveRecord::Migration
t.integer :snippets_access_level t.integer :snippets_access_level
t.integer :builds_access_level t.integer :builds_access_level
t.timestamps t.timestamps null: true
end end
end end
end end
...@@ -22,6 +22,7 @@ Example response: ...@@ -22,6 +22,7 @@ Example response:
[ [
{ {
"name": "master", "name": "master",
"merged": false,
"protected": true, "protected": true,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false, "developers_can_merge": false,
...@@ -65,6 +66,7 @@ Example response: ...@@ -65,6 +66,7 @@ Example response:
```json ```json
{ {
"name": "master", "name": "master",
"merged": false,
"protected": true, "protected": true,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false, "developers_can_merge": false,
...@@ -123,6 +125,7 @@ Example response: ...@@ -123,6 +125,7 @@ Example response:
] ]
}, },
"name": "master", "name": "master",
"merged": false,
"protected": true, "protected": true,
"developers_can_push": true, "developers_can_push": true,
"developers_can_merge": true "developers_can_merge": true
...@@ -166,6 +169,7 @@ Example response: ...@@ -166,6 +169,7 @@ Example response:
] ]
}, },
"name": "master", "name": "master",
"merged": false,
"protected": false, "protected": false,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false "developers_can_merge": false
...@@ -206,6 +210,7 @@ Example response: ...@@ -206,6 +210,7 @@ Example response:
] ]
}, },
"name": "newbranch", "name": "newbranch",
"merged": false,
"protected": false, "protected": false,
"developers_can_push": false, "developers_can_push": false,
"developers_can_merge": false "developers_can_merge": false
......
...@@ -626,6 +626,7 @@ Parameters: ...@@ -626,6 +626,7 @@ Parameters:
| `path` | string | no | Custom repository name for new project. By default generated based on name | | `path` | string | no | Custom repository name for new project. By default generated based on name |
| `default_branch` | string | no | `master` by default | | `default_branch` | string | no | `master` by default |
| `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) | | `namespace_id` | integer | no | Namespace for the new project (defaults to the current user's namespace) |
| `default_branch` | string | no | `master` by default |
| `description` | string | no | Short project description | | `description` | string | no | Short project description |
| `issues_enabled` | boolean | no | Enable issues for this project | | `issues_enabled` | boolean | no | Enable issues for this project |
| `merge_requests_enabled` | boolean | no | Enable merge requests for this project | | `merge_requests_enabled` | boolean | no | Enable merge requests for this project |
......
...@@ -60,7 +60,7 @@ Parameters: ...@@ -60,7 +60,7 @@ Parameters:
- `file_path` (required) - Full path to new file. Ex. lib/class.rb - `file_path` (required) - Full path to new file. Ex. lib/class.rb
- `branch_name` (required) - The name of branch - `branch_name` (required) - The name of branch
- `encoding` (optional) - 'text' or 'base64'. Text is default. - `encoding` (optional) - Change encoding to 'base64'. Default is text.
- `author_email` (optional) - Specify the commit author's email address - `author_email` (optional) - Specify the commit author's email address
- `author_name` (optional) - Specify the commit author's name - `author_name` (optional) - Specify the commit author's name
- `content` (required) - File content - `content` (required) - File content
...@@ -89,7 +89,7 @@ Parameters: ...@@ -89,7 +89,7 @@ Parameters:
- `file_path` (required) - Full path to file. Ex. lib/class.rb - `file_path` (required) - Full path to file. Ex. lib/class.rb
- `branch_name` (required) - The name of branch - `branch_name` (required) - The name of branch
- `encoding` (optional) - 'text' or 'base64'. Text is default. - `encoding` (optional) - Change encoding to 'base64'. Default is text.
- `author_email` (optional) - Specify the commit author's email address - `author_email` (optional) - Specify the commit author's email address
- `author_name` (optional) - Specify the commit author's name - `author_name` (optional) - Specify the commit author's name
- `content` (required) - New file content - `content` (required) - New file content
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
- [CI services (linked docker containers)](services/README.md) - [CI services (linked docker containers)](services/README.md)
- [CI/CD pipelines settings](../user/project/pipelines/settings.md) - [CI/CD pipelines settings](../user/project/pipelines/settings.md)
- [Review Apps](review_apps/index.md) - [Review Apps](review_apps/index.md)
- [Git submodules](git_submodules.md) Using Git submodules in your CI jobs
## Breaking changes ## Breaking changes
......
# Using Git submodules with GitLab CI
> **Notes:**
- GitLab 8.12 introduced a new [CI build permissions model][newperms] and you
are encouraged to upgrade your GitLab instance if you haven't done already.
If you are **not** using GitLab 8.12 or higher, you would need to work your way
around submodules in order to access the sources of e.g., `gitlab.com/group/project`
with the use of [SSH keys](ssh_keys/README.md).
- With GitLab 8.12 onward, your permissions are used to evaluate what a CI build
can access. More information about how this system works can be found in the
[Build permissions model](../user/permissions.md#builds-permissions).
- The HTTP(S) Git protocol [must be enabled][gitpro] in your GitLab instance.
## Configuring the `.gitmodules` file
If dealing with [Git submodules][gitscm], your project will probably have a file
named `.gitmodules`.
Let's consider the following example:
1. Your project is located at `https://gitlab.com/secret-group/my-project`.
1. To checkout your sources you usually use an SSH address like
`git@gitlab.com:secret-group/my-project.git`.
1. Your project depends on `https://gitlab.com/group/project`, which you want
to include as a submodule.
If you are using GitLab 8.12+ and your submodule is on the same GitLab server,
you must update your `.gitmodules` file to use **relative URLs**.
Since Git allows the usage of relative URLs for your `.gitmodules` configuration,
this easily allows you to use HTTP(S) for cloning all your CI builds and SSH
for all your local checkouts. The `.gitmodules` would look like:
```ini
[submodule "project"]
path = project
url = ../../group/project.git
```
The above configuration will instruct Git to automatically deduce the URL that
should be used when cloning sources. Whether you use HTTP(S) or SSH, Git will use
that same channel and it will allow to make all your CI builds use HTTP(S)
(because GitLab CI only uses HTTP(S) for cloning your sources), and all your local
clones will continue using SSH.
For all other submodules not located on the same GitLab server, use the full
HTTP(S) protocol URL:
```ini
[submodule "project-x"]
path = project-x
url = https://gitserver.com/group/project-x.git
```
Once `.gitmodules` is correctly configured, you can move on to
[configuring your `.gitlab-ci.yml`](#using-git-submodules-in-your-ci-jobs).
## Using Git submodules in your CI jobs
There are a few steps you need to take in order to make submodules work
correctly with your CI builds:
1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file)
for the submodules located in the same GitLab server.
1. Then, use `git submodule sync/update` in `before_script`:
```yaml
before_script:
- git submodule sync --recursive
- git submodule update --init --recursive
```
`--recursive` should be used in either both or none (`sync/update`) depending on
whether you have recursive submodules.
The rationale to set the `sync` and `update` in `before_script` is because of
the way Git submodules work. On a fresh Runner workspace, Git will set the
submodule URL including the token in `.git/config`
(or `.git/modules/<submodule>/config`) based on `.gitmodules` and the current
remote URL. On subsequent builds on the same Runner, `.git/config` is cached
and already contains a full URL for the submodule, corresponding to the previous
build, and to **a token from a previous build**. `sync` allows to force updating
the full URL.
[gitpro]: ../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols
[gitscm]: https://git-scm.com/book/en/v2/Git-Tools-Submodules "Git submodules documentation"
[newperms]: ../user/project/new_ci_build_permissions_model.md
...@@ -101,6 +101,116 @@ In short: ...@@ -101,6 +101,116 @@ In short:
5. If you must write a benchmark use the benchmark-ips Gem instead of Ruby's 5. If you must write a benchmark use the benchmark-ips Gem instead of Ruby's
`Benchmark` module. `Benchmark` module.
## Profiling
By collecting snapshots of process state at regular intervals, profiling allows
you to see where time is spent in a process. The [StackProf](https://github.com/tmm1/stackprof)
gem is included in GitLab's development environment, allowing you to investigate
the behaviour of suspect code in detail.
It's important to note that profiling an application *alters its performance*,
and will generally be done *in an unrepresentative environment*. In particular,
a method is not necessarily troublesome just because it is executed many times,
or takes a long time to execute. Profiles are tools you can use to better
understand what is happening in an application - using that information wisely
is up to you!
Keeping that in mind, to create a profile, identify (or create) a spec that
exercises the troublesome code path, then run it using the `bin/rspec-stackprof`
helper, e.g.:
```
$ LIMIT=10 bin/rspec-stackprof spec/policies/project_policy_spec.rb
8/8 |====== 100 ======>| Time: 00:00:18
Finished in 18.19 seconds (files took 4.8 seconds to load)
8 examples, 0 failures
==================================
Mode: wall(1000)
Samples: 17033 (5.59% miss rate)
GC: 1901 (11.16%)
==================================
TOTAL (pct) SAMPLES (pct) FRAME
6000 (35.2%) 2566 (15.1%) Sprockets::Cache::FileStore#get
2018 (11.8%) 888 (5.2%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_no_cache
1338 (7.9%) 640 (3.8%) ActiveRecord::ConnectionAdapters::PostgreSQL::DatabaseStatements#execute
3125 (18.3%) 394 (2.3%) Sprockets::Cache::FileStore#safe_open
913 (5.4%) 301 (1.8%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_cache
288 (1.7%) 288 (1.7%) ActiveRecord::Attribute#initialize
246 (1.4%) 246 (1.4%) Sprockets::Cache::FileStore#safe_stat
295 (1.7%) 193 (1.1%) block (2 levels) in class_attribute
187 (1.1%) 187 (1.1%) block (4 levels) in class_attribute
```
You can limit the specs that are run by passing any arguments `rspec` would
normally take.
The output is sorted by the `Samples` column by default. This is the number of
samples taken where the method is the one currently being executed. The `Total`
column shows the number of samples taken where the method, or any of the methods
it calls, were being executed.
To create a graphical view of the call stack:
```shell
$ stackprof tmp/project_policy_spec.rb.dump --graphviz > project_policy_spec.dot
$ dot -Tsvg project_policy_spec.dot > project_policy_spec.svg
```
To load the profile in [kcachegrind](https://kcachegrind.github.io/):
```
$ stackprof tmp/project_policy_spec.dump --callgrind > project_policy_spec.callgrind
$ kcachegrind project_policy_spec.callgrind # Linux
$ qcachegrind project_policy_spec.callgrind # Mac
```
It may be useful to zoom in on a specific method, e.g.:
```
$ stackprof tmp/project_policy_spec.rb.dump --method warm_asset_cache
TestEnv#warm_asset_cache (/Users/lupine/dev/gitlab.com/gitlab-org/gitlab-development-kit/gitlab/spec/support/test_env.rb:164)
samples: 0 self (0.0%) / 6288 total (36.9%)
callers:
6288 ( 100.0%) block (2 levels) in <top (required)>
callees (6288 total):
6288 ( 100.0%) Capybara::RackTest::Driver#visit
code:
| 164 | def warm_asset_cache
| 165 | return if warm_asset_cache?
| 166 | return unless defined?(Capybara)
| 167 |
6288 (36.9%) | 168 | Capybara.current_session.driver.visit '/'
| 169 | end
$ stackprof tmp/project_policy_spec.rb.dump --method BasePolicy#abilities
BasePolicy#abilities (/Users/lupine/dev/gitlab.com/gitlab-org/gitlab-development-kit/gitlab/app/policies/base_policy.rb:79)
samples: 0 self (0.0%) / 50 total (0.3%)
callers:
25 ( 50.0%) BasePolicy.abilities
25 ( 50.0%) BasePolicy#collect_rules
callees (50 total):
25 ( 50.0%) ProjectPolicy#rules
25 ( 50.0%) BasePolicy#collect_rules
code:
| 79 | def abilities
| 80 | return RuleSet.empty if @user && @user.blocked?
| 81 | return anonymous_abilities if @user.nil?
50 (0.3%) | 82 | collect_rules { rules }
| 83 | end
```
Since the profile includes the work done by the test suite as well as the
application code, these profiles can be used to investigate slow tests as well.
However, for smaller runs (like this example), this means that the cost of
setting up the test suite will tend to dominate.
It's also possible to modify the application code in-place to output profiles
whenever a particular code path is triggered without going through the test
suite first. See the
[StackProf documentation](https://github.com/tmm1/stackprof/blob/master/README.md)
for details.
## Importance of Changes ## Importance of Changes
When working on performance improvements, it's important to always ask yourself When working on performance improvements, it's important to always ask yourself
......
...@@ -396,15 +396,25 @@ GitLab Shell is an SSH access and repository management software developed speci ...@@ -396,15 +396,25 @@ GitLab Shell is an SSH access and repository management software developed speci
### Install gitlab-workhorse ### Install gitlab-workhorse
GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). The
If you are not using Linux you may have to run `gmake` instead of following command-line will install GitLab-Workhorse in `/home/git/gitlab-workhorse`
`make` below. which is the recommended location.
cd /home/git cd /home/git/gitlab
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
cd gitlab-workhorse sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
sudo -u git -H git checkout v1.0.1
sudo -u git -H make You can specify a different Git repository by providing `GITLAB_WORKHORSE_REPO`:
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" GITLAB_WORKHORSE_REPO=https://example.com/gitlab-workhorse.git RAILS_ENV=production
You can specify a different version to use by providing `GITLAB_WORKHORSE_VERSION`:
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" GITLAB_WORKHORSE_VERSION=0.8.1 RAILS_ENV=production
### Initialize Database and Activate Advanced Features ### Initialize Database and Activate Advanced Features
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment