Merge branch 'master' into tmp-reference-pipeline-and-caching

# Conflicts:
#	spec/lib/gitlab/markdown/autolink_filter_spec.rb
#	spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb
#	spec/lib/gitlab/markdown/commit_reference_filter_spec.rb
#	spec/lib/gitlab/markdown/cross_project_reference_spec.rb
#	spec/lib/gitlab/markdown/emoji_filter_spec.rb
#	spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb
#	spec/lib/gitlab/markdown/external_link_filter_spec.rb
#	spec/lib/gitlab/markdown/issue_reference_filter_spec.rb
#	spec/lib/gitlab/markdown/label_reference_filter_spec.rb
#	spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb
#	spec/lib/gitlab/markdown/redactor_filter_spec.rb
#	spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb
#	spec/lib/gitlab/markdown/relative_link_filter_spec.rb
#	spec/lib/gitlab/markdown/sanitization_filter_spec.rb
#	spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb
#	spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb
#	spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb
#	spec/lib/gitlab/markdown/task_list_filter_spec.rb
#	spec/lib/gitlab/markdown/upload_link_filter_spec.rb
#	spec/lib/gitlab/markdown/user_reference_filter_spec.rb
This commit is contained in:
Douwe Maan 2015-12-10 14:04:34 +01:00
commit 10387f6b8a
351 changed files with 2662 additions and 879 deletions

View File

@ -8,7 +8,7 @@ before_script:
- touch log/application.log
- touch log/test.log
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
- bundle exec rake db:create RAILS_ENV=test
- bundle exec rake db:reset db:create RAILS_ENV=test
spec:feature:
script:
@ -24,6 +24,27 @@ spec:api:
- ruby
- mysql
spec:models:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models
tags:
- ruby
- mysql
spec:lib:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib
tags:
- ruby
- mysql
spec:services:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services
tags:
- ruby
- mysql
spec:benchmark:
script:
- RAILS_ENV=test bundle exec rake spec:benchmark
@ -39,9 +60,16 @@ spec:other:
- ruby
- mysql
spinach:project:
spinach:project:half:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half
tags:
- ruby
- mysql
spinach:project:rest:
script:
- RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest
tags:
- ruby
- mysql
@ -89,10 +117,9 @@ flay:
- mysql
bundler:audit:
script:
script:
- "bundle exec bundle-audit update"
- "bundle exec bundle-audit check"
tags:
- ruby
- mysql
allow_failure: true

View File

@ -1,6 +1,7 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.3.0 (unreleased)
- Merge when build succeeds (Zeger-Jan van de Weg)
- Bump gollum-lib to 4.1.0 (Stan Hu)
- Fix broken group avatar upload under "New group" (Stan Hu)
- Update project repositorize size and commit count during import:repos task (Stan Hu)
@ -19,18 +20,24 @@ v 8.3.0 (unreleased)
- Add API endpoint to fetch merge request commits list
- Expose events API with comment information and author info
- Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583
- Fix 500 error when creating a merge request that removes a submodule
- Run custom Git hooks when branch is created or deleted.
- Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch
- Add languages page to graphs
- Block LDAP user when they are no longer found in the LDAP server
- Improve wording on project visibility levels (Zeger-Jan van de Weg)
- Automatically select default clone protocol based on user preferences (Eirik Lygre)
- Make Network page as sub tab of Commits
v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu)
- Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)
- Update documentation for "Guest" permissions
- Properly convert Emoji-only comments into Award Emojis
- Enable devise paranoid mode to prevent user enumeration attack
v 8.2.3
- Webhook payload has an added, modified and removed properties for each commit
- Fix 500 error when creating a merge request that removes a submodule
v 8.2.2
- Fix 404 in redirection after removing a project (Stan Hu)
@ -38,6 +45,7 @@ v 8.2.2
- Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
- Fix: Raw private snippets access workflow
- Prevent "413 Request entity too large" errors when pushing large files with LFS
- Fix: As an admin, cannot add oneself as a member to a group/project
- Fix invalid links within projects dashboard header
- Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
- Fix: duplicate email notifications on issue comments
@ -90,7 +98,6 @@ v 8.2.0
- Add email notification to former assignee upon unassignment (Adam Lieskovský)
- New design for project graphs page
- Remove deprecated dumped yaml file generated from previous job definitions
- Fix incoming email config defaults
- Show specific runners from projects where user is master or owner
- MR target branch is now visible on a list view when it is different from project's default one
- Improve Continuous Integration graphs page
@ -248,7 +255,6 @@ v 8.0.2
- Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
v 8.0.1
- Remove git refs used internally by GitLab from network graph (Stan Hu)
- Improve CI migration procedure and documentation
v 8.0.0

View File

@ -93,7 +93,6 @@ gem 'html-pipeline', '~> 1.11.0'
gem 'task_list', '~> 1.0.2', require: 'task_list/railtie'
gem 'github-markup', '~> 1.3.1'
gem 'redcarpet', '~> 3.3.3'
gem 'RedCloth', '~> 4.2.9'
gem 'rdoc', '~>3.6'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'

View File

@ -2,7 +2,6 @@ GEM
remote: https://rubygems.org/
specs:
CFPropertyList (2.3.2)
RedCloth (4.2.9)
ace-rails-ap (2.0.1)
actionmailer (4.2.4)
actionpack (= 4.2.4)
@ -826,7 +825,6 @@ PLATFORMS
ruby
DEPENDENCIES
RedCloth (~> 4.2.9)
ace-rails-ap (~> 2.0.1)
activerecord-deprecated_finders (~> 1.0.3)
activerecord-session_store (~> 0.1.0)

View File

@ -43,6 +43,7 @@
#
class @MergeRequestTabs
diffsLoaded: false
buildsLoaded: false
commitsLoaded: false
constructor: (@opts = {}) ->
@ -54,6 +55,12 @@ class @MergeRequestTabs
bindEvents: ->
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
$(document).on 'click', '.js-show-tab', @showTab
showTab: (event) =>
event.preventDefault()
@activateTab $(event.target).data('action')
tabShown: (event) =>
$target = $(event.target)
@ -63,6 +70,8 @@ class @MergeRequestTabs
@loadCommits($target.attr('href'))
else if action == 'diffs'
@loadDiff($target.attr('href'))
else if action == 'builds'
@loadBuilds($target.attr('href'))
@setCurrentAction(action)
@ -101,7 +110,7 @@ class @MergeRequestTabs
action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs'
new_state = @_location.pathname.replace(/\/(commits|diffs)(\.html)?\/?$/, '')
new_state = @_location.pathname.replace(/\/(commits|diffs|builds)(\.html)?\/?$/, '')
# Append the new action if we're on a tab other than 'notes'
unless action == 'notes'
@ -139,6 +148,17 @@ class @MergeRequestTabs
@diffsLoaded = true
@scrollToElement("#diffs")
loadBuilds: (source) ->
return if @buildsLoaded
@_get
url: "#{source}.json"
success: (data) =>
document.getElementById('builds').innerHTML = data.html
$('.js-timeago').timeago()
@buildsLoaded = true
@scrollToElement("#builds")
# Show or hide the loading spinner
#
# status - Boolean, true to show, false to hide

View File

@ -7,7 +7,7 @@
padding: 0;
list-style: none;
li {
> li {
padding: 10px 15px;
min-height: 20px;
border-bottom: 1px solid #eee;
@ -88,8 +88,14 @@ ul.bordered-list {
}
}
li.task-list-item {
list-style-type: none;
ul.task-list {
li.task-list-item {
list-style-type: none;
}
ul:not(.task-list) {
padding-left: 1.3em;
}
}
ul.content-list {
@ -125,3 +131,27 @@ ul.content-list {
margin: 0;
}
}
ul.controls {
padding-top: 1px;
float: right;
list-style: none;
.btn {
padding: 10px 14px;
}
> li {
float: left;
padding-right: 10px;
.author_link {
display: inline-block;
.avatar-inline {
margin-left: 0;
margin-right: 0;
}
}
}
}

View File

@ -18,6 +18,7 @@
.accept-merge-holder {
.accept-action {
display: inline-block;
float: left;
.accept_merge_request {
&.ci-pending,
@ -36,14 +37,15 @@
.accept-control {
display: inline-block;
float: left;
margin: 0;
margin-left: 20px;
padding: 5px;
padding-top: 12px;
line-height: 20px;
&.right {
float: right;
padding-top: 12px;
a {
color: $gl-gray;
}
@ -81,6 +83,10 @@
&.ci-error {
color: $gl-danger;
}
a.monospace {
color: inherit;
}
}
.mr-widget-body,

View File

@ -109,13 +109,9 @@ ul.notes {
}
}
// Reduce left padding of first task list ul element
ul.task-list:first-child {
padding-left: 10px;
// sub-tasks should be padded normally
ul {
padding-left: 20px;
ul.task-list {
ul:not(.task-list) {
padding-left: 1.3em;
}
}

View File

@ -40,7 +40,9 @@ class PasswordsController < Devise::PasswordsController
def throttle_reset
return unless resource && resource.recently_sent_password_reset?
redirect_to new_password_path(resource_name),
alert: I18n.t('devise.passwords.recently_reset')
# Throttle reset attempts, but return a normal message to
# avoid user enumeration attack.
redirect_to new_user_session_path,
notice: I18n.t('devise.passwords.send_paranoid_instructions')
end
end

View File

@ -21,7 +21,7 @@ class Projects::ApplicationController < ApplicationController
unless @repository.branch_names.include?(@ref)
redirect_to(
namespace_project_tree_path(@project.namespace, @project, @ref),
notice: "This action is not allowed unless you are on top of a branch"
notice: "This action is not allowed unless you are on a branch"
)
end
end

View File

@ -162,12 +162,20 @@ class Projects::BlobController < Projects::ApplicationController
end
def sanitized_new_branch_name
@new_branch ||= sanitize(strip_tags(params[:new_branch]))
sanitize(strip_tags(params[:new_branch]))
end
def editor_variables
@current_branch = @ref
@new_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref
@new_branch =
if params[:new_branch].present?
sanitized_new_branch_name
elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref)
@ref
else
@repository.next_patch_branch
end
@file_path =
if action_name.to_s == 'create'

View File

@ -37,7 +37,7 @@ class Projects::CommitController < Projects::ApplicationController
def cancel_builds
ci_commit.builds.running_or_pending.each(&:cancel)
redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def retry_builds
@ -47,7 +47,7 @@ class Projects::CommitController < Projects::ApplicationController
end
end
redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha)
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def branches
@ -74,8 +74,8 @@ class Projects::CommitController < Projects::ApplicationController
end
@notes_count = commit.notes.count
@builds = ci_commit.builds if ci_commit
@statuses = ci_commit.statuses if ci_commit
end
def authorize_manage_builds!

View File

@ -1,13 +1,14 @@
class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :merge, :merge_check,
:ci_status, :toggle_subscription
:edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
:ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds
]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
before_action :validates_merge_request, only: [:show, :diffs, :commits]
before_action :define_show_vars, only: [:show, :diffs, :commits]
before_action :ensure_ref_fetched, only: [:show, :commits, :diffs]
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
# Allow read any merge_request
before_action :authorize_read_merge_request!
@ -79,6 +80,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
def builds
@ci_project = @merge_request.source_project.gitlab_ci_project
respond_to do |format|
format.html { render 'show' }
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
end
end
def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
@ -91,20 +101,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@target_project = merge_request.target_project
@source_project = merge_request.source_project
@commits = @merge_request.compare_commits
@commits = @merge_request.compare_commits.reverse
@commit = @merge_request.last_commit
@first_commit = @merge_request.first_commit
@diffs = @merge_request.compare_diffs
@ci_project = @source_project.gitlab_ci_project
@ci_commit = @merge_request.ci_commit
@statuses = @ci_commit.statuses if @ci_commit
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
end
def edit
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
@target_branches = @merge_request.target_project.repository.branch_names
end
def create
@target_branches ||= []
@merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
@ -118,6 +127,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
def edit
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
@target_branches = @merge_request.target_project.repository.branch_names
end
def update
@merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
@ -150,15 +165,29 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
def cancel_merge_when_build_succeeds
return access_denied! unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user).cancel(@merge_request)
end
def merge
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
if @merge_request.mergeable?
@merge_request.update(merge_error: nil)
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
@status = true
unless @merge_request.mergeable?
@status = :failed
return
end
@merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds] && @merge_request.ci_commit && @merge_request.ci_commit.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request)
@status = :merge_when_build_succeeds
else
@status = false
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
@status = :success
end
end
@ -265,6 +294,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request_diff = @merge_request.merge_request_diff
@ci_commit = @merge_request.ci_commit
@statuses = @ci_commit.statuses if @ci_commit
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@ -272,6 +302,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
def define_widget_vars
@ci_commit = @merge_request.ci_commit
end
def invalid_mr
# Render special view for MR with removed source or target branch
render 'invalid'
@ -285,6 +319,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
)
end
def merge_params
params.permit(:should_remove_source_branch, :commit_message)
end
# Make sure merge requests created before 8.0
# have head file in refs/merge-requests/
def ensure_ref_fetched

View File

@ -23,7 +23,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
@group_members = @group_members.where(user_id: users)
end
@group_members = @group_members.order('access_level DESC').limit(20)
@group_members = @group_members.order('access_level DESC')
end
@project_member = @project.project_members.new

View File

@ -30,26 +30,24 @@ module BlobHelper
nil
end
if blob_viewable?(blob)
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
if allowed_tree_edit?(project, ref)
link_to(text,
namespace_project_edit_blob_path(project.namespace, project,
tree_join(ref, path),
link_opts),
class: cls
)
else
content_tag :span, text, class: cls + ' disabled'
end + after.html_safe
else
''
end
return unless blob && blob.text? && blob_editable?(blob)
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
link_to(text,
namespace_project_edit_blob_path(project.namespace, project,
tree_join(ref, path),
link_opts),
class: cls
) + after.html_safe
end
def blob_editable?(blob, project = @project, ref = @ref)
!blob.lfs_pointer? && allowed_tree_edit?(project, ref)
end
def leave_edit_message

View File

@ -11,7 +11,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_names.include?(branch_name)
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
end
end

View File

@ -58,7 +58,7 @@ module CiStatusHelper
def render_ci_status(ci_commit)
link_to ci_status_path(ci_commit),
class: "c#{ci_status_color(ci_commit)}",
title: "Build status: #{ci_status_label(ci_commit)}",
title: "Build #{ci_status_label(ci_commit)}",
data: { toggle: 'tooltip', placement: 'left' } do
ci_status_icon(ci_commit)
end

View File

@ -175,11 +175,19 @@ module ProjectsHelper
end
def default_url_to_repo(project = @project)
current_user ? project.url_to_repo : project.http_url_to_repo
if default_clone_protocol == "ssh"
project.ssh_url_to_repo
else
project.http_url_to_repo
end
end
def default_clone_protocol
current_user ? "ssh" : "http"
if !current_user || current_user.require_ssh_key?
"http"
else
"ssh"
end
end
def project_last_activity(project)

View File

@ -46,16 +46,26 @@ module TreeHelper
File.join(*args)
end
def on_top_of_branch?(project = @project, ref = @ref)
project.repository.branch_names.include?(ref)
end
def allowed_tree_edit?(project = nil, ref = nil)
project ||= @project
ref ||= @ref
return false unless project.repository.branch_names.include?(ref)
return false unless on_top_of_branch?(project, ref)
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
can?(current_user, :push_code, project)
end
def can_delete_or_replace?(blob)
allowed_tree_edit? && !blob.lfs_pointer?
def tree_edit_branch(project = @project, ref = @ref)
if allowed_tree_edit?(project, ref)
if can_push_branch?(project, ref)
ref
else
project.repository.next_patch_branch
end
end
end
def tree_breadcrumbs(tree, max_links = 2)

View File

@ -12,22 +12,22 @@ module VisibilityLevelHelper
# Return the description for the +level+ argument.
#
# +level+ One of the Gitlab::VisibilityLevel constants
# +form_model+ Either a model object (Project, Snippet, etc.) or the name of
# a Project or Snippet class.
# +level+ One of the Gitlab::VisibilityLevel constants
# +form_model+ Either a model object (Project, Snippet, etc.) or the name of
# a Project or Snippet class.
def visibility_level_description(level, form_model)
case form_model.is_a?(String) ? form_model : form_model.class.name
when 'PersonalSnippet', 'ProjectSnippet', 'Snippet'
snippet_visibility_level_description(level)
when 'Project'
case form_model
when Project
project_visibility_level_description(level)
when Snippet
snippet_visibility_level_description(level, form_model)
end
end
def project_visibility_level_description(level)
case level
when Gitlab::VisibilityLevel::PRIVATE
"Project access must be granted explicitly for each user."
"Project access must be granted explicitly to each user."
when Gitlab::VisibilityLevel::INTERNAL
"The project can be cloned by any logged in user."
when Gitlab::VisibilityLevel::PUBLIC
@ -35,12 +35,16 @@ module VisibilityLevelHelper
end
end
def snippet_visibility_level_description(level)
def snippet_visibility_level_description(level, snippet = nil)
case level
when Gitlab::VisibilityLevel::PRIVATE
"The snippet is visible only for me."
if snippet.is_a? ProjectSnippet
"The snippet is visible only to project members."
else
"The snippet is visible only to me."
end
when Gitlab::VisibilityLevel::INTERNAL
"The snippet is visible for any logged in user."
"The snippet is visible to any logged in user."
when Gitlab::VisibilityLevel::PUBLIC
"The snippet can be accessed without any authentication."
end

View File

@ -346,12 +346,10 @@ class Ability
unless group.last_owner?(target_user)
can_manage = group_abilities(user, group).include?(:admin_group_member)
if can_manage && user != target_user
if can_manage
rules << :update_group_member
rules << :destroy_group_member
end
if user == target_user
elsif user == target_user
rules << :destroy_group_member
end
end
@ -367,12 +365,10 @@ class Ability
unless target_user == project.owner
can_manage = project_abilities(user, project).include?(:admin_project_member)
if can_manage && user != target_user
if can_manage
rules << :update_project_member
rules << :destroy_project_member
end
if user == target_user
elsif user == target_user
rules << :destroy_project_member
end
end

View File

@ -165,6 +165,14 @@ module Ci
status == 'canceled'
end
def active?
running? || pending?
end
def complete?
canceled? || success? || failed?
end
def duration
duration_array = latest_statuses.map(&:duration).compact
duration_array.reduce(:+).to_i

View File

@ -175,11 +175,11 @@ class Commit
end
def author
@author ||= User.find_by_any_email(author_email)
@author ||= User.find_by_any_email(author_email.downcase)
end
def committer
@committer ||= User.find_by_any_email(committer_email)
@committer ||= User.find_by_any_email(committer_email.downcase)
end
def parents

View File

@ -1,34 +1,30 @@
# == Schema Information
#
# Table name: ci_builds
#
# id :integer not null, primary key
# project_id :integer
# status :string(255)
# finished_at :datetime
# trace :text
# created_at :datetime
# updated_at :datetime
# started_at :datetime
# runner_id :integer
# coverage :float
# commit_id :integer
# commands :text
# job_id :integer
# name :string(255)
# deploy :boolean default(FALSE)
# options :text
# allow_failure :boolean default(FALSE), not null
# stage :string(255)
# trigger_request_id :integer
# stage_idx :integer
# tag :boolean
# ref :string(255)
# user_id :integer
# type :string(255)
# target_url :string(255)
# description :string(255)
# artifacts_file :text
# project_id integer
# status string
# finished_at datetime
# trace text
# created_at datetime
# updated_at datetime
# started_at datetime
# runner_id integer
# coverage float
# commit_id integer
# commands text
# job_id integer
# name string
# deploy boolean default: false
# options text
# allow_failure boolean default: false, null: false
# stage string
# trigger_request_id integer
# stage_idx integer
# tag boolean
# ref string
# user_id integer
# type string
# target_url string
# description string
#
class CommitStatus < ActiveRecord::Base
@ -79,6 +75,10 @@ class CommitStatus < ActiveRecord::Base
build.update_attributes finished_at: Time.now
end
after_transition [:pending, :running] => :success do |build, transition|
MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build)
end
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'

View File

@ -1,3 +1,15 @@
# == Schema Information
#
# Table name: lfs_objects
#
# id :integer not null, primary key
# oid :string(255) not null
# size :integer not null
# created_at :datetime
# updated_at :datetime
# file :string(255)
#
class LfsObject < ActiveRecord::Base
has_many :lfs_objects_projects, dependent: :destroy
has_many :projects, through: :lfs_objects_projects

View File

@ -1,3 +1,14 @@
# == Schema Information
#
# Table name: lfs_objects_projects
#
# id :integer not null, primary key
# lfs_object_id :integer not null
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
#
class LfsObjectsProject < ActiveRecord::Base
belongs_to :project
belongs_to :lfs_object

View File

@ -2,25 +2,28 @@
#
# Table name: merge_requests
#
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# id :integer not null, primary key
# target_branch :string(255) not null
# source_branch :string(255) not null
# source_project_id :integer not null
# author_id :integer
# assignee_id :integer
# title :string(255)
# created_at :datetime
# updated_at :datetime
# milestone_id :integer
# state :string(255)
# merge_status :string(255)
# target_project_id :integer not null
# iid :integer
# description :text
# position :integer default(0)
# locked_at :datetime
# updated_by_id :integer
# merge_error :string(255)
# merge_params :text (serialized to hash)
# merge_when_build_succeeds :boolean default(false), not null
# merge_user_id :integer
#
require Rails.root.join("app/models/commit")
@ -35,9 +38,12 @@ class MergeRequest < ActiveRecord::Base
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
belongs_to :merge_user, class_name: "User"
has_one :merge_request_diff, dependent: :destroy
serialize :merge_params, Hash
after_create :create_merge_request_diff
after_update :update_merge_request_diff
@ -121,6 +127,7 @@ class MergeRequest < ActiveRecord::Base
validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true
validates :merge_user, presence: true, if: :merge_when_build_succeeds?
validate :validate_branches
validate :validate_fork
@ -258,6 +265,16 @@ class MergeRequest < ActiveRecord::Base
end
end
def can_cancel_merge_when_build_succeeds?(current_user)
can_be_merged_by?(current_user) || self.author == current_user
end
def can_remove_source_branch?(current_user)
!source_project.protected_branch?(source_branch) &&
!source_project.root_ref?(source_branch) &&
Ability.abilities.allowed?(current_user, :push_code, source_project)
end
def mr_and_commit_notes
# Fetch comments only from last 100 commits
commits_for_notes_limit = 100
@ -393,6 +410,16 @@ class MergeRequest < ActiveRecord::Base
message
end
def reset_merge_when_build_succeeds
return unless merge_when_build_succeeds?
self.merge_when_build_succeeds = false
self.merge_user = nil
self.merge_params = nil
self.save
end
# Return array of possible target branches
# depends on target project of MR
def target_branches
@ -480,8 +507,10 @@ class MergeRequest < ActiveRecord::Base
end
def ci_commit
if last_commit and source_project
source_project.ci_commit(last_commit.id)
end
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
end
def broken?
self.commits.blank? || branch_missing? || cannot_be_merged?
end
end

View File

@ -16,6 +16,7 @@
# system :boolean default(FALSE), not null
# st_diff :text
# updated_by_id :integer
# is_award :boolean default(FALSE), not null
#
require 'carrierwave/orm/activerecord'

View File

@ -28,6 +28,7 @@
# import_type :string(255)
# import_source :string(255)
# commit_count :integer default(0)
# import_error :text
#
require 'carrierwave/orm/activerecord'

View File

@ -329,6 +329,17 @@ class Repository
commit(sha)
end
def next_patch_branch
patch_branch_ids = self.branch_names.map do |n|
result = n.match(/\Apatch-([0-9]+)\z/)
result[1].to_i if result
end.compact
highest_patch_branch_id = patch_branch_ids.max || 0
"patch-#{highest_patch_branch_id + 1}"
end
# Remove archives older than 2 hours
def branches_sorted_by(value)
case value

View File

@ -56,6 +56,7 @@
# project_view :integer default(0)
# consumed_timestep :integer
# layout :integer default(0)
# hide_project_limit :boolean default(FALSE)
#
require 'carrierwave/orm/activerecord'

View File

@ -53,7 +53,7 @@ module Files
unless project.empty_repo?
unless repository.branch_names.include?(@current_branch)
raise_error("You can only create files if you are on top of a branch")
raise_error("You can only create or edit files when you are on a branch")
end
if @current_branch != @target_branch

View File

@ -6,15 +6,12 @@ module MergeRequests
# Executed when you do merge via GitLab UI
#
class MergeService < MergeRequests::BaseService
attr_reader :merge_request, :commit_message
attr_reader :merge_request
def execute(merge_request, commit_message)
@commit_message = commit_message
def execute(merge_request)
@merge_request = merge_request
unless @merge_request.mergeable?
return error('Merge request is not mergeable')
end
return error('Merge request is not mergeable') unless @merge_request.mergeable?
merge_request.in_locked_state do
if commit
@ -32,7 +29,7 @@ module MergeRequests
committer = repository.user_to_committer(current_user)
options = {
message: commit_message,
message: params[:commit_message] || merge_request.merge_commit_message,
author: committer,
committer: committer
}
@ -46,6 +43,11 @@ module MergeRequests
def after_merge
MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
if params[:should_remove_source_branch]
DeleteBranchService.new(@merge_request.source_project, current_user).
execute(merge_request.source_branch)
end
end
end
end

View File

@ -0,0 +1,55 @@
module MergeRequests
class MergeWhenBuildSucceedsService < MergeRequests::BaseService
# Marks the passed `merge_request` to be merged when the build succeeds or
# updates the params for the automatic merge
def execute(merge_request)
merge_request.merge_params.merge!(params)
# The service is also called when the merge params are updated.
already_approved = merge_request.merge_when_build_succeeds?
unless already_approved
merge_request.merge_when_build_succeeds = true
merge_request.merge_user = @current_user
SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.last_commit)
end
merge_request.save
end
# Triggers the automatic merge of merge_request once the build succeeds
def trigger(build)
merge_requests = merge_request_from(build)
merge_requests.each do |merge_request|
next unless merge_request.merge_when_build_succeeds?
if merge_request.ci_commit && merge_request.ci_commit.success? && merge_request.mergeable?
MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
end
end
end
# Cancels the automatic merge
def cancel(merge_request)
if merge_request.merge_when_build_succeeds? && merge_request.open?
merge_request.reset_merge_when_build_succeeds
SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user)
success
else
error("Can't cancel the automatic merge", 406)
end
end
private
def merge_request_from(build)
merge_requests = @project.origin_merge_requests.opened.where(source_branch: build.ref).to_a
merge_requests += @project.fork_merge_requests.opened.where(source_branch: build.ref).to_a
merge_requests.uniq.select(&:source_project)
end
end
end

View File

@ -11,6 +11,7 @@ module MergeRequests
# empty diff during a manual merge
close_merge_requests
reload_merge_requests
reset_merge_when_build_succeeds
# Leave a system note if a branch was deleted/added
if branch_added? || branch_removed?
@ -57,7 +58,6 @@ module MergeRequests
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_code
merge_request.mark_as_unchecked
@ -76,6 +76,10 @@ module MergeRequests
end
end
def reset_merge_when_build_succeeds
merge_requests_for_source_branch.each(&:reset_merge_when_build_succeeds)
end
def find_new_commits
if branch_added?
@commits = []

View File

@ -130,6 +130,20 @@ class SystemNoteService
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when 'merge when build succeeds' is executed
def self.merge_when_build_succeeds(noteable, project, author, last_commit)
body = "Enabled an automatic merge when the build for #{last_commit.to_reference(project)} succeeds"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when 'merge when build succeeds' is canceled
def self.cancel_merge_when_build_succeeds(noteable, project, author)
body = "Canceled the automatic merge"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when the title of a Noteable is changed
#
# noteable - Noteable object that responds to `title`

View File

@ -14,11 +14,11 @@
.form-group.project-visibility-level-holder
= f.label :default_project_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: 'Project')
= render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project)
.form-group.project-visibility-level-holder
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
.col-sm-10
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: 'Snippet')
= render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: PersonalSnippet)
.form-group
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
.col-sm-10

View File

@ -32,7 +32,7 @@
Files
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches releases)) do
= nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
= icon('history fw')
%span
@ -46,13 +46,6 @@
Builds
%span.count.builds_counter= @project.ci_builds.running_or_pending.count(:all)
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do
= icon('code-fork fw')
%span
Network
- 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
@ -118,3 +111,10 @@
= icon('cogs fw')
%span
Settings
-# Global shortcut to network page for compatibility
- if project_nav_tab? :network
%li.hidden
= link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do
Network

View File

@ -1,5 +1,4 @@
.btn-group.tree-btn-group
= edit_blob_link(@project, @ref, @path)
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
class: 'btn btn-sm', target: '_blank'
-# only show normal/blame view links for text files
@ -12,11 +11,16 @@
class: 'btn btn-sm' unless @blob.empty?
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id),
class: 'btn btn-sm'
- if @ref != @commit.sha
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
- if can_delete_or_replace?(@blob)
- if blob_editable?(@blob)
.btn-group{ role: "group" }
= edit_blob_link(@project, @ref, @path)
%button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
%button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
- elsif !on_top_of_branch?
.btn-group{ role: "group" }
%button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on a branch.", data: {container: 'body'}} Edit
%button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on a branch.", data: {container: 'body'}} Replace
%button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on a branch.", data: {container: 'body'}} Delete

View File

@ -6,7 +6,7 @@
%div#tree-holder.tree-holder
= render 'blob', blob: @blob
- if can_delete_or_replace?(@blob)
- if blob_editable?(@blob)
= render 'projects/blob/remove'
- title = "Replace #{@blob.name}"

View File

@ -12,17 +12,20 @@
%li{class: ('active' if @scope.nil?)}
= link_to project_builds_path(@project) do
Running
%span.badge.js-running-count= @all_builds.running_or_pending.count(:id)
%span.badge.js-running-count
= number_with_delimiter(@all_builds.running_or_pending.count(:id))
%li{class: ('active' if @scope == 'finished')}
= link_to project_builds_path(@project, scope: :finished) do
Finished
%span.badge.js-running-count= @all_builds.finished.count(:id)
%span.badge.js-running-count
= number_with_delimiter(@all_builds.finished.count(:id))
%li{class: ('active' if @scope == 'all')}
= link_to project_builds_path(@project, scope: :all) do
All
%span.badge.js-totalbuilds-count= @all_builds.count(:id)
%span.badge.js-totalbuilds-count
= number_with_delimiter(@all_builds.count(:id))
.gray-content-block
#{(@scope || 'running').capitalize} builds from this project

View File

@ -0,0 +1,67 @@
.gray-content-block.middle-block
.pull-right
- if @ci_project && can?(current_user, :manage_builds, @ci_commit.gl_project)
- if @ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
- if @ci_commit.builds.running_or_pending.any?
= link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
.oneline
= pluralize @statuses.count(:id), "build"
- if defined?(link_to_commit) && link_to_commit
for commit
= link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: "monospace"
- if @ci_commit.duration > 0
in
= time_interval_in_words @ci_commit.duration
- if @ci_commit.yaml_errors.present?
.bs-callout.bs-callout-danger
%h4 Found errors in your .gitlab-ci.yml:
%ul
- @ci_commit.yaml_errors.split(",").each do |error|
%li= error
- if @ci_commit.gl_project.builds_enabled? && !@ci_commit.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @ci_project && @ci_project.coverage_enabled?
%th Coverage
%th
- @ci_commit.refs.each do |ref|
= render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered,
locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true }
- if @ci_commit.retried.any?
.gray-content-block.second-block
Retried builds
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @ci_project && @ci_project.coverage_enabled?
%th Coverage
%th
= render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried,
locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true }

View File

@ -6,4 +6,4 @@
= nav_link(path: 'commit#builds') do
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Builds
%span.badge= @builds.count(:id)
%span.badge= @statuses.count

View File

@ -3,70 +3,4 @@
= render "commit_box"
= render "ci_menu"
- if @ci_commit.yaml_errors.present?
.bs-callout.bs-callout-danger
%h4 Found errors in your .gitlab-ci.yml:
%ul
- @ci_commit.yaml_errors.split(",").each do |error|
%li= error
- unless @ci_commit.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
.gray-content-block.second-block
Latest builds
.pull-right
- if @ci_commit.duration > 0
%i.fa.fa-time
#{time_interval_in_words @ci_commit.duration}
&nbsp;
- if @ci_project && current_user && can?(current_user, :manage_builds, @project)
- if @ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-primary', method: :post
- if @ci_commit.builds.running_or_pending.any?
= link_to "Cancel running", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger', method: :post
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @ci_project && @ci_project.coverage_enabled?
%th Coverage
%th
- @ci_commit.refs.each do |ref|
= render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered,
locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true }
- if @ci_commit.retried.any?
.gray-content-block.second-block
Retried builds
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @ci_project && @ci_project.coverage_enabled?
%th Coverage
%th
= render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried,
locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true }
= render "builds"

View File

@ -3,6 +3,11 @@
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
Commits
%span.badge= number_with_delimiter(@repository.commit_count)
= nav_link(controller: %w(network)) do
= link_to namespace_project_network_path(@project.namespace, @project, current_ref) do
Network
= nav_link(controller: :compare) do
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
Compare

View File

@ -1,4 +1,4 @@
.alert_holder
= content_for :flash_message do
- if current_user && can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
= render 'shared/no_password'

View File

@ -17,7 +17,7 @@
.input-group.cross-project-reference
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
= cross_project_reference(@project, @issue)
= clipboard_button(clipboard_target: '#cross-project-reference')
= clipboard_button(clipboard_target: 'span#cross-project-reference')
.row
%section.col-md-9

View File

@ -6,23 +6,26 @@
.issue-title
%span.issue-title-text
= link_to_gfm issue.title, issue_path(issue), class: "row_title"
.pull-right.light
%ul.controls.light
- if issue.closed?
%span
%li
CLOSED
- if issue.assignee
= link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
%li
= link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
- note_count = issue.notes.user.count
- if note_count > 0
&nbsp;
= link_to issue_path(issue) + "#notes" do
= icon('comments')
= note_count
%li
= link_to issue_path(issue) + "#notes" do
= icon('comments')
= note_count
- else
&nbsp;
= link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
= icon('comments')
= 0
%li
= link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
= icon('comments')
= note_count
.issue-info
#{issue.to_reference} &middot;

View File

@ -17,7 +17,7 @@
.input-group.cross-project-reference
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
= cross_project_reference(@project, @merge_request)
= clipboard_button(clipboard_target: '#cross-project-reference')
= clipboard_button(clipboard_target: 'span#cross-project-reference')
.row
%section.col-md-9

View File

@ -1,33 +1,41 @@
- ci_commit = merge_request.ci_commit
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
%span.merge-request-title-text
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
.pull-right.light
- if ci_commit
= render_ci_status(ci_commit)
%ul.controls.light
- if merge_request.merged?
%span
%li
= icon('check')
MERGED
- elsif merge_request.closed?
%span
%li
= icon('ban')
CLOSED
- note_count = merge_request.mr_and_commit_notes.user.count
- if merge_request.ci_commit
%li
= render_ci_status(merge_request.ci_commit)
- if merge_request.open? && merge_request.broken?
%li
= link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: {container: 'body'} do
= icon('exclamation-triangle')
- if merge_request.assignee
&nbsp;
= link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
%li
= link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
- note_count = merge_request.mr_and_commit_notes.user.count
- if note_count > 0
&nbsp;
= link_to merge_request_path(merge_request) + "#notes" do
= icon('comments')
= note_count
%li
= link_to merge_request_path(merge_request) + "#notes" do
= icon('comments')
= note_count
- else
&nbsp;
= link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
= icon('comments')
= 0
%li
= link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
= icon('comments')
= note_count
.merge-request-info
\##{merge_request.iid} &middot;

View File

@ -20,13 +20,18 @@
.mr-compare.merge-request
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
%li.commits-tab
= link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
= link_to url_for(params), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
Commits
%span.badge= @commits.size
%li.diffs-tab.active
= link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
= link_to url_for(params), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
Changes
%span.badge= @diffs.size
- if @ci_commit
%li.builds-tab.active
= link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do
Builds
%span.badge= @statuses.size
.tab-content
#commits.commits.tab-pane
@ -42,6 +47,9 @@
.alert.alert-danger
%h4 This comparison includes a huge diff.
%p To preserve performance the line changes are not shown.
- if @ci_commit
#builds.builds.tab-pane
= render "projects/merge_requests/show/builds"
:javascript
$('.assign-to-me-link').on('click', function(e){

View File

@ -26,8 +26,7 @@
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
.normal
%span Request to merge
%span.label-branch
= source_branch_with_namespace(@merge_request)
%span.label-branch= source_branch_with_namespace(@merge_request)
%span into
= link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
= @merge_request.target_branch
@ -44,17 +43,22 @@
- if @commits.present?
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do
Discussion
%span.badge= @merge_request.mr_and_commit_notes.user.count
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
Commits
%span.badge= @commits.size
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do
Changes
%span.badge= @merge_request.diffs.size
- if @ci_commit
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do
Builds
%span.badge= @statuses.size
.tab-content
#notes.notes.tab-pane.voting_notes
@ -63,6 +67,8 @@
- # This tab is always loaded via AJAX
#diffs.diffs.tab-pane
- # This tab is always loaded via AJAX
#builds.builds.tab-pane
- # This tab is always loaded via AJAX
.mr-loading-status
= spinner

View File

@ -0,0 +1,2 @@
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/accept'))}");

View File

@ -1,6 +1,10 @@
- if @status
- case @status
- when :success
:plain
merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
- when :merge_when_build_succeeds
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_build_succeeds'))}");
- else
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}");

View File

@ -0,0 +1 @@
= render "projects/commit/builds", link_to_commit: true

View File

@ -1,29 +1,33 @@
- if @ci_commit
- status = @ci_commit.status
.mr-widget-heading
.ci_widget{class: "ci-#{status}"}
.ci_widget{class: "ci-#{@ci_commit.status}"}
= ci_status_icon(@ci_commit)
%span CI build #{status}
for #{@merge_request.last_commit_short_sha}.
%span
Build
= ci_status_label(@ci_commit)
for
= succeed "." do
= link_to @ci_commit.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @ci_commit.sha), class: "monospace"
%span.ci-coverage
= link_to "View build details", ci_status_path(@ci_commit)
= link_to "View details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'}
- elsif @merge_request.has_ci?
- # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
- # Remove in later versions when services like Jenkins will set CI status via Commit status API
.mr-widget-heading
- [:success, :skipped, :canceled, :failed, :running, :pending].each do |status|
- %w[success skipped canceled failed running pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"}
- if status == :success
- status = "passed"
= icon("check-circle")
- else
= icon("circle")
%span CI build #{status}
for #{@merge_request.last_commit_short_sha}.
= ci_icon_for_status(status)
%span
CI build
= ci_label_for_status(status)
for
- commit = @merge_request.last_commit
= succeed "." do
= link_to commit.short_id, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, commit), class: "monospace"
%span.ci-coverage
- if ci_build_details_path(@merge_request)
= link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
- if details_path = ci_build_details_path(@merge_request)
= link_to "View details", details_path, :"data-no-turbolink" => "data-no-turbolink"
.ci_widget
= icon("spinner spin")

View File

@ -14,7 +14,7 @@
= @merge_request.target_branch
The source branch has been removed.
- elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch)
- elsif @merge_request.can_remove_source_branch?(current_user)
.remove_source_branch_widget
%p
= succeed '.' do
@ -50,5 +50,3 @@
$('.remove_source_branch_in_progress').hide();
$('.remove_source_branch_widget.failed').show();
});

View File

@ -13,6 +13,8 @@
= render 'projects/merge_requests/widget/open/conflicts'
- elsif @merge_request.work_in_progress?
= render 'projects/merge_requests/widget/open/wip'
- elsif @merge_request.merge_when_build_succeeds?
= 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.can_be_merged?

View File

@ -3,26 +3,60 @@
= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f|
= hidden_field_tag :authenticity_token, form_authenticity_token
.accept-merge-holder.clearfix.js-toggle-container
.accept-action
= f.button class: "btn btn-create accept_merge_request#{status_class}" do
Accept Merge Request
- if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
.accept-control.checkbox
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
= check_box_tag :should_remove_source_branch
Remove source branch
.accept-control.right
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
= icon('edit')
Modify commit message
.js-toggle-content.hide.prepend-top-20
.clearfix
.accept-action
- if @ci_commit && @ci_commit.active?
%span.btn-group
= link_to "#", class: "btn btn-create merge_when_build_succeeds" do
Merge When Build Succeeds
%a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%span.caret
%span.sr-only
Select Merge Moment
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li
= link_to "#", class: "merge_when_build_succeeds" do
= icon('check fw')
Merge When Build Succeeds
%li
= link_to "#", class: "accept_merge_request" do
= icon('warning fw')
Merge Immediately
- else
= f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do
Accept Merge Request
- if @merge_request.can_remove_source_branch?(current_user)
.accept-control.checkbox
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
= check_box_tag :should_remove_source_branch
Remove source branch
.accept-control.right
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
= icon('edit')
Modify commit message
.js-toggle-content.hide.prepend-top-default
= render 'shared/commit_message_container', params: params,
text: @merge_request.merge_commit_message,
rows: 14, hint: true
= hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
:javascript
$('.accept-mr-form').on('ajax:before', function() {
var btn = $('.accept_merge_request');
btn.disable();
btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
$('.accept_merge_request').on('click', function() {
$(this).html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
});
$('.accept-mr-form').on('ajax:send', function() {
$(".accept-mr-form :input").disable();
});
$('a.accept_merge_request').on('click', function(e) {
e.preventDefault();
$(this).closest("form").submit();
});
$('a.merge_when_build_succeeds').on('click', function(e) {
e.preventDefault();
$("#merge_when_build_succeeds").val("1");
$(this).closest("form").submit();
});

View File

@ -0,0 +1,26 @@
%h4
Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)}
to be merged automatically when the build succeeds.
%div
- should_remove_source_branch = @merge_request.merge_params["should_remove_source_branch"].present?
%p
= succeed '.' do
The changes will be merged into
%span.label-branch= @merge_request.target_branch
- if should_remove_source_branch
The source branch will be removed.
- else
The source branch will not be removed.
- remove_source_branch_button = @merge_request.can_remove_source_branch?(current_user) && !should_remove_source_branch
- user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
- if remove_source_branch_button || user_can_cancel_automatic_merge
.clearfix.prepend-top-10
- if remove_source_branch_button
= link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do
= icon('times')
Remove Source Branch When Merged
- if user_can_cancel_automatic_merge
= link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-warning btn-sm" do
Cancel Automatic Merge

View File

@ -1,4 +1,4 @@
.gray-content-block.top-block.append-bottom-default
.gray-content-block.append-bottom-default
.tree-ref-holder
= render partial: 'shared/ref_switcher', locals: {destination: 'graph'}

View File

@ -1,5 +1,6 @@
- page_title "Network", @ref
= header_title project_title(@project, "Network", namespace_project_network_path(@project.namespace, @project, current_ref))
= render "projects/commits/header_title"
= render "projects/commits/head"
= render "head"
.project-network
.controls

View File

@ -10,7 +10,7 @@
= icon('pencil-square-o')
Manage group members
%ul.content-list
- members.each do |member|
- members.limit(20).each do |member|
= render 'groups/group_members/group_member', member: member, show_controls: false
- if members.count > 20
%li

View File

@ -30,3 +30,7 @@
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
= icon('folder fw')
New directory
- elsif !on_top_of_branch?
%li
%span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch.", data: {container: 'body'}}
= icon('plus')

View File

@ -4,7 +4,7 @@
.form-group.branch
= label_tag 'new_branch', 'Target branch', class: 'control-label'
.col-sm-10
= text_field_tag 'new_branch', @new_branch || @ref, required: true, class: "form-control js-new-branch"
= text_field_tag 'new_branch', @new_branch || tree_edit_branch, required: true, class: "form-control js-new-branch"
.js-create-merge-request-container
.checkbox

View File

@ -1,6 +1,6 @@
.issuable-details
.page-title
.snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level), data: { container: 'body' }}
.snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }}
= visibility_level_icon(@snippet.visibility_level, fw: false)
= visibility_level_label(@snippet.visibility_level)
Snippet ##{@snippet.id}

View File

@ -8,16 +8,7 @@ class MergeWorker
current_user = User.find(current_user_id)
merge_request = MergeRequest.find(merge_request_id)
result = MergeRequests::MergeService.new(merge_request.target_project, current_user).
execute(merge_request, params[:commit_message])
if result[:status] == :success && params[:should_remove_source_branch].present?
DeleteBranchService.new(merge_request.source_project, current_user).
execute(merge_request.source_branch)
merge_request.source_project.repository.expire_branch_names
end
result
MergeRequests::MergeService.new(merge_request.target_project, current_user, params).
execute(merge_request)
end
end

View File

@ -76,7 +76,7 @@ production: &base
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com.
# issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
# issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)'
## Default project features settings
default_projects_features:

View File

@ -60,7 +60,7 @@ Devise.setup do |config|
# It will change confirmation, password recovery and other workflows
# to behave the same regardless if the e-mail provided was right or wrong.
# Does not affect registerable.
# config.paranoid = true
config.paranoid = true
# ==> Configuration for :database_authenticatable
# For bcrypt, this is the cost for hashing the password and defaults to 10. If

View File

@ -30,7 +30,6 @@ en:
success: "Successfully authenticated from %{kind} account."
passwords:
no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided."
recently_reset: "Instructions about how to reset your password have already been sent recently. Please wait a few minutes to try again."
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes."
send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes."
updated: "Your password has been changed successfully. You are now signed in."

View File

@ -570,10 +570,12 @@ Rails.application.routes.draw do
resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do
member do
get :diffs
get :commits
post :merge
get :diffs
get :builds
get :merge_check
post :merge
post :cancel_merge_when_build_succeeds
get :ci_status
post :toggle_subscription
end

View File

@ -1,6 +1,6 @@
class InitSchema < ActiveRecord::Migration
def up
create_table "events", force: true do |t|
t.string "target_type"
t.integer "target_id"
@ -12,14 +12,14 @@ class InitSchema < ActiveRecord::Migration
t.integer "action"
t.integer "author_id"
end
add_index "events", ["action"], name: "index_events_on_action", using: :btree
add_index "events", ["author_id"], name: "index_events_on_author_id", using: :btree
add_index "events", ["created_at"], name: "index_events_on_created_at", using: :btree
add_index "events", ["project_id"], name: "index_events_on_project_id", using: :btree
add_index "events", ["target_id"], name: "index_events_on_target_id", using: :btree
add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree
create_table "issues", force: true do |t|
t.string "title"
t.integer "assignee_id"
@ -33,7 +33,7 @@ class InitSchema < ActiveRecord::Migration
t.text "description"
t.integer "milestone_id"
end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree
add_index "issues", ["closed"], name: "index_issues_on_closed", using: :btree
@ -41,7 +41,7 @@ class InitSchema < ActiveRecord::Migration
add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree
add_index "issues", ["title"], name: "index_issues_on_title", using: :btree
create_table "keys", force: true do |t|
t.integer "user_id"
t.datetime "created_at"
@ -51,11 +51,11 @@ class InitSchema < ActiveRecord::Migration
t.string "identifier"
t.integer "project_id"
end
add_index "keys", ["identifier"], name: "index_keys_on_identifier", using: :btree
add_index "keys", ["project_id"], name: "index_keys_on_project_id", using: :btree
add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree
create_table "merge_requests", force: true do |t|
t.string "target_branch", null: false
t.string "source_branch", null: false
@ -66,13 +66,13 @@ class InitSchema < ActiveRecord::Migration
t.boolean "closed", default: false, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.text "st_commits", limit: 2147483647
t.text "st_diffs", limit: 2147483647
t.text "st_commits"
t.text "st_diffs"
t.boolean "merged", default: false, null: false
t.integer "state", default: 1, null: false
t.integer "milestone_id"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
add_index "merge_requests", ["closed"], name: "index_merge_requests_on_closed", using: :btree
@ -82,7 +82,7 @@ class InitSchema < ActiveRecord::Migration
add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
create_table "milestones", force: true do |t|
t.string "title", null: false
t.integer "project_id", null: false
@ -92,10 +92,10 @@ class InitSchema < ActiveRecord::Migration
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
create_table "namespaces", force: true do |t|
t.string "name", null: false
t.string "path", null: false
@ -104,12 +104,12 @@ class InitSchema < ActiveRecord::Migration
t.datetime "updated_at"
t.string "type"
end
add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree
add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree
add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
create_table "notes", force: true do |t|
t.text "note"
t.string "noteable_type"
@ -122,13 +122,13 @@ class InitSchema < ActiveRecord::Migration
t.string "commit_id"
t.integer "noteable_id"
end
add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree
add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
create_table "projects", force: true do |t|
t.string "name"
t.string "path"
@ -144,17 +144,17 @@ class InitSchema < ActiveRecord::Migration
t.boolean "wiki_enabled", default: true, null: false
t.integer "namespace_id"
end
add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
add_index "projects", ["owner_id"], name: "index_projects_on_owner_id", using: :btree
create_table "protected_branches", force: true do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "services", force: true do |t|
t.string "type"
t.string "title"
@ -165,9 +165,9 @@ class InitSchema < ActiveRecord::Migration
t.boolean "active", default: false, null: false
t.string "project_url"
end
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
create_table "snippets", force: true do |t|
t.string "title"
t.text "content"
@ -178,11 +178,11 @@ class InitSchema < ActiveRecord::Migration
t.string "file_name"
t.datetime "expires_at"
end
add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree
add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree
add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
create_table "taggings", force: true do |t|
t.integer "tag_id"
t.integer "taggable_id"
@ -192,14 +192,14 @@ class InitSchema < ActiveRecord::Migration
t.string "context"
t.datetime "created_at"
end
add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "tags", force: true do |t|
t.string "name"
end
create_table "user_team_project_relationships", force: true do |t|
t.integer "project_id"
t.integer "user_team_id"
@ -207,7 +207,7 @@ class InitSchema < ActiveRecord::Migration
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "user_team_user_relationships", force: true do |t|
t.integer "user_id"
t.integer "user_team_id"
@ -216,7 +216,7 @@ class InitSchema < ActiveRecord::Migration
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "user_teams", force: true do |t|
t.string "name"
t.string "path"
@ -224,7 +224,7 @@ class InitSchema < ActiveRecord::Migration
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
@ -255,7 +255,7 @@ class InitSchema < ActiveRecord::Migration
t.string "provider"
t.string "username"
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
add_index "users", ["blocked"], name: "index_users_on_blocked", using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
@ -263,7 +263,7 @@ class InitSchema < ActiveRecord::Migration
add_index "users", ["name"], name: "index_users_on_name", using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
add_index "users", ["username"], name: "index_users_on_username", using: :btree
create_table "users_projects", force: true do |t|
t.integer "user_id", null: false
t.integer "project_id", null: false
@ -271,11 +271,11 @@ class InitSchema < ActiveRecord::Migration
t.datetime "updated_at"
t.integer "project_access", default: 0, null: false
end
add_index "users_projects", ["project_access"], name: "index_users_projects_on_project_access", using: :btree
add_index "users_projects", ["project_id"], name: "index_users_projects_on_project_id", using: :btree
add_index "users_projects", ["user_id"], name: "index_users_projects_on_user_id", using: :btree
create_table "web_hooks", force: true do |t|
t.string "url"
t.integer "project_id"
@ -284,7 +284,7 @@ class InitSchema < ActiveRecord::Migration
t.string "type", default: "ProjectHook"
t.integer "service_id"
end
create_table "wikis", force: true do |t|
t.string "title"
t.text "content"
@ -294,10 +294,10 @@ class InitSchema < ActiveRecord::Migration
t.string "slug"
t.integer "user_id"
end
add_index "wikis", ["project_id"], name: "index_wikis_on_project_id", using: :btree
add_index "wikis", ["slug"], name: "index_wikis_on_slug", using: :btree
end
def down

View File

@ -1,12 +1,21 @@
class CreateMergeRequestDiffs < ActiveRecord::Migration
def change
def up
create_table :merge_request_diffs do |t|
t.string :state, null: false, default: 'collected'
t.text :st_commits, null: true, limit: 2147483647
t.text :st_diffs, null: true, limit: 2147483647
t.text :st_commits, null: true
t.text :st_diffs, null: true
t.integer :merge_request_id, null: false
t.timestamps
end
if ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/
change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647
change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
end
end
def down
drop_table :merge_request_diffs
end
end

View File

@ -1,5 +1,7 @@
class MigrateToNewShell < ActiveRecord::Migration
def change
return if Rails.env.test?
gitlab_shell_path = Gitlab.config.gitlab_shell.path
if system("#{gitlab_shell_path}/bin/create-hooks")
puts 'Repositories updated with new hooks'

View File

@ -0,0 +1,7 @@
class AddMergeWhenBuildSucceedsToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :merge_params, :text
add_column :merge_requests, :merge_when_build_succeeds, :boolean, default: false, null: false
add_column :merge_requests, :merge_user_id, :integer
end
end

View File

@ -476,9 +476,9 @@ ActiveRecord::Schema.define(version: 20151203162133) do
add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree
create_table "merge_requests", force: :cascade do |t|
t.string "target_branch", null: false
t.string "source_branch", null: false
t.integer "source_project_id", null: false
t.string "target_branch", null: false
t.string "source_branch", null: false
t.integer "source_project_id", null: false
t.integer "author_id"
t.integer "assignee_id"
t.string "title"
@ -487,13 +487,16 @@ ActiveRecord::Schema.define(version: 20151203162133) do
t.integer "milestone_id"
t.string "state"
t.string "merge_status"
t.integer "target_project_id", null: false
t.integer "target_project_id", null: false
t.integer "iid"
t.text "description"
t.integer "position", default: 0
t.integer "position", default: 0
t.datetime "locked_at"
t.integer "updated_by_id"
t.string "merge_error"
t.text "merge_params"
t.boolean "merge_when_build_succeeds", default: false, null: false
t.integer "merge_user_id"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree

View File

@ -24,9 +24,21 @@
- [Using Docker Images](ci/docker/using_docker_images.md)
- [Using Docker Build](ci/docker/using_docker_build.md)
- [Using Variables](ci/variables/README.md)
- [Using SSH keys](ci/ssh_keys/README.md)
- [User permissions](ci/permissions/README.md)
- [API](ci/api/README.md)
### CI Languages
+ [Testing PHP](ci/languages/php.md)
### CI Services
+ [Using MySQL](ci/services/mysql.md)
+ [Using PostgreSQL](ci/services/postgres.md)
+ [Using Redis](ci/services/redis.md)
+ [Using Other Services](ci/docker/using_docker_images.md#how-to-use-other-images-as-services)
### CI Examples
- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)

View File

@ -335,9 +335,57 @@ PUT /projects/:id/merge_request/:merge_request_id/merge
Parameters:
- `id` (required) - The ID of a project
- `merge_request_id` (required) - ID of MR
- `merge_commit_message` (optional) - Custom merge commit message
- `id` (required) - The ID of a project
- `merge_request_id` (required) - ID of MR
- `merge_commit_message` (optional) - Custom merge commit message
- `should_remove_source_branch` (optional) - if `true` removes the source branch
- `merged_when_build_succeeds` (optional) - if `true` the MR is merge when the build succeeds
```json
{
"id": 1,
"target_branch": "master",
"source_branch": "test1",
"project_id": 3,
"title": "test1",
"state": "merged",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
}
}
```
## Cancel Merge When Build Succeeds
If successful you'll get `200 OK`.
If you don't have permissions to accept this merge request - you'll get a 401
If the merge request is already merged or closed - you get 405 and error message 'Method Not Allowed'
In case the merge request is not set to be merged when the build succeeds, you'll also get a 406 error.
```
PUT /projects/:id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds
```
Parameters:
- `id` (required) - The ID of a project
- `merge_request_id` (required) - ID of MR
```json
{

View File

@ -398,7 +398,6 @@ Parameters:
- `user_id` (required) - user_id of owner
- `name` (required) - new project name
- `description` (optional) - short project description
- `default_branch` (optional) - 'master' by default
- `issues_enabled` (optional)
- `merge_requests_enabled` (optional)
- `builds_enabled` (optional)

View File

@ -9,6 +9,18 @@
+ [Using Docker Images](docker/using_docker_images.md)
+ [Using Docker Build](docker/using_docker_build.md)
+ [Using Variables](variables/README.md)
+ [Using SSH keys](ssh_keys/README.md)
### Languages
+ [Testing PHP](languages/php.md)
### Services
+ [Using MySQL](services/mysql.md)
+ [Using PostgreSQL](services/postgres.md)
+ [Using Redis](services/redis.md)
+ [Using Other Services](docker/using_docker_images.md#how-to-use-other-images-as-services)
### Examples

View File

@ -1,19 +1,29 @@
# Using Docker Images
GitLab CI can use [Docker Engine](https://www.docker.com/) to build projects.
Docker is an open-source project that allows to use predefined images to run applications
in independent "containers" that are run within a single Linux instance.
[Docker Hub](https://registry.hub.docker.com/) have rich database of built images that can be used to build applications.
GitLab CI in conjuction with [GitLab Runner](../runners/README.md) can use
[Docker Engine](https://www.docker.com/) to test and build any application.
Docker when used with GitLab CI runs each build in separate and isolated container using predefined image and always from scratch.
It makes it easier to have simple and reproducible build environment that can also be run on your workstation.
This allows you to test all commands from your shell, rather than having to test them on a CI server.
Docker is an open-source project that allows you to use predefined images to
run applications in independent "containers" that are run within a single Linux
instance. [Docker Hub][hub] has a rich database of prebuilt images that can be
used to test and build your applications.
### Register Docker runner
To use GitLab Runner with Docker you need to register new runner to use `docker` executor:
Docker, when used with GitLab CI, runs each build in a separate and isolated
container using the predefined image that is set up in
[`.gitlab-ci.yml`](../yaml/README.md).
This makes it easier to have a simple and reproducible build environment that
can also run on your workstation. The added benefit is that you can test all
the commands that we will explore later from your shell, rather than having to
test them on a dedicated CI server.
## Register docker runner
To use GitLab Runner with docker you need to register a new runner to use the
`docker` executor:
```bash
gitlab-ci-multi-runner register \
gitlab-runner register \
--url "https://gitlab.com/" \
--registration-token "PROJECT_REGISTRATION_TOKEN" \
--description "docker-ruby-2.1" \
@ -23,101 +33,79 @@ gitlab-ci-multi-runner register \
--docker-mysql latest
```
**The registered runner will use `ruby:2.1` image and will run two services (`postgres:latest` and `mysql:latest`) that will be accessible for time of the build.**
The registered runner will use the `ruby:2.1` docker image and will run two
services, `postgres:latest` and `mysql:latest`, both of which will be
accessible during the build process.
### What is image?
The image is the name of any repository that is present in local Docker Engine or any repository that can be found at [Docker Hub](https://registry.hub.docker.com/).
For more information about the image and Docker Hub please read the [Docker Fundamentals](https://docs.docker.com/introduction/understanding-docker/).
## What is image
### What is service?
Service is just another image that is run for time of your build and is linked to your build. This allows you to access the service image during build time.
The service image can run any application, but most common use case is to run some database container, ie.: `mysql`.
It's easier and faster to use existing image, run it as additional container than install `mysql` every time project is built.
The `image` keyword is the name of the docker image that is present in the
local Docker Engine (list all images with `docker images`) or any image that
can be found at [Docker Hub][hub]. For more information about images and Docker
Hub please read the [Docker Fundamentals][] documentation.
#### How is service linked to the build?
There's good document that describes how Docker linking works: [Linking containers together](https://docs.docker.com/userguide/dockerlinks/).
To summarize: if you add `mysql` as service to your application, the image will be used to create container that is linked to build container.
The service container for MySQL will be accessible under hostname `mysql`.
So, **to access your database service you have to connect to host: `mysql` instead of socket or `localhost`**.
In short, with `image` we refer to the docker image, which will be used to
create a container on which your build will run.
### How to use other images as services?
You are not limited to have only database services.
You can hand modify `config.toml` to add any image as service found at [Docker Hub](https://registry.hub.docker.com/).
Look for `[runners.docker]` section:
```
[runners.docker]
image = "ruby:2.1"
services = ["mysql:latest", "postgres:latest"]
```
## What is service
For example you need `wordpress` instance to test some API integration with `Wordpress`.
You can for example use this image: [tutum/wordpress](https://registry.hub.docker.com/u/tutum/wordpress/).
This is image that have fully preconfigured `wordpress` and have `MySQL` server built-in:
```
[runners.docker]
image = "ruby:2.1"
services = ["mysql:latest", "postgres:latest", "tutum/wordpress:latest"]
```
The `services` keyword defines just another docker image that is run during
your build and is linked to the docker image that the `image` keyword defines.
This allows you to access the service image during build time.
Next time when you run your application the `tutum/wordpress` will be started
and you will have access to it from your build container under hostname: `tutum__wordpress`.
The service image can run any application, but the most common use case is to
run a database container, eg. `mysql`. It's easier and faster to use an
existing image and run it as an additional container than install `mysql` every
time the project is built.
Alias hostname for the service is made from the image name:
1. Everything after `:` is stripped,
2. '/' is replaced with `__`.
You can see some widely used services examples in the relevant documentation of
[CI services examples](../services/README.md).
### Configuring services
Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment.
### How is service linked to the build
GitLab Runner 0.5.0 and up passes all YAML-defined variables to created service containers.
To better understand how the container linking works, read
[Linking containers together](https://docs.docker.com/userguide/dockerlinks/).
1. To configure database name for [postgres](https://registry.hub.docker.com/u/library/postgres/) service,
you need to set POSTGRES_DB.
To summarize, if you add `mysql` as service to your application, the image will
then be used to create a container that is linked to the build container.
```yaml
services:
- postgres
variables:
POSTGRES_DB: gitlab
```
The service container for MySQL will be accessible under the hostname `mysql`.
So, in order to access your database service you have to connect to the host
named `mysql` instead of a socket or `localhost`.
1. To use [mysql](https://registry.hub.docker.com/u/library/mysql/) service with empty password for time of build,
you need to set MYSQL_ALLOW_EMPTY_PASSWORD.
## Overwrite image and services
```yaml
services:
- mysql
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
```
See [How to use other images as services](#how-to-use-other-images-as-services).
For other possible configuration variables check the
https://registry.hub.docker.com/u/library/mysql/ or https://registry.hub.docker.com/u/library/postgres/
or README page for any other Docker image.
## How to use other images as services
**Note: All variables will passed to all service containers. It's not designed to distinguish which variable should go where.**
You are not limited to have only database services. You can add as many
services you need to `.gitlab-ci.yml` or manually modify `config.toml`.
Any image found at [Docker Hub][hub] can be used as a service.
### Overwrite image and services
It's possible to overwrite `docker-image` and specify services from `.gitlab-ci.yml`.
If you add to your YAML the `image` and the `services` these parameters
be used instead of the ones that were specified during runner's registration.
```
## Define image and services from `.gitlab-ci.yml`
You can simply define an image that will be used for all jobs and a list of
services that you want to use during build time.
```yaml
image: ruby:2.2
services:
- postgres:9.3
before_install:
before_script:
- bundle install
test:
script:
- bundle exec rake spec
```
It's possible to define image and service per-job:
```
before_install:
It is also possible to define different images and services per job:
```yaml
before_script:
- bundle install
test:2.1:
@ -135,34 +123,73 @@ test:2.2:
- bundle exec rake spec
```
#### How to enable overwriting?
To enable overwriting you have to **enable it first** (it's disabled by default for security reasons).
You can do that by hand modifying runner configuration: `config.toml`.
Please go to section where is `[runners.docker]` definition for your runner.
Add `allowed_images` and `allowed_services` to specify what images are allowed to be picked from `.gitlab-ci.yml`:
## Define image and services in `config.toml`
Look for the `[runners.docker]` section:
```
[runners.docker]
image = "ruby:2.1"
allowed_images = ["ruby:*", "python:*"]
allowed_services = ["mysql:*", "redis:*"]
```
This enables you to use in your `.gitlab-ci.yml` any image that matches above wildcards.
You will be able to pick only `ruby` and `python` images.
The same rule can be applied to limit services.
If you are courageous enough, you can make it fully open and accept everything:
```
[runners.docker]
image = "ruby:2.1"
allowed_images = ["*", "*/*"]
allowed_services = ["*", "*/*"]
services = ["mysql:latest", "postgres:latest"]
```
**It the feature is not enabled, or image isn't allowed the error message will be put into the build log.**
The image and services defined this way will be added to all builds run by
that runner.
## Accessing the services
Let's say that you need a Wordpress instance to test some API integration with
your application.
You can then use for example the [tutum/wordpress][] image in your
`.gitlab-ci.yml`:
```yaml
services:
- tutum/wordpress:latest
```
When the build is run, `tutum/wordpress` will be started and you will have
access to it from your build container under the hostname `tutum__wordpress`.
The alias hostname for the service is made from the image name following these
rules:
1. Everything after `:` is stripped
2. Backslash (`/`) is replaced with double underscores (`__`)
## Configuring services
Many services accept environment variables which allow you to easily change
database names or set account names depending on the environment.
GitLab Runner 0.5.0 and up passes all YAML-defined variables to the created
service containers.
For all possible configuration variables check the documentation of each image
provided in their corresponding Docker hub page.
*Note: All variables will be passed to all services containers. It's not
designed to distinguish which variable should go where.*
### PostgreSQL service example
See the specific documentation for
[using PostgreSQL as a service](../services/postgres.md).
### MySQL service example
See the specific documentation for
[using MySQL as a service](../services/mysql.md).
## How Docker integration works
Below is a high level overview of the steps performed by docker during build
time.
### How Docker integration works
1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`.
1. Create cache container to store all volumes as defined in `config.toml` and `Dockerfile` of build image (`ruby:2.1` as in above example).
1. Create cache container to store all volumes as defined in `config.toml` and
`Dockerfile` of build image (`ruby:2.1` as in above example).
1. Create build container and link any service container to build container.
1. Start build container and send build script to the container.
1. Run build script.
@ -171,33 +198,63 @@ If you are courageous enough, you can make it fully open and accept everything:
1. Check exit status of build script.
1. Remove build container and all created service containers.
### How to debug a build locally
1. Create a file with build script:
## How to debug a build locally
*Note: The following commands are run without root privileges. You should be
able to run docker with your regular user account.*
First start with creating a file named `build script`:
```bash
$ cat <<EOF > build_script
cat <<EOF > build_script
git clone https://gitlab.com/gitlab-org/gitlab-ci-multi-runner.git /builds/gitlab-org/gitlab-ci-multi-runner
cd /builds/gitlab-org/gitlab-ci-multi-runner
make <- or any other build step
make
EOF
```
1. Create service containers:
```
$ docker run -d -n service-mysql mysql:latest
$ docker run -d -n service-postgres postgres:latest
```
This will create two service containers (MySQL and PostgreSQL).
Here we use as an example the GitLab Runner repository which contains a
Makefile, so running `make` will execute the commands defined in the Makefile.
Your mileage may vary, so instead of `make` you could run the command which
is specific to your project.
1. Create a build container and execute script in its context:
```
$ docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script
```
This will create build container that has two service containers linked.
The build_script is piped using STDIN to bash interpreter which executes the build script in container.
Then create some service containers:
```
docker run -d -n service-mysql mysql:latest
docker run -d -n service-postgres postgres:latest
```
This will create two service containers, named `service-mysql` and
`service-postgres` which use the latest MySQL and PostgreSQL images
respectively. They will both run in the background (`-d`).
Finally, create a build container by executing the `build_script` file we
created earlier:
```
docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script
```
The above command will create a container named `build` that is spawned from
the `ruby:2.1` image and has two services linked to it. The `build_script` is
piped using STDIN to the bash interpreter which in turn executes the
`build_script` in the `build` container.
When you finish testing and no longer need the containers, you can remove them
with:
1. At the end remove all containers:
```
docker rm -f -v build service-mysql service-postgres
```
This will forcefully (the `-f` switch) remove build container and service containers
and all volumes (the `-v` switch) that were created with the container creation.
This will forcefully (`-f`) remove the `build` container, the two service
containers as well as all volumes (`-v`) that were created with the container
creation.
[Docker Fundamentals]: https://docs.docker.com/engine/introduction/understanding-docker/
[hub]: https://hub.docker.com/
[linking-containers]: https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/
[tutum/wordpress]: https://registry.hub.docker.com/u/tutum/wordpress/
[postgres-hub]: https://registry.hub.docker.com/u/library/postgres/
[mysql-hub]: https://registry.hub.docker.com/u/library/mysql/

View File

@ -0,0 +1,7 @@
### Languages
This is a list of languages you can test with GitLab CI. Each section has
comprehensive documentation and comes with a test repository hosted on
GitLab.com
+ [Testing PHP](php.md)

284
doc/ci/languages/php.md Normal file
View File

@ -0,0 +1,284 @@
# Testing PHP projects
This guide covers basic building instructions for PHP projects.
There are covered two cases: testing using the Docker executor and testing
using the Shell executor.
## Test PHP projects using the Docker executor
While it is possible to test PHP apps on any system, this would require manual
configuration from the developer. To overcome this we will be using the
official [PHP docker image][php-hub] that can be found in Docker Hub.
This will allow us to test PHP projects against different versions of PHP.
However, not everything is plug 'n' play, you still need to onfigure some
things manually.
As with every build, you need to create a valid `.gitlab-ci.yml` describing the
build environment.
Let's first specify the PHP image that will be used for the build process
(you can read more about what an image means in the Runner's lingo reading
about [Using Docker images](../docker/using_docker_images.md#what-is-image)).
Start by adding the image to your `.gitlab-ci.yml`:
```yaml
image: php:5.6
```
The official images are great, but they lack a few useful tools for testing.
We need to first prepare the build environment. A way to overcome this is to
create a script which installs all prerequisites prior the actual testing is
done.
Let's create a `ci/docker_install.sh` file in the root directory of our
repository with the following content:
```bash
#!/bin/bash
# We need to install dependencies only for Docker
[[ ! -e /.dockerinit ]] && exit 0
set -xe
# Install git (the php image doesn't have it) which is required by composer
apt-get update -yqq
apt-get install git -yqq
# Install phpunit, the tool that we will use for testing
curl -o /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar
chmod +x /usr/local/bin/phpunit
# Install mysql driver
# Here you can install any other extension that you need
docker-php-ext-install pdo_mysql
```
You might wonder what `docker-php-ext-install` is. In short, it is a script
provided by the official php docker image that you can use to easilly install
extensions. For more information read the the documentation at
<https://hub.docker.com/_/php/>.
Now that we created the script that contains all prerequisites for our build
environment, let's add it in `.gitlab-ci.yml`:
```yaml
...
before_script:
- bash ci/docker_install.sh > /dev/null
...
```
Last step, run the actual tests using `phpunit`:
```yaml
...
test:app:
script:
- phpunit --configuration phpunit_myapp.xml
...
```
Finally, commit your files and push them to GitLab to see your build succeeding
(or failing).
The final `.gitlab-ci.yml` should look similar to this:
```yaml
# Select image from https://hub.docker.com/_/php/
image: php:5.6
before_script:
# Install dependencies
- ci/docker_install.sh > /dev/null
test:app:
script:
- phpunit --configuration phpunit_myapp.xml
```
### Test against different PHP versions in Docker builds
Testing against multiple versions of PHP is super easy. Just add another job
with a different docker image version and the runner will do the rest:
```yaml
before_script:
# Install dependencies
- ci/docker_install.sh > /dev/null
# We test PHP5.6
test:5.6:
image: php:5.6
script:
- phpunit --configuration phpunit_myapp.xml
# We test PHP7.0 (good luck with that)
test:7.0:
image: php:7.0
script:
- phpunit --configuration phpunit_myapp.xml
```
### Custom PHP configuration in Docker builds
There are times where you will need to customise your PHP environment by
putting your `.ini` file into `/usr/local/etc/php/conf.d/`. For that purpose
add a `before_script` action:
```yaml
before_script:
- cp my_php.ini /usr/local/etc/php/conf.d/test.ini
```
Of course, `my_php.ini` must be present in the root directory of your repository.
## Test PHP projects using the Shell executor
The shell executor runs your builds in a terminal session on your server.
Thus, in order to test your projects you first need to make sure that all
dependencies are installed.
For example, in a VM running Debian 8 we first update the cache, then we
install `phpunit` and `php5-mysql`:
```bash
sudo apt-get update -y
sudo apt-get install -y phpunit php5-mysql
```
Next, add the following snippet to your `.gitlab-ci.yml`:
```yaml
test:app:
script:
- phpunit --configuration phpunit_myapp.xml
```
Finally, push to GitLab and let the tests begin!
### Test against different PHP versions in Shell builds
The [phpenv][] project allows you to easily manage different versions of PHP
each with its own config. This is specially usefull when testing PHP projects
with the Shell executor.
You will have to install it on your build machine under the `gitlab-runner`
user following [the upstream installation guide][phpenv-installation].
Using phpenv also allows to easily configure the PHP environment with:
```
phpenv config-add my_config.ini
```
*__Important note:__ It seems `phpenv/phpenv`
[is abandoned](https://github.com/phpenv/phpenv/issues/57). There is a fork
at [madumlao/phpenv](https://github.com/madumlao/phpenv) that tries to bring
the project back to life. [CHH/phpenv](https://github.com/CHH/phpenv) also
seems like a good alternative. Picking any of the mentioned tools will work
with the basic phpenv commands. Guiding you to choose the right phpenv is out
of the scope of this tutorial.*
### Install custom extensions
Since this is a pretty bare installation of the PHP environment, you may need
some extensions that are not currently present on the build machine.
To install additional extensions simply execute:
```bash
pecl install <extension>
```
It's not advised to add this to `.gitlab-ci.yml`. You should execute this
command once, only to setup the build environment.
## Extend your tests
### Using atoum
Instead of PHPUnit, you can use any other tool to run unit tests. For example
you can use [atoum](https://github.com/atoum/atoum):
```yaml
before_script:
- wget http://downloads.atoum.org/nightly/mageekguy.atoum.phar
test:atoum:
script:
- php mageekguy.atoum.phar
```
### Using Composer
The majority of the PHP projects use Composer for managing their PHP packages.
In order to execute Composer before running your tests, simply add the
following in your `.gitlab-ci.yml`:
```yaml
...
# Composer stores all downloaded packages in the vendor/ directory.
# Do not use the following if the vendor/ directory is commited to
# your git repository.
cache:
paths:
- vendor/
before_script:
# Install composer dependencies
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install
...
```
## Access private packages / dependencies
If your test suite needs to access a private repository, you need to configure
[the SSH keys](../ssh_keys/README.md) in order to be able to clone it.
## Use databases or other services
Most of the time you will need a running database in order for your tests to
run. If you are using the Docker executor you can leverage Docker's ability to
link to other containers. In GitLab Runner lingo, this can be achieved by
defining a `service`.
This functionality is covered in [the CI services](../services/README.md)
documentation.
## Testing things locally
With GitLab Runner 1.0 you can also test any changes locally. From your
terminal execute:
```bash
# Check using docker executor
gitlab-runner exec docker test:app
# Check using shell executor
gitlab-runner exec shell test:app
```
## Example project
We have set up an [Example PHP Project][php-example-repo] for your convenience
that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the build will begin.
[php-hub]: https://hub.docker.com/_/php/
[phpenv]: https://github.com/phpenv/phpenv
[phpenv-installation]: https://github.com/phpenv/phpenv#installation
[php-example-repo]: https://gitlab.com/gitlab-examples/php

View File

@ -0,0 +1,9 @@
## GitLab CI Services
GitLab CI uses the `services` keyword to define what docker containers should be
linked with your base image. Below is a list of examples you may use.
+ [Using MySQL](mysql.md)
+ [Using PostgreSQL](postgres.md)
+ [Using Redis](redis.md)
+ [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services)

View File

@ -0,0 +1,5 @@
## GitLab CI Services
+ [Using MySQL](mysql.md)
+ [Using PostgreSQL](postgres.md)
+ [Using Redis](redis.md)

118
doc/ci/services/mysql.md Normal file
View File

@ -0,0 +1,118 @@
# Using MySQL
As many applications depend on MySQL as their database, you will eventually
need it in order for your tests to run. Below you are guided how to do this
with the Docker and Shell executors of GitLab Runner.
## Use MySQL with the Docker executor
If you are using [GitLab Runner](../runners/README.md) with the Docker executor
you basically have everything set up already.
First, in your `.gitlab-ci.yml` add:
```yaml
services:
- mysql:latest
variables:
# Configure mysql environment variables (https://hub.docker.com/_/mysql/)
MYSQL_DATABASE: el_duderino
MYSQL_ROOT_PASSWORD: mysql_strong_password
```
And then configure your application to use the database, for example:
```yaml
Host: mysql
User: root
Password: mysql_strong_password
Database: el_duderino
```
If you are wondering why we used `mysql` for the `Host`, read more at
[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build).
You can also use any other docker image available on [Docker Hub][hub-mysql].
For example, to use MySQL 5.5 the service becomes `mysql:5.5`.
The `mysql` image can accept some environment variables. For more details
check the documentation on [Docker Hub][hub-mysql].
## Use MySQL with the Shell executor
You can also use MySQL on manually configured servers that are using
GitLab Runner with the Shell executor.
First install the MySQL server:
```bash
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
```
Pick a MySQL root password (can be anything), and type it twice when asked.
*Note: As a security measure you can run `mysql_secure_installation` to
remove anonymous users, drop the test database and disable remote logins with
the root user.*
The next step is to create a user, so login to MySQL as root:
```bash
mysql -u root -p
```
Then create a user (in our case `runner`) which will be used by your
application. Change `$password` in the command below to a real strong password.
*Note: Do not type `mysql>`, this is part of the MySQL prompt.*
```bash
mysql> CREATE USER 'runner'@'localhost' IDENTIFIED BY '$password';
```
Create the database:
```bash
mysql> CREATE DATABASE IF NOT EXISTS `el_duderino` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
```
Grant the necessary permissions on the database:
```bash
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES ON `el_duderino`.* TO 'runner'@'localhost';
```
If all went well you can now quit the database session:
```bash
mysql> \q
```
Now, try to connect to the newly created database to check that everything is
in place:
```bash
mysql -u runner -p -D el_duderino
```
As a final step, configure your application to use the database, for example:
```bash
Host: localhost
User: runner
Password: $password
Database: el_duderino
```
## Example project
We have set up an [Example MySQL Project][mysql-example-repo] for your
convenience that runs on [GitLab.com](https://gitlab.com) using our publicly
available [shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the build will begin.
[hub-mysql]: https://hub.docker.com/_/mysql/
[mysql-example-repo]: https://gitlab.com/gitlab-examples/mysql

114
doc/ci/services/postgres.md Normal file
View File

@ -0,0 +1,114 @@
# Using PostgreSQL
As many applications depend on PostgreSQL as their database, you will
eventually need it in order for your tests to run. Below you are guided how to
do this with the Docker and Shell executors of GitLab Runner.
## Use PostgreSQL with the Docker executor
If you are using [GitLab Runner](../runners/README.md) with the Docker executor
you basically have everything set up already.
First, in your `.gitlab-ci.yml` add:
```yaml
services:
- postgres:latest
variables:
POSTGRES_DB: nice_marmot
POSTGRES_USER: runner
POSTGRES_PASSWORD: ""
```
And then configure your application to use the database, for example:
```yaml
Host: postgres
User: runner
Password:
Database: nice_marmot
```
If you are wondering why we used `postgres` for the `Host`, read more at
[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build).
You can also use any other docker image available on [Docker Hub][hub-pg].
For example, to use PostgreSQL 9.3 the service becomes `postgres:9.3`.
The `postgres` image can accept some environment variables. For more details
check the documentation on [Docker Hub][hub-pg].
## Use PostgreSQL with the Shell executor
You can also use PostgreSQL on manually configured servers that are using
GitLab Runner with the Shell executor.
First install the PostgreSQL server:
```bash
sudo apt-get install -y postgresql postgresql-client libpq-dev
```
The next step is to create a user, so login to PostgreSQL:
```bash
sudo -u postgres psql -d template1
```
Then create a user (in our case `runner`) which will be used by your
application. Change `$password` in the command below to a real strong password.
*__Note:__ Do not type `template1=#`, this is part of the PostgreSQL prompt.*
```bash
template1=# CREATE USER runner WITH PASSWORD '$password' CREATEDB;
```
*__Note:__ Notice that we created the user with the privilege to be able to
create databases (`CREATEDB`). In the following steps we will create a database
explicitly for that user but having that privilege can be useful if in your
testing framework you have tools that drop and create databases.*
Create the database and grant all privileges on it for the user `runner`:
```bash
template1=# CREATE DATABASE nice_marmot OWNER runner;
```
If all went well you can now quit the database session:
```bash
template1=# \q
```
Now, try to connect to the newly created database with the user `runner` to
check that everything is in place.
```bash
psql -U runner -h localhost -d nice_marmot -W
```
*__Note:__ We are explicitly telling `psql` to connect to localhost in order
to use the md5 authentication. If you omit this step you will be denied access.*
Finally, configure your application to use the database, for example:
```yaml
Host: localhost
User: runner
Password: $password
Database: nice_marmot
```
## Example project
We have set up an [Example PostgreSQL Project][postgres-example-repo] for your
convenience that runs on [GitLab.com](https://gitlab.com) using our publicly
available [shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the build will begin.
[hub-pg]: https://hub.docker.com/_/postgres/
[postgres-example-repo]: https://gitlab.com/gitlab-examples/postgres

69
doc/ci/services/redis.md Normal file
View File

@ -0,0 +1,69 @@
# Using Redis
As many applications depend on Redis as their key-value store, you will
eventually need it in order for your tests to run. Below you are guided how to
do this with the Docker and Shell executors of GitLab Runner.
## Use Redis with the Docker executor
If you are using [GitLab Runner](../runners/README.md) with the Docker executor
you basically have everything set up already.
First, in your `.gitlab-ci.yml` add:
```yaml
services:
- redis:latest
```
Then you need to configure your application to use the Redis database, for
example:
```yaml
Host: redis
```
And that's it. Redis will now be available to be used within your testing
framework.
You can also use any other docker image available on [Docker Hub][hub-redis].
For example, to use Redis 2.8 the service becomes `redis:2.8`.
## Use Redis with the Shell executor
Redis can also be used on manually configured servers that are using GitLab
Runner with the Shell executor.
In your build machine install the Redis server:
```bash
sudo apt-get install redis-server
```
Verify that you can connect to the server with the `gitlab-runner` user:
```bash
# Try connecting the the Redis server
sudo -u gitlab-runner -H redis-cli
# Quit the session
127.0.0.1:6379> quit
```
Finally, configure your application to use the database, for example:
```yaml
Host: localhost
```
## Example project
We have set up an [Example Redis Project][redis-example-repo] for your convenience
that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the build will begin.
[hub-redis]: https://hub.docker.com/_/redis/
[redis-example-repo]: https://gitlab.com/gitlab-examples/redis

109
doc/ci/ssh_keys/README.md Normal file
View File

@ -0,0 +1,109 @@
# Using SSH keys
GitLab currently doesn't have built-in support for managing SSH keys in a build
environment.
The SSH keys can be useful when:
1. You want to checkout internal submodules
2. You want to download private packages using your package manager (eg. bundler)
3. You want to deploy your application to eg. Heroku or your own server
4. You want to execute SSH commands from the build server to the remote server
5. You want to rsync files from your build server to the remote server
If anything of the above rings a bell, then you most likely need an SSH key.
## Inject keys in your build server
The most widely supported method is to inject an SSH key into your build
environment by extending your `.gitlab-ci.yml`.
This is the universal solution which works with any type of executor
(docker, shell, etc.).
### How it works
1. Create a new SSH key pair with [ssh-keygen][]
2. Add the private key as a **Secret Variable** to the project
3. Run the [ssh-agent][] during build to load the private key.
## SSH keys when using the Docker executor
You will first need to create an SSH key pair. For more information, follow the
instructions to [generate an SSH key](../ssh/README.md).
Then, create a new **Secret Variable** in your project settings on GitLab
following **Settings > Variables**. As **Key** add the name `SSH_PRIVATE_KEY`
and in the **Value** field paste the content of your _private_ key that you
created earlier.
Next you need to modify your `.gitlab-ci.yml` with a `before_script` action.
Add it to the top:
```
before_script:
# Install ssh-agent if not already installed, it is required by Docker.
# (change apt-get to yum if you use a CentOS-based image)
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
# Run ssh-agent (inside the build environment)
- eval $(ssh-agent -s)
# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
- ssh-add <(echo "$SSH_PRIVATE_KEY")
# For Docker builds disable host key checking. Be aware that by adding that
# you are suspectible to man-in-the-middle attacks.
# WARNING: Use this only with the Docker executor, if you use it with shell
# you will overwrite your user's SSH config.
- mkdir -p ~/.ssh
- '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config`
```
As a final step, add the _public_ key from the one you created earlier to the
services that you want to have an access to from within the build environment.
If you are accessing a private GitLab repository you need to add it as a
[deploy key](../ssh/README.md#deploy-keys).
That's it! You can now have access to private servers or repositories in your
build environment.
## SSH keys when using the Shell executor
If you are using the Shell executor and not Docker, it is easier to set up an
SSH key.
You can generate the SSH key from the machine that GitLab Runner is installed
on, and use that key for all projects that are run on this machine.
First, you need to login to the server that runs your builds.
Then from the terminal login as the `gitlab-runner` user and generate the SSH
key pair as described in the [SSH keys documentation](../ssh/README.md).
As a final step, add the _public_ key from the one you created earlier to the
services that you want to have an access to from within the build environment.
If you are accessing a private GitLab repository you need to add it as a
[deploy key](../ssh/README.md#deploy-keys).
Once done, try to login to the remote server in order to accept the fingerprint:
```bash
ssh <address-of-my-server>
```
For accessing repositories on GitLab.com, the `<address-of-my-server>` would be
`git@gitlab.com`.
## Example project
We have set up an [Example SSH Project][ssh-example-repo] for your convenience
that runs on [GitLab.com](https://gitlab.com) using our publicly available
[shared runners](../runners/README.md).
Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the build will begin.
[ssh-keygen]: http://linux.die.net/man/1/ssh-keygen
[ssh-agent]: http://linux.die.net/man/1/ssh-agent
[ssh-example-repo]: https://gitlab.com/gitlab-examples/ssh-private-key/

View File

@ -1,38 +1,39 @@
# Issue closing pattern
Here's how to close multiple issues in one commit message:
When a commit or merge request resolves one or more issues, it is possible to automatically have these issues closed when the commit or merge request lands in the project's default branch.
If a commit message matches the regular expression below, all issues referenced from
the matched text will be closed. This happens when the commit is pushed or merged
into the default branch of a project.
If a commit message or merge request description contains a sentence matching the regular expression below, all issues referenced from
the matched text will be closed. This happens when the commit is pushed to a project's default branch, or when a commit or merge request is merged into there.
When not specified, the default issue_closing_pattern as shown below will be used:
When not specified, the default `issue_closing_pattern` as shown below will be used:
```bash
((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)
((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)
```
Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that matches a reference to a local issue (`#123`), cross-project issue (`group/project#123`) or a link to an issue (`https://gitlab.example.com/group/project/issues/123`).
For example:
```
git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes #22). This commit is also related to #17 and fixes #18, #19 and #23."
git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes group/otherproject#2). This commit is also related to #17 and fixes #18, #19 and https://gitlab.example.com/group/otherproject/issues/23."
```
will close `#20`, `#21`, `#22`, `#18`, `#19` and `#23`, but `#17` won't be closed
as it does not match the pattern. It also works with multiline commit messages.
will close `#18`, `#19`, `#20`, and `#21` in the project this commit is pushed to, as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it does not match the pattern. It also works with multiline commit messages.
Tip: you can test this closing pattern at [http://rubular.com][1]. Use this site
to test your own patterns.
Because Rubular doesn't understand `%{issue_ref}`, you can replace this by `#\d+` in testing, which matches only local issue references like `#123`.
## Change the pattern
For Omnibus installs you can change the default pattern in `/etc/gitlab/gitlab.rb`:
```
issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)'
```
For manual installs you can customize the pattern in [gitlab.yml][0].
For manual installs you can customize the pattern in [gitlab.yml][0] using the `issue_closing_pattern` key.
[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/40c3675372320febf5264061c9bcd63db2dfd13c/config/gitlab.yml.example#L65
[1]: http://rubular.com/r/Xmbexed1OJ
[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example
[1]: http://rubular.com/r/Xmbexed1OJ

View File

@ -30,7 +30,7 @@ Bitbucket will generate an application ID and secret key for you to use.
sudo editor /etc/gitlab/gitlab.rb
```
For instalations from source:
For installations from source:
```sh
cd /home/git/gitlab

View File

@ -10,7 +10,7 @@ To enable the Crowd OmniAuth provider you must register your application with Cr
sudo editor /etc/gitlab/gitlab.rb
```
For instalations from source:
For installations from source:
```sh
cd /home/git/gitlab

View File

@ -32,7 +32,7 @@ GitHub will generate an application ID and secret key for you to use.
sudo editor /etc/gitlab/gitlab.rb
```
For instalations from source:
For installations from source:
```sh
cd /home/git/gitlab

View File

@ -38,7 +38,7 @@ GitLab.com will generate an application ID and secret key for you to use.
sudo editor /etc/gitlab/gitlab.rb
```
For instalations from source:
For installations from source:
```sh
cd /home/git/gitlab

View File

@ -35,7 +35,7 @@ To enable the Google OAuth2 OmniAuth provider you must register your application
sudo editor /etc/gitlab/gitlab.rb
```
For instalations from source:
For installations from source:
```sh
cd /home/git/gitlab

View File

@ -14,7 +14,7 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application
sudo editor /etc/gitlab/gitlab.rb
```
For instalations from source:
For installations from source:
```sh
cd /home/git/gitlab

View File

@ -37,7 +37,7 @@ To enable the Twitter OmniAuth provider you must register your application with
sudo editor /etc/gitlab/gitlab.rb
```
For instalations from source:
For installations from source:
```sh
cd /home/git/gitlab

View File

@ -85,11 +85,10 @@ sudo -u git -H git checkout 0.4.2
sudo -u git -H make
```
Update the GitLab init script and 'default' file.
Update the GitLab 'default' file.
```
cd /home/git/gitlab
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
test -e /etc/default/gitlab && \
sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab
```

View File

@ -47,7 +47,7 @@ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`ca
```bash
cd /home/git/gitlab-workhorse
sudo -u git -H git fetch
sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION`
sudo -u git -H git checkout `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION`
```
### 5. Install libs, migrations, etc.

View File

@ -17,4 +17,5 @@
- [Milestones](milestones.md)
- [Merge Requests](merge_requests.md)
- ["Work In Progress" Merge Requests](wip_merge_requests.md)
- [Merge When Build Succeeds](merge_when_build_succeeds.md)
- [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md)

Some files were not shown because too many files have changed in this diff Show More