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/application.log
|
||||||
- touch log/test.log
|
- touch log/test.log
|
||||||
- bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}"
|
- 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:
|
spec:feature:
|
||||||
script:
|
script:
|
||||||
|
@ -24,6 +24,27 @@ spec:api:
|
||||||
- ruby
|
- ruby
|
||||||
- mysql
|
- 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:
|
spec:benchmark:
|
||||||
script:
|
script:
|
||||||
- RAILS_ENV=test bundle exec rake spec:benchmark
|
- RAILS_ENV=test bundle exec rake spec:benchmark
|
||||||
|
@ -39,9 +60,16 @@ spec:other:
|
||||||
- ruby
|
- ruby
|
||||||
- mysql
|
- mysql
|
||||||
|
|
||||||
spinach:project:
|
spinach:project:half:
|
||||||
script:
|
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:
|
tags:
|
||||||
- ruby
|
- ruby
|
||||||
- mysql
|
- mysql
|
||||||
|
@ -89,10 +117,9 @@ flay:
|
||||||
- mysql
|
- mysql
|
||||||
|
|
||||||
bundler:audit:
|
bundler:audit:
|
||||||
script:
|
script:
|
||||||
- "bundle exec bundle-audit update"
|
- "bundle exec bundle-audit update"
|
||||||
- "bundle exec bundle-audit check"
|
- "bundle exec bundle-audit check"
|
||||||
tags:
|
tags:
|
||||||
- ruby
|
- ruby
|
||||||
- mysql
|
- 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.
|
Please view this file on the master branch, on stable branches it's out of date.
|
||||||
|
|
||||||
v 8.3.0 (unreleased)
|
v 8.3.0 (unreleased)
|
||||||
|
- Merge when build succeeds (Zeger-Jan van de Weg)
|
||||||
- Bump gollum-lib to 4.1.0 (Stan Hu)
|
- Bump gollum-lib to 4.1.0 (Stan Hu)
|
||||||
- Fix broken group avatar upload under "New group" (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)
|
- 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
|
- Add API endpoint to fetch merge request commits list
|
||||||
- Expose events API with comment information and author info
|
- 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: 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.
|
- 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
|
- 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
|
- Add languages page to graphs
|
||||||
- Block LDAP user when they are no longer found in the LDAP server
|
- 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
|
v 8.2.3
|
||||||
- Fix application settings cache not expiring after changes (Stan Hu)
|
- Fix application settings cache not expiring after changes (Stan Hu)
|
||||||
- Fix Error 500s when creating global milestones with Unicode characters (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
|
v 8.2.3
|
||||||
- Webhook payload has an added, modified and removed properties for each commit
|
- 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
|
v 8.2.2
|
||||||
- Fix 404 in redirection after removing a project (Stan Hu)
|
- 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 Error 500 when viewing user's personal projects from admin page (Stan Hu)
|
||||||
- Fix: Raw private snippets access workflow
|
- Fix: Raw private snippets access workflow
|
||||||
- Prevent "413 Request entity too large" errors when pushing large files with LFS
|
- 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
|
- Fix invalid links within projects dashboard header
|
||||||
- Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
|
- Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
|
||||||
- Fix: duplicate email notifications on issue comments
|
- 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ý)
|
- Add email notification to former assignee upon unassignment (Adam Lieskovský)
|
||||||
- New design for project graphs page
|
- New design for project graphs page
|
||||||
- Remove deprecated dumped yaml file generated from previous job definitions
|
- 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
|
- 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
|
- MR target branch is now visible on a list view when it is different from project's default one
|
||||||
- Improve Continuous Integration graphs page
|
- 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)
|
- Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie)
|
||||||
|
|
||||||
v 8.0.1
|
v 8.0.1
|
||||||
- Remove git refs used internally by GitLab from network graph (Stan Hu)
|
|
||||||
- Improve CI migration procedure and documentation
|
- Improve CI migration procedure and documentation
|
||||||
|
|
||||||
v 8.0.0
|
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 'task_list', '~> 1.0.2', require: 'task_list/railtie'
|
||||||
gem 'github-markup', '~> 1.3.1'
|
gem 'github-markup', '~> 1.3.1'
|
||||||
gem 'redcarpet', '~> 3.3.3'
|
gem 'redcarpet', '~> 3.3.3'
|
||||||
gem 'RedCloth', '~> 4.2.9'
|
|
||||||
gem 'rdoc', '~>3.6'
|
gem 'rdoc', '~>3.6'
|
||||||
gem 'org-ruby', '~> 0.9.12'
|
gem 'org-ruby', '~> 0.9.12'
|
||||||
gem 'creole', '~> 0.5.0'
|
gem 'creole', '~> 0.5.0'
|
||||||
|
|
|
@ -2,7 +2,6 @@ GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (2.3.2)
|
CFPropertyList (2.3.2)
|
||||||
RedCloth (4.2.9)
|
|
||||||
ace-rails-ap (2.0.1)
|
ace-rails-ap (2.0.1)
|
||||||
actionmailer (4.2.4)
|
actionmailer (4.2.4)
|
||||||
actionpack (= 4.2.4)
|
actionpack (= 4.2.4)
|
||||||
|
@ -826,7 +825,6 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
RedCloth (~> 4.2.9)
|
|
||||||
ace-rails-ap (~> 2.0.1)
|
ace-rails-ap (~> 2.0.1)
|
||||||
activerecord-deprecated_finders (~> 1.0.3)
|
activerecord-deprecated_finders (~> 1.0.3)
|
||||||
activerecord-session_store (~> 0.1.0)
|
activerecord-session_store (~> 0.1.0)
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#
|
#
|
||||||
class @MergeRequestTabs
|
class @MergeRequestTabs
|
||||||
diffsLoaded: false
|
diffsLoaded: false
|
||||||
|
buildsLoaded: false
|
||||||
commitsLoaded: false
|
commitsLoaded: false
|
||||||
|
|
||||||
constructor: (@opts = {}) ->
|
constructor: (@opts = {}) ->
|
||||||
|
@ -54,6 +55,12 @@ class @MergeRequestTabs
|
||||||
|
|
||||||
bindEvents: ->
|
bindEvents: ->
|
||||||
$(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown
|
$(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) =>
|
tabShown: (event) =>
|
||||||
$target = $(event.target)
|
$target = $(event.target)
|
||||||
|
@ -63,6 +70,8 @@ class @MergeRequestTabs
|
||||||
@loadCommits($target.attr('href'))
|
@loadCommits($target.attr('href'))
|
||||||
else if action == 'diffs'
|
else if action == 'diffs'
|
||||||
@loadDiff($target.attr('href'))
|
@loadDiff($target.attr('href'))
|
||||||
|
else if action == 'builds'
|
||||||
|
@loadBuilds($target.attr('href'))
|
||||||
|
|
||||||
@setCurrentAction(action)
|
@setCurrentAction(action)
|
||||||
|
|
||||||
|
@ -101,7 +110,7 @@ class @MergeRequestTabs
|
||||||
action = 'notes' if action == 'show'
|
action = 'notes' if action == 'show'
|
||||||
|
|
||||||
# Remove a trailing '/commits' or '/diffs'
|
# 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'
|
# Append the new action if we're on a tab other than 'notes'
|
||||||
unless action == 'notes'
|
unless action == 'notes'
|
||||||
|
@ -139,6 +148,17 @@ class @MergeRequestTabs
|
||||||
@diffsLoaded = true
|
@diffsLoaded = true
|
||||||
@scrollToElement("#diffs")
|
@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
|
# Show or hide the loading spinner
|
||||||
#
|
#
|
||||||
# status - Boolean, true to show, false to hide
|
# status - Boolean, true to show, false to hide
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
li {
|
> li {
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
min-height: 20px;
|
min-height: 20px;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #eee;
|
||||||
|
@ -88,8 +88,14 @@ ul.bordered-list {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
li.task-list-item {
|
ul.task-list {
|
||||||
list-style-type: none;
|
li.task-list-item {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul:not(.task-list) {
|
||||||
|
padding-left: 1.3em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.content-list {
|
ul.content-list {
|
||||||
|
@ -125,3 +131,27 @@ ul.content-list {
|
||||||
margin: 0;
|
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-merge-holder {
|
||||||
.accept-action {
|
.accept-action {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
|
||||||
.accept_merge_request {
|
.accept_merge_request {
|
||||||
&.ci-pending,
|
&.ci-pending,
|
||||||
|
@ -36,14 +37,15 @@
|
||||||
|
|
||||||
.accept-control {
|
.accept-control {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
padding-top: 12px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
|
||||||
&.right {
|
&.right {
|
||||||
float: right;
|
float: right;
|
||||||
padding-top: 12px;
|
|
||||||
a {
|
a {
|
||||||
color: $gl-gray;
|
color: $gl-gray;
|
||||||
}
|
}
|
||||||
|
@ -81,6 +83,10 @@
|
||||||
&.ci-error {
|
&.ci-error {
|
||||||
color: $gl-danger;
|
color: $gl-danger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.monospace {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mr-widget-body,
|
.mr-widget-body,
|
||||||
|
|
|
@ -109,13 +109,9 @@ ul.notes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reduce left padding of first task list ul element
|
ul.task-list {
|
||||||
ul.task-list:first-child {
|
ul:not(.task-list) {
|
||||||
padding-left: 10px;
|
padding-left: 1.3em;
|
||||||
|
|
||||||
// sub-tasks should be padded normally
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,9 @@ class PasswordsController < Devise::PasswordsController
|
||||||
def throttle_reset
|
def throttle_reset
|
||||||
return unless resource && resource.recently_sent_password_reset?
|
return unless resource && resource.recently_sent_password_reset?
|
||||||
|
|
||||||
redirect_to new_password_path(resource_name),
|
# Throttle reset attempts, but return a normal message to
|
||||||
alert: I18n.t('devise.passwords.recently_reset')
|
# avoid user enumeration attack.
|
||||||
|
redirect_to new_user_session_path,
|
||||||
|
notice: I18n.t('devise.passwords.send_paranoid_instructions')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ class Projects::ApplicationController < ApplicationController
|
||||||
unless @repository.branch_names.include?(@ref)
|
unless @repository.branch_names.include?(@ref)
|
||||||
redirect_to(
|
redirect_to(
|
||||||
namespace_project_tree_path(@project.namespace, @project, @ref),
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -162,12 +162,20 @@ class Projects::BlobController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def sanitized_new_branch_name
|
def sanitized_new_branch_name
|
||||||
@new_branch ||= sanitize(strip_tags(params[:new_branch]))
|
sanitize(strip_tags(params[:new_branch]))
|
||||||
end
|
end
|
||||||
|
|
||||||
def editor_variables
|
def editor_variables
|
||||||
@current_branch = @ref
|
@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 =
|
@file_path =
|
||||||
if action_name.to_s == 'create'
|
if action_name.to_s == 'create'
|
||||||
|
|
|
@ -37,7 +37,7 @@ class Projects::CommitController < Projects::ApplicationController
|
||||||
def cancel_builds
|
def cancel_builds
|
||||||
ci_commit.builds.running_or_pending.each(&:cancel)
|
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
|
end
|
||||||
|
|
||||||
def retry_builds
|
def retry_builds
|
||||||
|
@ -47,7 +47,7 @@ class Projects::CommitController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
def branches
|
def branches
|
||||||
|
@ -74,8 +74,8 @@ class Projects::CommitController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
@notes_count = commit.notes.count
|
@notes_count = commit.notes.count
|
||||||
|
|
||||||
@builds = ci_commit.builds if ci_commit
|
@statuses = ci_commit.statuses if ci_commit
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_manage_builds!
|
def authorize_manage_builds!
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
class Projects::MergeRequestsController < Projects::ApplicationController
|
class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
before_action :module_enabled
|
before_action :module_enabled
|
||||||
before_action :merge_request, only: [
|
before_action :merge_request, only: [
|
||||||
:edit, :update, :show, :diffs, :commits, :merge, :merge_check,
|
:edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
|
||||||
:ci_status, :toggle_subscription
|
:ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds
|
||||||
]
|
]
|
||||||
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits]
|
before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds]
|
||||||
before_action :validates_merge_request, only: [:show, :diffs, :commits]
|
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
|
||||||
before_action :define_show_vars, only: [:show, :diffs, :commits]
|
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
|
||||||
before_action :ensure_ref_fetched, only: [:show, :commits, :diffs]
|
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
|
# Allow read any merge_request
|
||||||
before_action :authorize_read_merge_request!
|
before_action :authorize_read_merge_request!
|
||||||
|
@ -79,6 +80,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
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
|
def new
|
||||||
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
|
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
|
||||||
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
|
@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
|
@target_project = merge_request.target_project
|
||||||
@source_project = merge_request.source_project
|
@source_project = merge_request.source_project
|
||||||
@commits = @merge_request.compare_commits
|
@commits = @merge_request.compare_commits.reverse
|
||||||
@commit = @merge_request.last_commit
|
@commit = @merge_request.last_commit
|
||||||
@first_commit = @merge_request.first_commit
|
@first_commit = @merge_request.first_commit
|
||||||
@diffs = @merge_request.compare_diffs
|
@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)).
|
@note_counts = Note.where(commit_id: @commits.map(&:id)).
|
||||||
group(:commit_id).count
|
group(:commit_id).count
|
||||||
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 create
|
def create
|
||||||
@target_branches ||= []
|
@target_branches ||= []
|
||||||
@merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
|
@merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
|
||||||
|
@ -118,6 +127,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
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
|
def update
|
||||||
@merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
|
@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
|
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
|
||||||
end
|
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
|
def merge
|
||||||
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
|
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
|
||||||
|
|
||||||
if @merge_request.mergeable?
|
unless @merge_request.mergeable?
|
||||||
@merge_request.update(merge_error: nil)
|
@status = :failed
|
||||||
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
|
return
|
||||||
@status = true
|
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
|
else
|
||||||
@status = false
|
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
|
||||||
|
@status = :success
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -265,6 +294,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
@merge_request_diff = @merge_request.merge_request_diff
|
@merge_request_diff = @merge_request.merge_request_diff
|
||||||
|
|
||||||
@ci_commit = @merge_request.ci_commit
|
@ci_commit = @merge_request.ci_commit
|
||||||
|
@statuses = @ci_commit.statuses if @ci_commit
|
||||||
|
|
||||||
if @merge_request.locked_long_ago?
|
if @merge_request.locked_long_ago?
|
||||||
@merge_request.unlock_mr
|
@merge_request.unlock_mr
|
||||||
|
@ -272,6 +302,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def define_widget_vars
|
||||||
|
@ci_commit = @merge_request.ci_commit
|
||||||
|
end
|
||||||
|
|
||||||
def invalid_mr
|
def invalid_mr
|
||||||
# Render special view for MR with removed source or target branch
|
# Render special view for MR with removed source or target branch
|
||||||
render 'invalid'
|
render 'invalid'
|
||||||
|
@ -285,6 +319,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def merge_params
|
||||||
|
params.permit(:should_remove_source_branch, :commit_message)
|
||||||
|
end
|
||||||
|
|
||||||
# Make sure merge requests created before 8.0
|
# Make sure merge requests created before 8.0
|
||||||
# have head file in refs/merge-requests/
|
# have head file in refs/merge-requests/
|
||||||
def ensure_ref_fetched
|
def ensure_ref_fetched
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
|
||||||
@group_members = @group_members.where(user_id: users)
|
@group_members = @group_members.where(user_id: users)
|
||||||
end
|
end
|
||||||
|
|
||||||
@group_members = @group_members.order('access_level DESC').limit(20)
|
@group_members = @group_members.order('access_level DESC')
|
||||||
end
|
end
|
||||||
|
|
||||||
@project_member = @project.project_members.new
|
@project_member = @project.project_members.new
|
||||||
|
|
|
@ -30,26 +30,24 @@ module BlobHelper
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if blob_viewable?(blob)
|
return unless blob && blob.text? && blob_editable?(blob)
|
||||||
text = 'Edit'
|
|
||||||
after = options[:after] || ''
|
text = 'Edit'
|
||||||
from_mr = options[:from_merge_request_id]
|
after = options[:after] || ''
|
||||||
link_opts = {}
|
from_mr = options[:from_merge_request_id]
|
||||||
link_opts[:from_merge_request_id] = from_mr if from_mr
|
link_opts = {}
|
||||||
cls = 'btn btn-small'
|
link_opts[:from_merge_request_id] = from_mr if from_mr
|
||||||
if allowed_tree_edit?(project, ref)
|
cls = 'btn btn-small'
|
||||||
link_to(text,
|
link_to(text,
|
||||||
namespace_project_edit_blob_path(project.namespace, project,
|
namespace_project_edit_blob_path(project.namespace, project,
|
||||||
tree_join(ref, path),
|
tree_join(ref, path),
|
||||||
link_opts),
|
link_opts),
|
||||||
class: cls
|
class: cls
|
||||||
)
|
) + after.html_safe
|
||||||
else
|
end
|
||||||
content_tag :span, text, class: cls + ' disabled'
|
|
||||||
end + after.html_safe
|
def blob_editable?(blob, project = @project, ref = @ref)
|
||||||
else
|
!blob.lfs_pointer? && allowed_tree_edit?(project, ref)
|
||||||
''
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def leave_edit_message
|
def leave_edit_message
|
||||||
|
|
|
@ -11,7 +11,7 @@ module BranchesHelper
|
||||||
|
|
||||||
def can_push_branch?(project, branch_name)
|
def can_push_branch?(project, branch_name)
|
||||||
return false unless project.repository.branch_names.include?(branch_name)
|
return false unless project.repository.branch_names.include?(branch_name)
|
||||||
|
|
||||||
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
|
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -58,7 +58,7 @@ module CiStatusHelper
|
||||||
def render_ci_status(ci_commit)
|
def render_ci_status(ci_commit)
|
||||||
link_to ci_status_path(ci_commit),
|
link_to ci_status_path(ci_commit),
|
||||||
class: "c#{ci_status_color(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
|
data: { toggle: 'tooltip', placement: 'left' } do
|
||||||
ci_status_icon(ci_commit)
|
ci_status_icon(ci_commit)
|
||||||
end
|
end
|
||||||
|
|
|
@ -175,11 +175,19 @@ module ProjectsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_url_to_repo(project = @project)
|
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
|
end
|
||||||
|
|
||||||
def default_clone_protocol
|
def default_clone_protocol
|
||||||
current_user ? "ssh" : "http"
|
if !current_user || current_user.require_ssh_key?
|
||||||
|
"http"
|
||||||
|
else
|
||||||
|
"ssh"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_last_activity(project)
|
def project_last_activity(project)
|
||||||
|
|
|
@ -46,16 +46,26 @@ module TreeHelper
|
||||||
File.join(*args)
|
File.join(*args)
|
||||||
end
|
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)
|
def allowed_tree_edit?(project = nil, ref = nil)
|
||||||
project ||= @project
|
project ||= @project
|
||||||
ref ||= @ref
|
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
|
end
|
||||||
|
|
||||||
def can_delete_or_replace?(blob)
|
def tree_edit_branch(project = @project, ref = @ref)
|
||||||
allowed_tree_edit? && !blob.lfs_pointer?
|
if allowed_tree_edit?(project, ref)
|
||||||
|
if can_push_branch?(project, ref)
|
||||||
|
ref
|
||||||
|
else
|
||||||
|
project.repository.next_patch_branch
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def tree_breadcrumbs(tree, max_links = 2)
|
def tree_breadcrumbs(tree, max_links = 2)
|
||||||
|
|
|
@ -12,22 +12,22 @@ module VisibilityLevelHelper
|
||||||
|
|
||||||
# Return the description for the +level+ argument.
|
# Return the description for the +level+ argument.
|
||||||
#
|
#
|
||||||
# +level+ One of the Gitlab::VisibilityLevel constants
|
# +level+ One of the Gitlab::VisibilityLevel constants
|
||||||
# +form_model+ Either a model object (Project, Snippet, etc.) or the name of
|
# +form_model+ Either a model object (Project, Snippet, etc.) or the name of
|
||||||
# a Project or Snippet class.
|
# a Project or Snippet class.
|
||||||
def visibility_level_description(level, form_model)
|
def visibility_level_description(level, form_model)
|
||||||
case form_model.is_a?(String) ? form_model : form_model.class.name
|
case form_model
|
||||||
when 'PersonalSnippet', 'ProjectSnippet', 'Snippet'
|
when Project
|
||||||
snippet_visibility_level_description(level)
|
|
||||||
when 'Project'
|
|
||||||
project_visibility_level_description(level)
|
project_visibility_level_description(level)
|
||||||
|
when Snippet
|
||||||
|
snippet_visibility_level_description(level, form_model)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def project_visibility_level_description(level)
|
def project_visibility_level_description(level)
|
||||||
case level
|
case level
|
||||||
when Gitlab::VisibilityLevel::PRIVATE
|
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
|
when Gitlab::VisibilityLevel::INTERNAL
|
||||||
"The project can be cloned by any logged in user."
|
"The project can be cloned by any logged in user."
|
||||||
when Gitlab::VisibilityLevel::PUBLIC
|
when Gitlab::VisibilityLevel::PUBLIC
|
||||||
|
@ -35,12 +35,16 @@ module VisibilityLevelHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def snippet_visibility_level_description(level)
|
def snippet_visibility_level_description(level, snippet = nil)
|
||||||
case level
|
case level
|
||||||
when Gitlab::VisibilityLevel::PRIVATE
|
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
|
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
|
when Gitlab::VisibilityLevel::PUBLIC
|
||||||
"The snippet can be accessed without any authentication."
|
"The snippet can be accessed without any authentication."
|
||||||
end
|
end
|
||||||
|
|
|
@ -346,12 +346,10 @@ class Ability
|
||||||
unless group.last_owner?(target_user)
|
unless group.last_owner?(target_user)
|
||||||
can_manage = group_abilities(user, group).include?(:admin_group_member)
|
can_manage = group_abilities(user, group).include?(:admin_group_member)
|
||||||
|
|
||||||
if can_manage && user != target_user
|
if can_manage
|
||||||
rules << :update_group_member
|
rules << :update_group_member
|
||||||
rules << :destroy_group_member
|
rules << :destroy_group_member
|
||||||
end
|
elsif user == target_user
|
||||||
|
|
||||||
if user == target_user
|
|
||||||
rules << :destroy_group_member
|
rules << :destroy_group_member
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -367,12 +365,10 @@ class Ability
|
||||||
unless target_user == project.owner
|
unless target_user == project.owner
|
||||||
can_manage = project_abilities(user, project).include?(:admin_project_member)
|
can_manage = project_abilities(user, project).include?(:admin_project_member)
|
||||||
|
|
||||||
if can_manage && user != target_user
|
if can_manage
|
||||||
rules << :update_project_member
|
rules << :update_project_member
|
||||||
rules << :destroy_project_member
|
rules << :destroy_project_member
|
||||||
end
|
elsif user == target_user
|
||||||
|
|
||||||
if user == target_user
|
|
||||||
rules << :destroy_project_member
|
rules << :destroy_project_member
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -165,6 +165,14 @@ module Ci
|
||||||
status == 'canceled'
|
status == 'canceled'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def active?
|
||||||
|
running? || pending?
|
||||||
|
end
|
||||||
|
|
||||||
|
def complete?
|
||||||
|
canceled? || success? || failed?
|
||||||
|
end
|
||||||
|
|
||||||
def duration
|
def duration
|
||||||
duration_array = latest_statuses.map(&:duration).compact
|
duration_array = latest_statuses.map(&:duration).compact
|
||||||
duration_array.reduce(:+).to_i
|
duration_array.reduce(:+).to_i
|
||||||
|
|
|
@ -175,11 +175,11 @@ class Commit
|
||||||
end
|
end
|
||||||
|
|
||||||
def author
|
def author
|
||||||
@author ||= User.find_by_any_email(author_email)
|
@author ||= User.find_by_any_email(author_email.downcase)
|
||||||
end
|
end
|
||||||
|
|
||||||
def committer
|
def committer
|
||||||
@committer ||= User.find_by_any_email(committer_email)
|
@committer ||= User.find_by_any_email(committer_email.downcase)
|
||||||
end
|
end
|
||||||
|
|
||||||
def parents
|
def parents
|
||||||
|
|
|
@ -1,34 +1,30 @@
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
# Table name: ci_builds
|
# project_id integer
|
||||||
#
|
# status string
|
||||||
# id :integer not null, primary key
|
# finished_at datetime
|
||||||
# project_id :integer
|
# trace text
|
||||||
# status :string(255)
|
# created_at datetime
|
||||||
# finished_at :datetime
|
# updated_at datetime
|
||||||
# trace :text
|
# started_at datetime
|
||||||
# created_at :datetime
|
# runner_id integer
|
||||||
# updated_at :datetime
|
# coverage float
|
||||||
# started_at :datetime
|
# commit_id integer
|
||||||
# runner_id :integer
|
# commands text
|
||||||
# coverage :float
|
# job_id integer
|
||||||
# commit_id :integer
|
# name string
|
||||||
# commands :text
|
# deploy boolean default: false
|
||||||
# job_id :integer
|
# options text
|
||||||
# name :string(255)
|
# allow_failure boolean default: false, null: false
|
||||||
# deploy :boolean default(FALSE)
|
# stage string
|
||||||
# options :text
|
# trigger_request_id integer
|
||||||
# allow_failure :boolean default(FALSE), not null
|
# stage_idx integer
|
||||||
# stage :string(255)
|
# tag boolean
|
||||||
# trigger_request_id :integer
|
# ref string
|
||||||
# stage_idx :integer
|
# user_id integer
|
||||||
# tag :boolean
|
# type string
|
||||||
# ref :string(255)
|
# target_url string
|
||||||
# user_id :integer
|
# description string
|
||||||
# type :string(255)
|
|
||||||
# target_url :string(255)
|
|
||||||
# description :string(255)
|
|
||||||
# artifacts_file :text
|
|
||||||
#
|
#
|
||||||
|
|
||||||
class CommitStatus < ActiveRecord::Base
|
class CommitStatus < ActiveRecord::Base
|
||||||
|
@ -79,6 +75,10 @@ class CommitStatus < ActiveRecord::Base
|
||||||
build.update_attributes finished_at: Time.now
|
build.update_attributes finished_at: Time.now
|
||||||
end
|
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 :pending, value: 'pending'
|
||||||
state :running, value: 'running'
|
state :running, value: 'running'
|
||||||
state :failed, value: 'failed'
|
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
|
class LfsObject < ActiveRecord::Base
|
||||||
has_many :lfs_objects_projects, dependent: :destroy
|
has_many :lfs_objects_projects, dependent: :destroy
|
||||||
has_many :projects, through: :lfs_objects_projects
|
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
|
class LfsObjectsProject < ActiveRecord::Base
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
belongs_to :lfs_object
|
belongs_to :lfs_object
|
||||||
|
|
|
@ -2,25 +2,28 @@
|
||||||
#
|
#
|
||||||
# Table name: merge_requests
|
# Table name: merge_requests
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# target_branch :string(255) not null
|
# target_branch :string(255) not null
|
||||||
# source_branch :string(255) not null
|
# source_branch :string(255) not null
|
||||||
# source_project_id :integer not null
|
# source_project_id :integer not null
|
||||||
# author_id :integer
|
# author_id :integer
|
||||||
# assignee_id :integer
|
# assignee_id :integer
|
||||||
# title :string(255)
|
# title :string(255)
|
||||||
# created_at :datetime
|
# created_at :datetime
|
||||||
# updated_at :datetime
|
# updated_at :datetime
|
||||||
# milestone_id :integer
|
# milestone_id :integer
|
||||||
# state :string(255)
|
# state :string(255)
|
||||||
# merge_status :string(255)
|
# merge_status :string(255)
|
||||||
# target_project_id :integer not null
|
# target_project_id :integer not null
|
||||||
# iid :integer
|
# iid :integer
|
||||||
# description :text
|
# description :text
|
||||||
# position :integer default(0)
|
# position :integer default(0)
|
||||||
# locked_at :datetime
|
# locked_at :datetime
|
||||||
# updated_by_id :integer
|
# updated_by_id :integer
|
||||||
# merge_error :string(255)
|
# 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")
|
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 :target_project, foreign_key: :target_project_id, class_name: "Project"
|
||||||
belongs_to :source_project, foreign_key: :source_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
|
has_one :merge_request_diff, dependent: :destroy
|
||||||
|
|
||||||
|
serialize :merge_params, Hash
|
||||||
|
|
||||||
after_create :create_merge_request_diff
|
after_create :create_merge_request_diff
|
||||||
after_update :update_merge_request_diff
|
after_update :update_merge_request_diff
|
||||||
|
|
||||||
|
@ -121,6 +127,7 @@ class MergeRequest < ActiveRecord::Base
|
||||||
validates :source_branch, presence: true
|
validates :source_branch, presence: true
|
||||||
validates :target_project, presence: true
|
validates :target_project, presence: true
|
||||||
validates :target_branch, presence: true
|
validates :target_branch, presence: true
|
||||||
|
validates :merge_user, presence: true, if: :merge_when_build_succeeds?
|
||||||
validate :validate_branches
|
validate :validate_branches
|
||||||
validate :validate_fork
|
validate :validate_fork
|
||||||
|
|
||||||
|
@ -258,6 +265,16 @@ class MergeRequest < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
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
|
def mr_and_commit_notes
|
||||||
# Fetch comments only from last 100 commits
|
# Fetch comments only from last 100 commits
|
||||||
commits_for_notes_limit = 100
|
commits_for_notes_limit = 100
|
||||||
|
@ -393,6 +410,16 @@ class MergeRequest < ActiveRecord::Base
|
||||||
message
|
message
|
||||||
end
|
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
|
# Return array of possible target branches
|
||||||
# depends on target project of MR
|
# depends on target project of MR
|
||||||
def target_branches
|
def target_branches
|
||||||
|
@ -480,8 +507,10 @@ class MergeRequest < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def ci_commit
|
def ci_commit
|
||||||
if last_commit and source_project
|
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
|
||||||
source_project.ci_commit(last_commit.id)
|
end
|
||||||
end
|
|
||||||
|
def broken?
|
||||||
|
self.commits.blank? || branch_missing? || cannot_be_merged?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
# system :boolean default(FALSE), not null
|
# system :boolean default(FALSE), not null
|
||||||
# st_diff :text
|
# st_diff :text
|
||||||
# updated_by_id :integer
|
# updated_by_id :integer
|
||||||
|
# is_award :boolean default(FALSE), not null
|
||||||
#
|
#
|
||||||
|
|
||||||
require 'carrierwave/orm/activerecord'
|
require 'carrierwave/orm/activerecord'
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
# import_type :string(255)
|
# import_type :string(255)
|
||||||
# import_source :string(255)
|
# import_source :string(255)
|
||||||
# commit_count :integer default(0)
|
# commit_count :integer default(0)
|
||||||
|
# import_error :text
|
||||||
#
|
#
|
||||||
|
|
||||||
require 'carrierwave/orm/activerecord'
|
require 'carrierwave/orm/activerecord'
|
||||||
|
|
|
@ -329,6 +329,17 @@ class Repository
|
||||||
commit(sha)
|
commit(sha)
|
||||||
end
|
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
|
# Remove archives older than 2 hours
|
||||||
def branches_sorted_by(value)
|
def branches_sorted_by(value)
|
||||||
case value
|
case value
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
# project_view :integer default(0)
|
# project_view :integer default(0)
|
||||||
# consumed_timestep :integer
|
# consumed_timestep :integer
|
||||||
# layout :integer default(0)
|
# layout :integer default(0)
|
||||||
|
# hide_project_limit :boolean default(FALSE)
|
||||||
#
|
#
|
||||||
|
|
||||||
require 'carrierwave/orm/activerecord'
|
require 'carrierwave/orm/activerecord'
|
||||||
|
|
|
@ -53,7 +53,7 @@ module Files
|
||||||
|
|
||||||
unless project.empty_repo?
|
unless project.empty_repo?
|
||||||
unless repository.branch_names.include?(@current_branch)
|
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
|
end
|
||||||
|
|
||||||
if @current_branch != @target_branch
|
if @current_branch != @target_branch
|
||||||
|
|
|
@ -6,15 +6,12 @@ module MergeRequests
|
||||||
# Executed when you do merge via GitLab UI
|
# Executed when you do merge via GitLab UI
|
||||||
#
|
#
|
||||||
class MergeService < MergeRequests::BaseService
|
class MergeService < MergeRequests::BaseService
|
||||||
attr_reader :merge_request, :commit_message
|
attr_reader :merge_request
|
||||||
|
|
||||||
def execute(merge_request, commit_message)
|
def execute(merge_request)
|
||||||
@commit_message = commit_message
|
|
||||||
@merge_request = merge_request
|
@merge_request = merge_request
|
||||||
|
|
||||||
unless @merge_request.mergeable?
|
return error('Merge request is not mergeable') unless @merge_request.mergeable?
|
||||||
return error('Merge request is not mergeable')
|
|
||||||
end
|
|
||||||
|
|
||||||
merge_request.in_locked_state do
|
merge_request.in_locked_state do
|
||||||
if commit
|
if commit
|
||||||
|
@ -32,7 +29,7 @@ module MergeRequests
|
||||||
committer = repository.user_to_committer(current_user)
|
committer = repository.user_to_committer(current_user)
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
message: commit_message,
|
message: params[:commit_message] || merge_request.merge_commit_message,
|
||||||
author: committer,
|
author: committer,
|
||||||
committer: committer
|
committer: committer
|
||||||
}
|
}
|
||||||
|
@ -46,6 +43,11 @@ module MergeRequests
|
||||||
|
|
||||||
def after_merge
|
def after_merge
|
||||||
MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
|
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
|
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
|
# empty diff during a manual merge
|
||||||
close_merge_requests
|
close_merge_requests
|
||||||
reload_merge_requests
|
reload_merge_requests
|
||||||
|
reset_merge_when_build_succeeds
|
||||||
|
|
||||||
# Leave a system note if a branch was deleted/added
|
# Leave a system note if a branch was deleted/added
|
||||||
if branch_added? || branch_removed?
|
if branch_added? || branch_removed?
|
||||||
|
@ -57,7 +58,6 @@ module MergeRequests
|
||||||
merge_requests = filter_merge_requests(merge_requests)
|
merge_requests = filter_merge_requests(merge_requests)
|
||||||
|
|
||||||
merge_requests.each do |merge_request|
|
merge_requests.each do |merge_request|
|
||||||
|
|
||||||
if merge_request.source_branch == @branch_name || force_push?
|
if merge_request.source_branch == @branch_name || force_push?
|
||||||
merge_request.reload_code
|
merge_request.reload_code
|
||||||
merge_request.mark_as_unchecked
|
merge_request.mark_as_unchecked
|
||||||
|
@ -76,6 +76,10 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reset_merge_when_build_succeeds
|
||||||
|
merge_requests_for_source_branch.each(&:reset_merge_when_build_succeeds)
|
||||||
|
end
|
||||||
|
|
||||||
def find_new_commits
|
def find_new_commits
|
||||||
if branch_added?
|
if branch_added?
|
||||||
@commits = []
|
@commits = []
|
||||||
|
|
|
@ -130,6 +130,20 @@ class SystemNoteService
|
||||||
create_note(noteable: noteable, project: project, author: author, note: body)
|
create_note(noteable: noteable, project: project, author: author, note: body)
|
||||||
end
|
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
|
# Called when the title of a Noteable is changed
|
||||||
#
|
#
|
||||||
# noteable - Noteable object that responds to `title`
|
# noteable - Noteable object that responds to `title`
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
.form-group.project-visibility-level-holder
|
.form-group.project-visibility-level-holder
|
||||||
= f.label :default_project_visibility, class: 'control-label col-sm-2'
|
= f.label :default_project_visibility, class: 'control-label col-sm-2'
|
||||||
.col-sm-10
|
.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
|
.form-group.project-visibility-level-holder
|
||||||
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
|
= f.label :default_snippet_visibility, class: 'control-label col-sm-2'
|
||||||
.col-sm-10
|
.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
|
.form-group
|
||||||
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
|
= f.label :restricted_visibility_levels, class: 'control-label col-sm-2'
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
Files
|
Files
|
||||||
|
|
||||||
- if project_nav_tab? :commits
|
- 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
|
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
|
||||||
= icon('history fw')
|
= icon('history fw')
|
||||||
%span
|
%span
|
||||||
|
@ -46,13 +46,6 @@
|
||||||
Builds
|
Builds
|
||||||
%span.count.builds_counter= @project.ci_builds.running_or_pending.count(:all)
|
%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
|
- if project_nav_tab? :graphs
|
||||||
= nav_link(controller: %w(graphs)) do
|
= nav_link(controller: %w(graphs)) do
|
||||||
= link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-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')
|
= icon('cogs fw')
|
||||||
%span
|
%span
|
||||||
Settings
|
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
|
.btn-group.tree-btn-group
|
||||||
= edit_blob_link(@project, @ref, @path)
|
|
||||||
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
|
= link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
|
||||||
class: 'btn btn-sm', target: '_blank'
|
class: 'btn btn-sm', target: '_blank'
|
||||||
-# only show normal/blame view links for text files
|
-# only show normal/blame view links for text files
|
||||||
|
@ -12,11 +11,16 @@
|
||||||
class: 'btn btn-sm' unless @blob.empty?
|
class: 'btn btn-sm' unless @blob.empty?
|
||||||
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id),
|
= link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id),
|
||||||
class: 'btn btn-sm'
|
class: 'btn btn-sm'
|
||||||
- if @ref != @commit.sha
|
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
|
||||||
= link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
|
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
|
||||||
tree_join(@commit.sha, @path)), class: 'btn btn-sm'
|
|
||||||
|
|
||||||
- if can_delete_or_replace?(@blob)
|
- if blob_editable?(@blob)
|
||||||
.btn-group{ role: "group" }
|
.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-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
|
||||||
%button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
|
%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
|
%div#tree-holder.tree-holder
|
||||||
= render 'blob', blob: @blob
|
= render 'blob', blob: @blob
|
||||||
|
|
||||||
- if can_delete_or_replace?(@blob)
|
- if blob_editable?(@blob)
|
||||||
= render 'projects/blob/remove'
|
= render 'projects/blob/remove'
|
||||||
|
|
||||||
- title = "Replace #{@blob.name}"
|
- title = "Replace #{@blob.name}"
|
||||||
|
|
|
@ -12,17 +12,20 @@
|
||||||
%li{class: ('active' if @scope.nil?)}
|
%li{class: ('active' if @scope.nil?)}
|
||||||
= link_to project_builds_path(@project) do
|
= link_to project_builds_path(@project) do
|
||||||
Running
|
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')}
|
%li{class: ('active' if @scope == 'finished')}
|
||||||
= link_to project_builds_path(@project, scope: :finished) do
|
= link_to project_builds_path(@project, scope: :finished) do
|
||||||
Finished
|
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')}
|
%li{class: ('active' if @scope == 'all')}
|
||||||
= link_to project_builds_path(@project, scope: :all) do
|
= link_to project_builds_path(@project, scope: :all) do
|
||||||
All
|
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
|
.gray-content-block
|
||||||
#{(@scope || 'running').capitalize} builds from this project
|
#{(@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
|
= nav_link(path: 'commit#builds') do
|
||||||
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
|
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
|
||||||
Builds
|
Builds
|
||||||
%span.badge= @builds.count(:id)
|
%span.badge= @statuses.count
|
||||||
|
|
|
@ -3,70 +3,4 @@
|
||||||
= render "commit_box"
|
= render "commit_box"
|
||||||
= render "ci_menu"
|
= render "ci_menu"
|
||||||
|
|
||||||
|
= render "builds"
|
||||||
- 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 }
|
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
|
= link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
|
||||||
Commits
|
Commits
|
||||||
%span.badge= number_with_delimiter(@repository.commit_count)
|
%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
|
= nav_link(controller: :compare) do
|
||||||
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
|
= link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do
|
||||||
Compare
|
Compare
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.alert_holder
|
= content_for :flash_message do
|
||||||
- if current_user && can?(current_user, :download_code, @project)
|
- if current_user && can?(current_user, :download_code, @project)
|
||||||
= render 'shared/no_ssh'
|
= render 'shared/no_ssh'
|
||||||
= render 'shared/no_password'
|
= render 'shared/no_password'
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
.input-group.cross-project-reference
|
.input-group.cross-project-reference
|
||||||
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
|
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
|
||||||
= cross_project_reference(@project, @issue)
|
= cross_project_reference(@project, @issue)
|
||||||
= clipboard_button(clipboard_target: '#cross-project-reference')
|
= clipboard_button(clipboard_target: 'span#cross-project-reference')
|
||||||
|
|
||||||
.row
|
.row
|
||||||
%section.col-md-9
|
%section.col-md-9
|
||||||
|
|
|
@ -6,23 +6,26 @@
|
||||||
.issue-title
|
.issue-title
|
||||||
%span.issue-title-text
|
%span.issue-title-text
|
||||||
= link_to_gfm issue.title, issue_path(issue), class: "row_title"
|
= link_to_gfm issue.title, issue_path(issue), class: "row_title"
|
||||||
.pull-right.light
|
%ul.controls.light
|
||||||
- if issue.closed?
|
- if issue.closed?
|
||||||
%span
|
%li
|
||||||
CLOSED
|
CLOSED
|
||||||
|
|
||||||
- if issue.assignee
|
- 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
|
- note_count = issue.notes.user.count
|
||||||
- if note_count > 0
|
- if note_count > 0
|
||||||
|
%li
|
||||||
= link_to issue_path(issue) + "#notes" do
|
= link_to issue_path(issue) + "#notes" do
|
||||||
= icon('comments')
|
= icon('comments')
|
||||||
= note_count
|
= note_count
|
||||||
- else
|
- else
|
||||||
|
%li
|
||||||
= link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
|
= link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
|
||||||
= icon('comments')
|
= icon('comments')
|
||||||
= 0
|
= note_count
|
||||||
|
|
||||||
.issue-info
|
.issue-info
|
||||||
#{issue.to_reference} ·
|
#{issue.to_reference} ·
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
.input-group.cross-project-reference
|
.input-group.cross-project-reference
|
||||||
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
|
%span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
|
||||||
= cross_project_reference(@project, @merge_request)
|
= cross_project_reference(@project, @merge_request)
|
||||||
= clipboard_button(clipboard_target: '#cross-project-reference')
|
= clipboard_button(clipboard_target: 'span#cross-project-reference')
|
||||||
|
|
||||||
.row
|
.row
|
||||||
%section.col-md-9
|
%section.col-md-9
|
||||||
|
|
|
@ -1,33 +1,41 @@
|
||||||
- ci_commit = merge_request.ci_commit
|
|
||||||
%li{ class: mr_css_classes(merge_request) }
|
%li{ class: mr_css_classes(merge_request) }
|
||||||
.merge-request-title
|
.merge-request-title
|
||||||
%span.merge-request-title-text
|
%span.merge-request-title-text
|
||||||
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
|
= link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
|
||||||
.pull-right.light
|
%ul.controls.light
|
||||||
- if ci_commit
|
|
||||||
= render_ci_status(ci_commit)
|
|
||||||
- if merge_request.merged?
|
- if merge_request.merged?
|
||||||
%span
|
%li
|
||||||
= icon('check')
|
= icon('check')
|
||||||
MERGED
|
MERGED
|
||||||
- elsif merge_request.closed?
|
- elsif merge_request.closed?
|
||||||
%span
|
%li
|
||||||
= icon('ban')
|
= icon('ban')
|
||||||
CLOSED
|
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
|
- if merge_request.assignee
|
||||||
|
%li
|
||||||
= link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
|
= 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
|
- if note_count > 0
|
||||||
|
%li
|
||||||
= link_to merge_request_path(merge_request) + "#notes" do
|
= link_to merge_request_path(merge_request) + "#notes" do
|
||||||
= icon('comments')
|
= icon('comments')
|
||||||
= note_count
|
= note_count
|
||||||
- else
|
- else
|
||||||
|
%li
|
||||||
= link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
|
= link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
|
||||||
= icon('comments')
|
= icon('comments')
|
||||||
= 0
|
= note_count
|
||||||
|
|
||||||
.merge-request-info
|
.merge-request-info
|
||||||
\##{merge_request.iid} ·
|
\##{merge_request.iid} ·
|
||||||
|
|
|
@ -20,13 +20,18 @@
|
||||||
.mr-compare.merge-request
|
.mr-compare.merge-request
|
||||||
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
|
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
|
||||||
%li.commits-tab
|
%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
|
Commits
|
||||||
%span.badge= @commits.size
|
%span.badge= @commits.size
|
||||||
%li.diffs-tab.active
|
%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
|
Changes
|
||||||
%span.badge= @diffs.size
|
%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
|
.tab-content
|
||||||
#commits.commits.tab-pane
|
#commits.commits.tab-pane
|
||||||
|
@ -42,6 +47,9 @@
|
||||||
.alert.alert-danger
|
.alert.alert-danger
|
||||||
%h4 This comparison includes a huge diff.
|
%h4 This comparison includes a huge diff.
|
||||||
%p To preserve performance the line changes are not shown.
|
%p To preserve performance the line changes are not shown.
|
||||||
|
- if @ci_commit
|
||||||
|
#builds.builds.tab-pane
|
||||||
|
= render "projects/merge_requests/show/builds"
|
||||||
|
|
||||||
:javascript
|
:javascript
|
||||||
$('.assign-to-me-link').on('click', function(e){
|
$('.assign-to-me-link').on('click', function(e){
|
||||||
|
|
|
@ -26,8 +26,7 @@
|
||||||
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
|
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
|
||||||
.normal
|
.normal
|
||||||
%span Request to merge
|
%span Request to merge
|
||||||
%span.label-branch
|
%span.label-branch= source_branch_with_namespace(@merge_request)
|
||||||
= source_branch_with_namespace(@merge_request)
|
|
||||||
%span into
|
%span into
|
||||||
= link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
|
= link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do
|
||||||
= @merge_request.target_branch
|
= @merge_request.target_branch
|
||||||
|
@ -44,17 +43,22 @@
|
||||||
- if @commits.present?
|
- if @commits.present?
|
||||||
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
|
%ul.merge-request-tabs.center-top-menu.no-top.no-bottom
|
||||||
%li.notes-tab
|
%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
|
Discussion
|
||||||
%span.badge= @merge_request.mr_and_commit_notes.user.count
|
%span.badge= @merge_request.mr_and_commit_notes.user.count
|
||||||
%li.commits-tab
|
%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
|
Commits
|
||||||
%span.badge= @commits.size
|
%span.badge= @commits.size
|
||||||
%li.diffs-tab
|
%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
|
Changes
|
||||||
%span.badge= @merge_request.diffs.size
|
%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
|
.tab-content
|
||||||
#notes.notes.tab-pane.voting_notes
|
#notes.notes.tab-pane.voting_notes
|
||||||
|
@ -63,6 +67,8 @@
|
||||||
- # This tab is always loaded via AJAX
|
- # This tab is always loaded via AJAX
|
||||||
#diffs.diffs.tab-pane
|
#diffs.diffs.tab-pane
|
||||||
- # This tab is always loaded via AJAX
|
- # This tab is always loaded via AJAX
|
||||||
|
#builds.builds.tab-pane
|
||||||
|
- # This tab is always loaded via AJAX
|
||||||
|
|
||||||
.mr-loading-status
|
.mr-loading-status
|
||||||
= spinner
|
= 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
|
:plain
|
||||||
merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
|
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
|
- else
|
||||||
:plain
|
:plain
|
||||||
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}");
|
$('.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
|
- if @ci_commit
|
||||||
- status = @ci_commit.status
|
|
||||||
.mr-widget-heading
|
.mr-widget-heading
|
||||||
.ci_widget{class: "ci-#{status}"}
|
.ci_widget{class: "ci-#{@ci_commit.status}"}
|
||||||
= ci_status_icon(@ci_commit)
|
= ci_status_icon(@ci_commit)
|
||||||
%span CI build #{status}
|
%span
|
||||||
for #{@merge_request.last_commit_short_sha}.
|
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
|
%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?
|
- elsif @merge_request.has_ci?
|
||||||
- # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
|
- # 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
|
- # Remove in later versions when services like Jenkins will set CI status via Commit status API
|
||||||
.mr-widget-heading
|
.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"}
|
.ci_widget{class: "ci-#{status}", style: "display:none"}
|
||||||
- if status == :success
|
= ci_icon_for_status(status)
|
||||||
- status = "passed"
|
%span
|
||||||
= icon("check-circle")
|
CI build
|
||||||
- else
|
= ci_label_for_status(status)
|
||||||
= icon("circle")
|
for
|
||||||
%span CI build #{status}
|
- commit = @merge_request.last_commit
|
||||||
for #{@merge_request.last_commit_short_sha}.
|
= 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
|
%span.ci-coverage
|
||||||
- if ci_build_details_path(@merge_request)
|
- if details_path = ci_build_details_path(@merge_request)
|
||||||
= link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
|
= link_to "View details", details_path, :"data-no-turbolink" => "data-no-turbolink"
|
||||||
|
|
||||||
.ci_widget
|
.ci_widget
|
||||||
= icon("spinner spin")
|
= icon("spinner spin")
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
= @merge_request.target_branch
|
= @merge_request.target_branch
|
||||||
The source branch has been removed.
|
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
|
.remove_source_branch_widget
|
||||||
%p
|
%p
|
||||||
= succeed '.' do
|
= succeed '.' do
|
||||||
|
@ -50,5 +50,3 @@
|
||||||
$('.remove_source_branch_in_progress').hide();
|
$('.remove_source_branch_in_progress').hide();
|
||||||
$('.remove_source_branch_widget.failed').show();
|
$('.remove_source_branch_widget.failed').show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
= render 'projects/merge_requests/widget/open/conflicts'
|
= render 'projects/merge_requests/widget/open/conflicts'
|
||||||
- elsif @merge_request.work_in_progress?
|
- elsif @merge_request.work_in_progress?
|
||||||
= render 'projects/merge_requests/widget/open/wip'
|
= 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)
|
- elsif !@merge_request.can_be_merged_by?(current_user)
|
||||||
= render 'projects/merge_requests/widget/open/not_allowed'
|
= render 'projects/merge_requests/widget/open/not_allowed'
|
||||||
- elsif @merge_request.can_be_merged?
|
- 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|
|
= 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
|
= hidden_field_tag :authenticity_token, form_authenticity_token
|
||||||
.accept-merge-holder.clearfix.js-toggle-container
|
.accept-merge-holder.clearfix.js-toggle-container
|
||||||
.accept-action
|
.clearfix
|
||||||
= f.button class: "btn btn-create accept_merge_request#{status_class}" do
|
.accept-action
|
||||||
Accept Merge Request
|
- if @ci_commit && @ci_commit.active?
|
||||||
- if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
|
%span.btn-group
|
||||||
.accept-control.checkbox
|
= link_to "#", class: "btn btn-create merge_when_build_succeeds" do
|
||||||
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
|
Merge When Build Succeeds
|
||||||
= check_box_tag :should_remove_source_branch
|
%a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
|
||||||
Remove source branch
|
%span.caret
|
||||||
.accept-control.right
|
%span.sr-only
|
||||||
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
|
Select Merge Moment
|
||||||
= icon('edit')
|
%ul.dropdown-menu.dropdown-menu-right{ role: 'menu' }
|
||||||
Modify commit message
|
%li
|
||||||
.js-toggle-content.hide.prepend-top-20
|
= 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,
|
= render 'shared/commit_message_container', params: params,
|
||||||
text: @merge_request.merge_commit_message,
|
text: @merge_request.merge_commit_message,
|
||||||
rows: 14, hint: true
|
rows: 14, hint: true
|
||||||
|
|
||||||
|
= hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
|
||||||
|
|
||||||
:javascript
|
:javascript
|
||||||
$('.accept-mr-form').on('ajax:before', function() {
|
$('.accept_merge_request').on('click', function() {
|
||||||
var btn = $('.accept_merge_request');
|
$(this).html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
|
||||||
btn.disable();
|
});
|
||||||
btn.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
|
.tree-ref-holder
|
||||||
= render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
|
= render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
- page_title "Network", @ref
|
- 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"
|
= render "head"
|
||||||
.project-network
|
.project-network
|
||||||
.controls
|
.controls
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
= icon('pencil-square-o')
|
= icon('pencil-square-o')
|
||||||
Manage group members
|
Manage group members
|
||||||
%ul.content-list
|
%ul.content-list
|
||||||
- members.each do |member|
|
- members.limit(20).each do |member|
|
||||||
= render 'groups/group_members/group_member', member: member, show_controls: false
|
= render 'groups/group_members/group_member', member: member, show_controls: false
|
||||||
- if members.count > 20
|
- if members.count > 20
|
||||||
%li
|
%li
|
||||||
|
|
|
@ -30,3 +30,7 @@
|
||||||
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
|
= link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
|
||||||
= icon('folder fw')
|
= icon('folder fw')
|
||||||
New directory
|
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
|
.form-group.branch
|
||||||
= label_tag 'new_branch', 'Target branch', class: 'control-label'
|
= label_tag 'new_branch', 'Target branch', class: 'control-label'
|
||||||
.col-sm-10
|
.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
|
.js-create-merge-request-container
|
||||||
.checkbox
|
.checkbox
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
.issuable-details
|
.issuable-details
|
||||||
.page-title
|
.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_icon(@snippet.visibility_level, fw: false)
|
||||||
= visibility_level_label(@snippet.visibility_level)
|
= visibility_level_label(@snippet.visibility_level)
|
||||||
Snippet ##{@snippet.id}
|
Snippet ##{@snippet.id}
|
||||||
|
|
|
@ -8,16 +8,7 @@ class MergeWorker
|
||||||
current_user = User.find(current_user_id)
|
current_user = User.find(current_user_id)
|
||||||
merge_request = MergeRequest.find(merge_request_id)
|
merge_request = MergeRequest.find(merge_request_id)
|
||||||
|
|
||||||
result = MergeRequests::MergeService.new(merge_request.target_project, current_user).
|
MergeRequests::MergeService.new(merge_request.target_project, current_user, params).
|
||||||
execute(merge_request, params[:commit_message])
|
execute(merge_request)
|
||||||
|
|
||||||
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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -76,7 +76,7 @@ production: &base
|
||||||
# This happens when the commit is pushed or merged into the default branch of a project.
|
# 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.
|
# 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.
|
# 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 project features settings
|
||||||
default_projects_features:
|
default_projects_features:
|
||||||
|
|
|
@ -60,7 +60,7 @@ Devise.setup do |config|
|
||||||
# It will change confirmation, password recovery and other workflows
|
# It will change confirmation, password recovery and other workflows
|
||||||
# to behave the same regardless if the e-mail provided was right or wrong.
|
# to behave the same regardless if the e-mail provided was right or wrong.
|
||||||
# Does not affect registerable.
|
# Does not affect registerable.
|
||||||
# config.paranoid = true
|
config.paranoid = true
|
||||||
|
|
||||||
# ==> Configuration for :database_authenticatable
|
# ==> Configuration for :database_authenticatable
|
||||||
# For bcrypt, this is the cost for hashing the password and defaults to 10. If
|
# 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."
|
success: "Successfully authenticated from %{kind} account."
|
||||||
passwords:
|
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."
|
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_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."
|
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."
|
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
|
resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do
|
||||||
member do
|
member do
|
||||||
get :diffs
|
|
||||||
get :commits
|
get :commits
|
||||||
post :merge
|
get :diffs
|
||||||
|
get :builds
|
||||||
get :merge_check
|
get :merge_check
|
||||||
|
post :merge
|
||||||
|
post :cancel_merge_when_build_succeeds
|
||||||
get :ci_status
|
get :ci_status
|
||||||
post :toggle_subscription
|
post :toggle_subscription
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class InitSchema < ActiveRecord::Migration
|
class InitSchema < ActiveRecord::Migration
|
||||||
def up
|
def up
|
||||||
|
|
||||||
create_table "events", force: true do |t|
|
create_table "events", force: true do |t|
|
||||||
t.string "target_type"
|
t.string "target_type"
|
||||||
t.integer "target_id"
|
t.integer "target_id"
|
||||||
|
@ -12,14 +12,14 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.integer "action"
|
t.integer "action"
|
||||||
t.integer "author_id"
|
t.integer "author_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "events", ["action"], name: "index_events_on_action", using: :btree
|
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", ["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", ["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", ["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_id"], name: "index_events_on_target_id", using: :btree
|
||||||
add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree
|
add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree
|
||||||
|
|
||||||
create_table "issues", force: true do |t|
|
create_table "issues", force: true do |t|
|
||||||
t.string "title"
|
t.string "title"
|
||||||
t.integer "assignee_id"
|
t.integer "assignee_id"
|
||||||
|
@ -33,7 +33,7 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.text "description"
|
t.text "description"
|
||||||
t.integer "milestone_id"
|
t.integer "milestone_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
|
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", ["author_id"], name: "index_issues_on_author_id", using: :btree
|
||||||
add_index "issues", ["closed"], name: "index_issues_on_closed", 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", ["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", ["project_id"], name: "index_issues_on_project_id", using: :btree
|
||||||
add_index "issues", ["title"], name: "index_issues_on_title", using: :btree
|
add_index "issues", ["title"], name: "index_issues_on_title", using: :btree
|
||||||
|
|
||||||
create_table "keys", force: true do |t|
|
create_table "keys", force: true do |t|
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
|
@ -51,11 +51,11 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.string "identifier"
|
t.string "identifier"
|
||||||
t.integer "project_id"
|
t.integer "project_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "keys", ["identifier"], name: "index_keys_on_identifier", using: :btree
|
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", ["project_id"], name: "index_keys_on_project_id", using: :btree
|
||||||
add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree
|
add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "merge_requests", force: true do |t|
|
create_table "merge_requests", force: true do |t|
|
||||||
t.string "target_branch", null: false
|
t.string "target_branch", null: false
|
||||||
t.string "source_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.boolean "closed", default: false, null: false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.text "st_commits", limit: 2147483647
|
t.text "st_commits"
|
||||||
t.text "st_diffs", limit: 2147483647
|
t.text "st_diffs"
|
||||||
t.boolean "merged", default: false, null: false
|
t.boolean "merged", default: false, null: false
|
||||||
t.integer "state", default: 1, null: false
|
t.integer "state", default: 1, null: false
|
||||||
t.integer "milestone_id"
|
t.integer "milestone_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
|
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", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree
|
||||||
add_index "merge_requests", ["closed"], name: "index_merge_requests_on_closed", 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", ["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", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree
|
||||||
add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
|
add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
|
||||||
|
|
||||||
create_table "milestones", force: true do |t|
|
create_table "milestones", force: true do |t|
|
||||||
t.string "title", null: false
|
t.string "title", null: false
|
||||||
t.integer "project_id", null: false
|
t.integer "project_id", null: false
|
||||||
|
@ -92,10 +92,10 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
|
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
|
add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
|
||||||
|
|
||||||
create_table "namespaces", force: true do |t|
|
create_table "namespaces", force: true do |t|
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.string "path", null: false
|
t.string "path", null: false
|
||||||
|
@ -104,12 +104,12 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "type"
|
t.string "type"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree
|
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", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
|
||||||
add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree
|
add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree
|
||||||
add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
|
add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
|
||||||
|
|
||||||
create_table "notes", force: true do |t|
|
create_table "notes", force: true do |t|
|
||||||
t.text "note"
|
t.text "note"
|
||||||
t.string "noteable_type"
|
t.string "noteable_type"
|
||||||
|
@ -122,13 +122,13 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.string "commit_id"
|
t.string "commit_id"
|
||||||
t.integer "noteable_id"
|
t.integer "noteable_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
|
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", ["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", ["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", "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
|
add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
|
||||||
|
|
||||||
create_table "projects", force: true do |t|
|
create_table "projects", force: true do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.string "path"
|
t.string "path"
|
||||||
|
@ -144,17 +144,17 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.boolean "wiki_enabled", default: true, null: false
|
t.boolean "wiki_enabled", default: true, null: false
|
||||||
t.integer "namespace_id"
|
t.integer "namespace_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
|
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
|
add_index "projects", ["owner_id"], name: "index_projects_on_owner_id", using: :btree
|
||||||
|
|
||||||
create_table "protected_branches", force: true do |t|
|
create_table "protected_branches", force: true do |t|
|
||||||
t.integer "project_id", null: false
|
t.integer "project_id", null: false
|
||||||
t.string "name", null: false
|
t.string "name", null: false
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "services", force: true do |t|
|
create_table "services", force: true do |t|
|
||||||
t.string "type"
|
t.string "type"
|
||||||
t.string "title"
|
t.string "title"
|
||||||
|
@ -165,9 +165,9 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.boolean "active", default: false, null: false
|
t.boolean "active", default: false, null: false
|
||||||
t.string "project_url"
|
t.string "project_url"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
|
add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
|
||||||
|
|
||||||
create_table "snippets", force: true do |t|
|
create_table "snippets", force: true do |t|
|
||||||
t.string "title"
|
t.string "title"
|
||||||
t.text "content"
|
t.text "content"
|
||||||
|
@ -178,11 +178,11 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.string "file_name"
|
t.string "file_name"
|
||||||
t.datetime "expires_at"
|
t.datetime "expires_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree
|
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", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree
|
||||||
add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
|
add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
|
||||||
|
|
||||||
create_table "taggings", force: true do |t|
|
create_table "taggings", force: true do |t|
|
||||||
t.integer "tag_id"
|
t.integer "tag_id"
|
||||||
t.integer "taggable_id"
|
t.integer "taggable_id"
|
||||||
|
@ -192,14 +192,14 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.string "context"
|
t.string "context"
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree
|
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
|
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|
|
create_table "tags", force: true do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "user_team_project_relationships", force: true do |t|
|
create_table "user_team_project_relationships", force: true do |t|
|
||||||
t.integer "project_id"
|
t.integer "project_id"
|
||||||
t.integer "user_team_id"
|
t.integer "user_team_id"
|
||||||
|
@ -207,7 +207,7 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "user_team_user_relationships", force: true do |t|
|
create_table "user_team_user_relationships", force: true do |t|
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
t.integer "user_team_id"
|
t.integer "user_team_id"
|
||||||
|
@ -216,7 +216,7 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "user_teams", force: true do |t|
|
create_table "user_teams", force: true do |t|
|
||||||
t.string "name"
|
t.string "name"
|
||||||
t.string "path"
|
t.string "path"
|
||||||
|
@ -224,7 +224,7 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.datetime "created_at"
|
t.datetime "created_at"
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "users", force: true do |t|
|
create_table "users", force: true do |t|
|
||||||
t.string "email", default: "", null: false
|
t.string "email", default: "", null: false
|
||||||
t.string "encrypted_password", default: "", null: false
|
t.string "encrypted_password", default: "", null: false
|
||||||
|
@ -255,7 +255,7 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.string "provider"
|
t.string "provider"
|
||||||
t.string "username"
|
t.string "username"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
|
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", ["blocked"], name: "index_users_on_blocked", using: :btree
|
||||||
add_index "users", ["email"], name: "index_users_on_email", unique: true, 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", ["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", ["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
|
add_index "users", ["username"], name: "index_users_on_username", using: :btree
|
||||||
|
|
||||||
create_table "users_projects", force: true do |t|
|
create_table "users_projects", force: true do |t|
|
||||||
t.integer "user_id", null: false
|
t.integer "user_id", null: false
|
||||||
t.integer "project_id", null: false
|
t.integer "project_id", null: false
|
||||||
|
@ -271,11 +271,11 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.integer "project_access", default: 0, null: false
|
t.integer "project_access", default: 0, null: false
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "users_projects", ["project_access"], name: "index_users_projects_on_project_access", using: :btree
|
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", ["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
|
add_index "users_projects", ["user_id"], name: "index_users_projects_on_user_id", using: :btree
|
||||||
|
|
||||||
create_table "web_hooks", force: true do |t|
|
create_table "web_hooks", force: true do |t|
|
||||||
t.string "url"
|
t.string "url"
|
||||||
t.integer "project_id"
|
t.integer "project_id"
|
||||||
|
@ -284,7 +284,7 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.string "type", default: "ProjectHook"
|
t.string "type", default: "ProjectHook"
|
||||||
t.integer "service_id"
|
t.integer "service_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "wikis", force: true do |t|
|
create_table "wikis", force: true do |t|
|
||||||
t.string "title"
|
t.string "title"
|
||||||
t.text "content"
|
t.text "content"
|
||||||
|
@ -294,10 +294,10 @@ class InitSchema < ActiveRecord::Migration
|
||||||
t.string "slug"
|
t.string "slug"
|
||||||
t.integer "user_id"
|
t.integer "user_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "wikis", ["project_id"], name: "index_wikis_on_project_id", using: :btree
|
add_index "wikis", ["project_id"], name: "index_wikis_on_project_id", using: :btree
|
||||||
add_index "wikis", ["slug"], name: "index_wikis_on_slug", using: :btree
|
add_index "wikis", ["slug"], name: "index_wikis_on_slug", using: :btree
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
class CreateMergeRequestDiffs < ActiveRecord::Migration
|
class CreateMergeRequestDiffs < ActiveRecord::Migration
|
||||||
def change
|
def up
|
||||||
create_table :merge_request_diffs do |t|
|
create_table :merge_request_diffs do |t|
|
||||||
t.string :state, null: false, default: 'collected'
|
t.string :state, null: false, default: 'collected'
|
||||||
t.text :st_commits, null: true, limit: 2147483647
|
t.text :st_commits, null: true
|
||||||
t.text :st_diffs, null: true, limit: 2147483647
|
t.text :st_diffs, null: true
|
||||||
t.integer :merge_request_id, null: false
|
t.integer :merge_request_id, null: false
|
||||||
|
|
||||||
t.timestamps
|
t.timestamps
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
class MigrateToNewShell < ActiveRecord::Migration
|
class MigrateToNewShell < ActiveRecord::Migration
|
||||||
def change
|
def change
|
||||||
|
return if Rails.env.test?
|
||||||
|
|
||||||
gitlab_shell_path = Gitlab.config.gitlab_shell.path
|
gitlab_shell_path = Gitlab.config.gitlab_shell.path
|
||||||
if system("#{gitlab_shell_path}/bin/create-hooks")
|
if system("#{gitlab_shell_path}/bin/create-hooks")
|
||||||
puts 'Repositories updated with new 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
|
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|
|
create_table "merge_requests", force: :cascade do |t|
|
||||||
t.string "target_branch", null: false
|
t.string "target_branch", null: false
|
||||||
t.string "source_branch", null: false
|
t.string "source_branch", null: false
|
||||||
t.integer "source_project_id", null: false
|
t.integer "source_project_id", null: false
|
||||||
t.integer "author_id"
|
t.integer "author_id"
|
||||||
t.integer "assignee_id"
|
t.integer "assignee_id"
|
||||||
t.string "title"
|
t.string "title"
|
||||||
|
@ -487,13 +487,16 @@ ActiveRecord::Schema.define(version: 20151203162133) do
|
||||||
t.integer "milestone_id"
|
t.integer "milestone_id"
|
||||||
t.string "state"
|
t.string "state"
|
||||||
t.string "merge_status"
|
t.string "merge_status"
|
||||||
t.integer "target_project_id", null: false
|
t.integer "target_project_id", null: false
|
||||||
t.integer "iid"
|
t.integer "iid"
|
||||||
t.text "description"
|
t.text "description"
|
||||||
t.integer "position", default: 0
|
t.integer "position", default: 0
|
||||||
t.datetime "locked_at"
|
t.datetime "locked_at"
|
||||||
t.integer "updated_by_id"
|
t.integer "updated_by_id"
|
||||||
t.string "merge_error"
|
t.string "merge_error"
|
||||||
|
t.text "merge_params"
|
||||||
|
t.boolean "merge_when_build_succeeds", default: false, null: false
|
||||||
|
t.integer "merge_user_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
|
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 Images](ci/docker/using_docker_images.md)
|
||||||
- [Using Docker Build](ci/docker/using_docker_build.md)
|
- [Using Docker Build](ci/docker/using_docker_build.md)
|
||||||
- [Using Variables](ci/variables/README.md)
|
- [Using Variables](ci/variables/README.md)
|
||||||
|
- [Using SSH keys](ci/ssh_keys/README.md)
|
||||||
- [User permissions](ci/permissions/README.md)
|
- [User permissions](ci/permissions/README.md)
|
||||||
- [API](ci/api/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
|
### CI Examples
|
||||||
|
|
||||||
- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md)
|
- [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:
|
Parameters:
|
||||||
|
|
||||||
- `id` (required) - The ID of a project
|
- `id` (required) - The ID of a project
|
||||||
- `merge_request_id` (required) - ID of MR
|
- `merge_request_id` (required) - ID of MR
|
||||||
- `merge_commit_message` (optional) - Custom merge commit message
|
- `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
|
```json
|
||||||
{
|
{
|
||||||
|
|
|
@ -398,7 +398,6 @@ Parameters:
|
||||||
- `user_id` (required) - user_id of owner
|
- `user_id` (required) - user_id of owner
|
||||||
- `name` (required) - new project name
|
- `name` (required) - new project name
|
||||||
- `description` (optional) - short project description
|
- `description` (optional) - short project description
|
||||||
- `default_branch` (optional) - 'master' by default
|
|
||||||
- `issues_enabled` (optional)
|
- `issues_enabled` (optional)
|
||||||
- `merge_requests_enabled` (optional)
|
- `merge_requests_enabled` (optional)
|
||||||
- `builds_enabled` (optional)
|
- `builds_enabled` (optional)
|
||||||
|
|
|
@ -9,6 +9,18 @@
|
||||||
+ [Using Docker Images](docker/using_docker_images.md)
|
+ [Using Docker Images](docker/using_docker_images.md)
|
||||||
+ [Using Docker Build](docker/using_docker_build.md)
|
+ [Using Docker Build](docker/using_docker_build.md)
|
||||||
+ [Using Variables](variables/README.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
|
### Examples
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,29 @@
|
||||||
# Using Docker Images
|
# 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
|
GitLab CI in conjuction with [GitLab Runner](../runners/README.md) can use
|
||||||
in independent "containers" that are run within a single Linux instance.
|
[Docker Engine](https://www.docker.com/) to test and build any application.
|
||||||
[Docker Hub](https://registry.hub.docker.com/) have rich database of built images that can be used to build applications.
|
|
||||||
|
|
||||||
Docker when used with GitLab CI runs each build in separate and isolated container using predefined image and always from scratch.
|
Docker is an open-source project that allows you to use predefined images to
|
||||||
It makes it easier to have simple and reproducible build environment that can also be run on your workstation.
|
run applications in independent "containers" that are run within a single Linux
|
||||||
This allows you to test all commands from your shell, rather than having to test them on a CI server.
|
instance. [Docker Hub][hub] has a rich database of prebuilt images that can be
|
||||||
|
used to test and build your applications.
|
||||||
|
|
||||||
### Register Docker runner
|
Docker, when used with GitLab CI, runs each build in a separate and isolated
|
||||||
To use GitLab Runner with Docker you need to register new runner to use `docker` executor:
|
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
|
```bash
|
||||||
gitlab-ci-multi-runner register \
|
gitlab-runner register \
|
||||||
--url "https://gitlab.com/" \
|
--url "https://gitlab.com/" \
|
||||||
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
--registration-token "PROJECT_REGISTRATION_TOKEN" \
|
||||||
--description "docker-ruby-2.1" \
|
--description "docker-ruby-2.1" \
|
||||||
|
@ -23,101 +33,79 @@ gitlab-ci-multi-runner register \
|
||||||
--docker-mysql latest
|
--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?
|
## 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 service?
|
The `image` keyword is the name of the docker image that is present in the
|
||||||
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.
|
local Docker Engine (list all images with `docker images`) or any image that
|
||||||
The service image can run any application, but most common use case is to run some database container, ie.: `mysql`.
|
can be found at [Docker Hub][hub]. For more information about images and Docker
|
||||||
It's easier and faster to use existing image, run it as additional container than install `mysql` every time project is built.
|
Hub please read the [Docker Fundamentals][] documentation.
|
||||||
|
|
||||||
#### How is service linked to the build?
|
In short, with `image` we refer to the docker image, which will be used to
|
||||||
There's good document that describes how Docker linking works: [Linking containers together](https://docs.docker.com/userguide/dockerlinks/).
|
create a container on which your build will run.
|
||||||
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`**.
|
|
||||||
|
|
||||||
### How to use other images as services?
|
## What is service
|
||||||
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"]
|
|
||||||
```
|
|
||||||
|
|
||||||
For example you need `wordpress` instance to test some API integration with `Wordpress`.
|
The `services` keyword defines just another docker image that is run during
|
||||||
You can for example use this image: [tutum/wordpress](https://registry.hub.docker.com/u/tutum/wordpress/).
|
your build and is linked to the docker image that the `image` keyword defines.
|
||||||
This is image that have fully preconfigured `wordpress` and have `MySQL` server built-in:
|
This allows you to access the service image during build time.
|
||||||
```
|
|
||||||
[runners.docker]
|
|
||||||
image = "ruby:2.1"
|
|
||||||
services = ["mysql:latest", "postgres:latest", "tutum/wordpress:latest"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Next time when you run your application the `tutum/wordpress` will be started
|
The service image can run any application, but the most common use case is to
|
||||||
and you will have access to it from your build container under hostname: `tutum__wordpress`.
|
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:
|
You can see some widely used services examples in the relevant documentation of
|
||||||
1. Everything after `:` is stripped,
|
[CI services examples](../services/README.md).
|
||||||
2. '/' is replaced with `__`.
|
|
||||||
|
|
||||||
### Configuring services
|
### How is service linked to the build
|
||||||
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 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,
|
To summarize, if you add `mysql` as service to your application, the image will
|
||||||
you need to set POSTGRES_DB.
|
then be used to create a container that is linked to the build container.
|
||||||
|
|
||||||
```yaml
|
The service container for MySQL will be accessible under the hostname `mysql`.
|
||||||
services:
|
So, in order to access your database service you have to connect to the host
|
||||||
- postgres
|
named `mysql` instead of a socket or `localhost`.
|
||||||
|
|
||||||
variables:
|
|
||||||
POSTGRES_DB: gitlab
|
|
||||||
```
|
|
||||||
|
|
||||||
1. To use [mysql](https://registry.hub.docker.com/u/library/mysql/) service with empty password for time of build,
|
## Overwrite image and services
|
||||||
you need to set MYSQL_ALLOW_EMPTY_PASSWORD.
|
|
||||||
|
|
||||||
```yaml
|
See [How to use other images as services](#how-to-use-other-images-as-services).
|
||||||
services:
|
|
||||||
- mysql
|
|
||||||
|
|
||||||
variables:
|
|
||||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
|
||||||
```
|
|
||||||
|
|
||||||
For other possible configuration variables check the
|
## How to use other images as services
|
||||||
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.
|
|
||||||
|
|
||||||
**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
|
## Define image and services from `.gitlab-ci.yml`
|
||||||
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
|
You can simply define an image that will be used for all jobs and a list of
|
||||||
be used instead of the ones that were specified during runner's registration.
|
services that you want to use during build time.
|
||||||
```
|
|
||||||
|
```yaml
|
||||||
image: ruby:2.2
|
image: ruby:2.2
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- postgres:9.3
|
- postgres:9.3
|
||||||
before_install:
|
|
||||||
|
before_script:
|
||||||
- bundle install
|
- bundle install
|
||||||
|
|
||||||
test:
|
test:
|
||||||
script:
|
script:
|
||||||
- bundle exec rake spec
|
- bundle exec rake spec
|
||||||
```
|
```
|
||||||
|
|
||||||
It's possible to define image and service per-job:
|
It is also possible to define different images and services per job:
|
||||||
```
|
|
||||||
before_install:
|
```yaml
|
||||||
|
before_script:
|
||||||
- bundle install
|
- bundle install
|
||||||
|
|
||||||
test:2.1:
|
test:2.1:
|
||||||
|
@ -135,34 +123,73 @@ test:2.2:
|
||||||
- bundle exec rake spec
|
- bundle exec rake spec
|
||||||
```
|
```
|
||||||
|
|
||||||
#### How to enable overwriting?
|
## Define image and services in `config.toml`
|
||||||
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`.
|
Look for the `[runners.docker]` section:
|
||||||
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`:
|
|
||||||
```
|
```
|
||||||
[runners.docker]
|
[runners.docker]
|
||||||
image = "ruby:2.1"
|
image = "ruby:2.1"
|
||||||
allowed_images = ["ruby:*", "python:*"]
|
services = ["mysql:latest", "postgres:latest"]
|
||||||
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 = ["*", "*/*"]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**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 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. Create build container and link any service container to build container.
|
||||||
1. Start build container and send build script to the container.
|
1. Start build container and send build script to the container.
|
||||||
1. Run build script.
|
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. Check exit status of build script.
|
||||||
1. Remove build container and all created service containers.
|
1. Remove build container and all created service containers.
|
||||||
|
|
||||||
### How to debug a build locally
|
## How to debug a build locally
|
||||||
1. Create a file with build script:
|
|
||||||
|
*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
|
```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
|
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
|
cd /builds/gitlab-org/gitlab-ci-multi-runner
|
||||||
make <- or any other build step
|
make
|
||||||
EOF
|
EOF
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Create service containers:
|
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.
|
||||||
$ docker run -d -n service-mysql mysql:latest
|
Your mileage may vary, so instead of `make` you could run the command which
|
||||||
$ docker run -d -n service-postgres postgres:latest
|
is specific to your project.
|
||||||
```
|
|
||||||
This will create two service containers (MySQL and PostgreSQL).
|
|
||||||
|
|
||||||
1. Create a build container and execute script in its context:
|
Then create some service containers:
|
||||||
```
|
|
||||||
$ docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script
|
```
|
||||||
```
|
docker run -d -n service-mysql mysql:latest
|
||||||
This will create build container that has two service containers linked.
|
docker run -d -n service-postgres postgres:latest
|
||||||
The build_script is piped using STDIN to bash interpreter which executes the build script in container.
|
```
|
||||||
|
|
||||||
|
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
|
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
|
# 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
|
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 or merged
|
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.
|
||||||
into the default branch of a project.
|
|
||||||
|
|
||||||
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
|
```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:
|
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
|
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.
|
||||||
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
|
Tip: you can test this closing pattern at [http://rubular.com][1]. Use this site
|
||||||
to test your own patterns.
|
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
|
## Change the pattern
|
||||||
|
|
||||||
For Omnibus installs you can change the default pattern in `/etc/gitlab/gitlab.rb`:
|
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
|
[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example
|
||||||
[1]: http://rubular.com/r/Xmbexed1OJ
|
[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
|
sudo editor /etc/gitlab/gitlab.rb
|
||||||
```
|
```
|
||||||
|
|
||||||
For instalations from source:
|
For installations from source:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /home/git/gitlab
|
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
|
sudo editor /etc/gitlab/gitlab.rb
|
||||||
```
|
```
|
||||||
|
|
||||||
For instalations from source:
|
For installations from source:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /home/git/gitlab
|
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
|
sudo editor /etc/gitlab/gitlab.rb
|
||||||
```
|
```
|
||||||
|
|
||||||
For instalations from source:
|
For installations from source:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /home/git/gitlab
|
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
|
sudo editor /etc/gitlab/gitlab.rb
|
||||||
```
|
```
|
||||||
|
|
||||||
For instalations from source:
|
For installations from source:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /home/git/gitlab
|
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
|
sudo editor /etc/gitlab/gitlab.rb
|
||||||
```
|
```
|
||||||
|
|
||||||
For instalations from source:
|
For installations from source:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /home/git/gitlab
|
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
|
sudo editor /etc/gitlab/gitlab.rb
|
||||||
```
|
```
|
||||||
|
|
||||||
For instalations from source:
|
For installations from source:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /home/git/gitlab
|
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
|
sudo editor /etc/gitlab/gitlab.rb
|
||||||
```
|
```
|
||||||
|
|
||||||
For instalations from source:
|
For installations from source:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd /home/git/gitlab
|
cd /home/git/gitlab
|
||||||
|
|
|
@ -85,11 +85,10 @@ sudo -u git -H git checkout 0.4.2
|
||||||
sudo -u git -H make
|
sudo -u git -H make
|
||||||
```
|
```
|
||||||
|
|
||||||
Update the GitLab init script and 'default' file.
|
Update the GitLab 'default' file.
|
||||||
|
|
||||||
```
|
```
|
||||||
cd /home/git/gitlab
|
cd /home/git/gitlab
|
||||||
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
|
|
||||||
test -e /etc/default/gitlab && \
|
test -e /etc/default/gitlab && \
|
||||||
sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /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
|
```bash
|
||||||
cd /home/git/gitlab-workhorse
|
cd /home/git/gitlab-workhorse
|
||||||
sudo -u git -H git fetch
|
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.
|
### 5. Install libs, migrations, etc.
|
||||||
|
|
|
@ -17,4 +17,5 @@
|
||||||
- [Milestones](milestones.md)
|
- [Milestones](milestones.md)
|
||||||
- [Merge Requests](merge_requests.md)
|
- [Merge Requests](merge_requests.md)
|
||||||
- ["Work In Progress" Merge Requests](wip_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)
|
- [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