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:
commit
10387f6b8a
|
@ -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
|
||||
|
|
12
CHANGELOG
12
CHANGELOG
|
@ -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
|
||||
|
|
1
Gemfile
1
Gemfile
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
# import_type :string(255)
|
||||
# import_source :string(255)
|
||||
# commit_count :integer default(0)
|
||||
# import_error :text
|
||||
#
|
||||
|
||||
require 'carrierwave/orm/activerecord'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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 = []
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
||||
|
||||
- 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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
= link_to issue_path(issue) + "#notes" do
|
||||
= icon('comments')
|
||||
= note_count
|
||||
%li
|
||||
= link_to issue_path(issue) + "#notes" do
|
||||
= icon('comments')
|
||||
= note_count
|
||||
- else
|
||||
|
||||
= 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} ·
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
= 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
|
||||
|
||||
= 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
|
||||
|
||||
= 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} ·
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
:plain
|
||||
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/accept'))}");
|
|
@ -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'))}");
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
= render "projects/commit/builds", link_to_commit: true
|
|
@ -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")
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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
|
|
@ -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'}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
13
db/schema.rb
13
db/schema.rb
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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)
|
|
@ -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
|
|
@ -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)
|
|
@ -0,0 +1,5 @@
|
|||
## GitLab CI Services
|
||||
|
||||
+ [Using MySQL](mysql.md)
|
||||
+ [Using PostgreSQL](postgres.md)
|
||||
+ [Using Redis](redis.md)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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/
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue