Commit 740feeec authored by Douwe Maan's avatar Douwe Maan

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

parents 7851a292 940d68cc
Please view this file on the master branch, on stable branches it's out of date.
v 8.2.0 (unreleased)
- Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
- Improved performance of replacing references in comments
- Fix duplicate repositories in GitHub import page (Stan Hu)
- Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
- Show last project commit to default branch on project home page
- Highlight comment based on anchor in URL
- Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw)
- Improved performance of sorting milestone issues
- Allow users to select the Files view as default project view (Cristian Bica)
v 8.1.0 (unreleased)
- Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy)
- Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork
- Use git follow flag for commits page when retrieve history for file or directory
- Show merge request CI status on merge requests index page
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
v 8.1.1
- Fix cloning Wiki repositories via HTTP (Stan Hu)
- Add migration to remove satellites directory
- Fix specific runners visibility
- Fix 500 when editing CI service
- Require CI jobs to be named
- Fix CSS for runner status
- Fix CI badge
- Allow developer to manage builds
v 8.1.0
- Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
- Fix duplicate repositories in GitHub import page (Stan Hu)
- Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
- Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
- Show notifications button when user is member of group rather than project (Grzegorz Bizon)
- Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
- Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu)
- Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu)
- Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu)
- Speed up load times of issue detail pages by roughly 1.5x
- Fix CI rendering regressions
- If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg)
- Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu)
- Make diff file view easier to use on mobile screens (Stan Hu)
......@@ -27,8 +44,10 @@ v 8.1.0 (unreleased)
- Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
- Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
- Improved performance of the trending projects page
- Remove CI migration task
- Improved performance of finding projects by their namespace
- Fix bug where transferring a project would result in stale commit links (Stan Hu)
- Fix build trace updating
- Include full path of source and target branch names in New Merge Request page (Stan Hu)
- Add user preference to view activities as default dashboard (Stan Hu)
- Add option to admin area to sign in as a specific user (Pavel Forkert)
......@@ -71,6 +90,7 @@ v 8.1.0 (unreleased)
- Fix position of hamburger in header for smaller screens (Han Loong Liauw)
- Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
- Persist filters when sorting on admin user page (Jerry Lukins)
- Update style of snippets pages (Han Loong Liauw)
- Allow dashboard and group issues/MRs to be filtered by label
- Add spellcheck=false to certain input fields
- Invalidate stored service password if the endpoint URL is changed
......@@ -83,11 +103,11 @@ v 8.1.0 (unreleased)
- Let gitlab-git-http-server generate and serve 'git archive' downloads
- Optimize query when filtering on issuables (Zeger-Jan van de Weg)
- Fix padding of outdated discussion item.
- Animate the logo on hover
v 8.0.5
- Correct lookup-by-email for LDAP logins
- Fix loading spinner sometimes not being hidden on Merge Request tab switches
- Animate the logo on hover
v 8.0.4
- Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
......
......@@ -197,7 +197,7 @@ gem 'bootstrap-sass', '~> 3.0'
gem 'font-awesome-rails', '~> 4.2'
gem 'gitlab_emoji', '~> 0.1'
gem 'gon', '~> 5.0.0'
gem 'jquery-atwho-rails', '~> 1.0.0'
gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'jquery-rails', '~> 3.1.3'
gem 'jquery-scrollto-rails', '~> 1.4.3'
gem 'jquery-ui-rails', '~> 4.2.1'
......
......@@ -354,7 +354,7 @@ GEM
ice_nine (0.11.1)
inflecto (0.0.2)
ipaddress (0.8.0)
jquery-atwho-rails (1.0.1)
jquery-atwho-rails (1.3.2)
jquery-rails (3.1.3)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
......@@ -840,7 +840,7 @@ DEPENDENCIES
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
jquery-atwho-rails (~> 1.0.0)
jquery-atwho-rails (~> 1.3.2)
jquery-rails (~> 3.1.3)
jquery-scrollto-rails (~> 1.4.3)
jquery-turbolinks (~> 2.0.1)
......
No preview for this file type
File mode changed from 100755 to 100644
No preview for this file type
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
......@@ -22,7 +22,7 @@ class CiBuild
# Only valid for runnig build when output changes during time
#
CiBuild.interval = setInterval =>
if window.location.href is build_url
if window.location.href.split("#").first() is build_url
$.ajax
url: build_url
dataType: "json"
......@@ -31,7 +31,7 @@ class CiBuild
$('#build-trace code').html build.trace_html
$('#build-trace code').append '<i class="fa fa-refresh fa-spin"/>'
@checkAutoscroll()
else
else if build.status != build_status
Turbolinks.visit build_url
, 4000
......
#= require clipboard
$ ->
clipboard = new Clipboard '.js-clipboard-trigger',
text: (trigger) ->
$target = $(trigger.nextElementSibling || trigger.previousElementSibling)
$target.data('clipboard-text') || $target.text().trim()
clipboard.on 'success', (e) ->
$(e.trigger).
tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!').
tooltip('show')
# Clear the selection and blur the trigger so it loses its border
e.clearSelection()
$(e.trigger).blur()
# Manually hide the tooltip after 1 second
setTimeout(->
$(e.trigger).tooltip('hide')
, 1000)
......@@ -162,10 +162,21 @@
border-color: #e7e9ed;
width: 140px;
.badge {
font-weight: normal;
background-color: #eee;
color: #78a;
}
&.active {
border-color: $gl-info;
background: $gl-info;
color: #fff;
.badge {
color: $gl-info;
background-color: white;
}
}
}
}
......@@ -147,14 +147,8 @@
.badge {
font-weight: normal;
background-color: #fff;
background-color: #eee;
color: #78a;
}
}
}
.fa-align {
top: 20px;
position: relative;
}
......@@ -80,3 +80,24 @@
}
}
}
.issuable-filter-count {
span {
display: block;
margin-bottom: -16px;
padding: 13px 0;
}
}
.cross-project-reference {
text-align: center;
width: 100%;
.slead {
padding: 5px;
}
span, button {
background-color: $background-color;
}
}
......@@ -205,6 +205,15 @@
#modal_merge_info .modal-dialog {
width: 600px;
.btn-clipboard {
@extend .pull-right;
margin-right: 18px;
margin-top: 5px;
position: absolute;
right: 0;
}
}
.mr-source-target {
......
......@@ -50,7 +50,17 @@
}
.project-home-dropdown {
margin: 11px 3px 0;
margin: 13px 0px 0;
}
.notifications-btn {
.fa-bell {
margin-right: 6px;
}
.fa-angle-down {
margin-left: 6px;
}
}
.project-home-desc {
......@@ -85,6 +95,7 @@
color: inherit;
}
}
.input-group {
display: inline-table;
position: relative;
......@@ -233,23 +244,11 @@
}
}
.fa-fw {
i {
margin-right: 8px;
}
}
.fa-bell {
margin-right: 6px;
}
.fa-angle-down {
margin-left: 6px;
}
.project-home-panel .project-home-dropdown {
margin: 13px 0px 0;
}
.project-visibility-level-holder {
.radio {
margin-bottom: 10px;
......@@ -544,5 +543,13 @@ pre.light-well {
}
.project-show-readme .readme-holder {
margin-left: -$gl-padding;
margin-right: -$gl-padding;
padding: ($gl-padding + 7px);
border-top: 0;
.edit-project-readme {
z-index: 100;
position: relative;
}
}
.ci-body {
.runner-state {
padding: 6px 12px;
margin-right: 10px;
color: #FFF;
.runner-state {
padding: 6px 12px;
margin-right: 10px;
color: #FFF;
&.runner-state-shared {
background: #32b186;
}
&.runner-state-specific {
background: #3498db;
}
&.runner-state-shared {
background: #32b186;
}
.runner-status-online {
color: green;
&.runner-state-specific {
background: #3498db;
}
}
.runner-status-offline {
color: gray;
}
.runner-status-online {
color: green;
}
.runner-status-paused {
color: red;
}
.runner-status-offline {
color: gray;
}
.runner-status-paused {
color: red;
}
.runner {
.btn {
padding: 1px 6px;
}
.runner {
.btn {
padding: 1px 6px;
}
h4 {
font-weight: normal;
}
h4 {
font-weight: normal;
}
}
.my-snippets li:first-child {
h4 { margin-top: 0; }
padding-top: 0;
}
.snippet-form-holder .file-holder .file-title {
padding: 2px;
}
......@@ -30,3 +25,58 @@
}
}
}
.snippet-holder {
.snippet-details {
.page-title {
margin-top: -15px;
padding: 10px 0;
margin-bottom: 0;
color: #5c5d5e;
font-size: 16px;
.author {
color: #5c5d5e;
}
.snippet-id {
color: #5c5d5e;
}
}
.snippet-title {
margin: 0;
font-size: 23px;
color: #313236;
}
@media (max-width: $screen-md-max) {
.new-snippet-link {
display: none;
}
}
@media (max-width: $screen-sm-max) {
.creator,
.page-title .btn-close {
display: none;
}
}
}
.file-holder {
border-top: 0;
}
}
.snippet-box {
@include border-radius(2px);
display: inline-block;
padding: 10px $gl-padding;
font-weight: normal;
margin-right: 10px;
font-size: $gl-font-size;
border: 1px solid;
}
......@@ -5,7 +5,7 @@
tr {
> td, > th {
line-height: 32px;
line-height: 28px;
}
&:hover {
......
......@@ -124,7 +124,6 @@ class ApplicationController < ActionController::Base
project_path = "#{namespace}/#{id}"
@project = Project.find_with_namespace(project_path)
if @project and can?(current_user, :read_project, @project)
if @project.path_with_namespace != project_path
redirect_to request.original_url.gsub(project_path, @project.path_with_namespace) and return
......
......@@ -17,6 +17,7 @@ module Ci
@projects = @projects.where(gitlab_id: @gl_projects.select(:id))
end
@projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any?
@projects = @projects.joins(:gl_project)
@projects = @projects.page(params[:page]).per(30)
end
......
......@@ -8,14 +8,6 @@ module Ci
private
def authenticate_public_page!
unless project.public
authenticate_user!
return access_denied! unless can?(current_user, :read_project, gl_project)
end
end
def authenticate_token!
unless project.valid_token?(params[:token])
return head(403)
......
......@@ -2,23 +2,24 @@ class Projects::BuildsController < Projects::ApplicationController
before_action :ci_project
before_action :build, except: [:index, :cancel_all]
before_action :authorize_admin_project!, except: [:index, :show, :status]
before_action :authorize_manage_builds!, except: [:index, :show, :status]
layout "project"
def index
@scope = params[:scope]
@all_builds = project.ci_builds
@builds = @all_builds.order('created_at DESC')
@builds =
case @scope
when 'all'
@all_builds
@builds
when 'finished'
@all_builds.finished
@builds.finished
else
@all_builds.running_or_pending
@builds.running_or_pending.reverse_order
end
@builds = @builds.order('created_at DESC').page(params[:page]).per(30)
@builds = @builds.page(params[:page]).per(30)
end
def cancel_all
......@@ -73,4 +74,10 @@ class Projects::BuildsController < Projects::ApplicationController
def build_path(build)
namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
end
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return page_404
end
end
end
......@@ -14,17 +14,17 @@ class Projects::CiServicesController < Projects::ApplicationController
end
def update
if @service.update_attributes(service_params)
redirect_to edit_namespace_project_ci_service_path(@project, @project.namespace, @service.to_param)
if service.update_attributes(service_params)
redirect_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param)
else
render 'edit'
end
end
def test
last_build = @project.builds.last
last_build = @project.ci_builds.last
if @service.execute(last_build)
if service.execute(last_build)
message = { notice: 'We successfully tested the service' }
else
message = { alert: 'We tried to test the service but error occurred' }
......
......@@ -4,7 +4,8 @@
class Projects::CommitController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :authorize_download_code!, except: [:cancel_builds]
before_action :authorize_manage_builds!, only: [:cancel_builds]
before_action :commit
def show
......@@ -55,4 +56,12 @@ class Projects::CommitController < Projects::ApplicationController
def commit
@commit ||= @project.commit(params[:id])
end
private
def authorize_manage_builds!
unless can?(current_user, :manage_builds, project)
return page_404
end
end
end
......@@ -12,7 +12,7 @@ class Projects::CommitsController < Projects::ApplicationController
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
@commits = @repo.commits(@ref, @path, @limit, @offset)
@note_counts = Note.where(commit_id: @commits.map(&:id)).
@note_counts = project.notes.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
respond_to do |format|
......
......@@ -6,11 +6,10 @@ class Projects::RunnersController < Projects::ApplicationController
layout 'project_settings'
def index
@runners = @ci_project.runners.order('id DESC')
@specific_runners =
Ci::Runner.specific.includes(:runner_projects).
where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ).
where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20)
@runners = @ci_project.runners.ordered
@specific_runners = current_user.ci_authorized_runners.
where.not(id: @ci_project.runners).
ordered.page(params[:page]).per(20)
@shared_runners = Ci::Runner.shared.active
@shared_runners_count = @shared_runners.count(:all)
end
......
......@@ -21,6 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController
filter: :by_project,
project: @project
})
@snippets = @snippets.page(params[:page]).per(PER_PAGE)
end
def new
......
......@@ -124,11 +124,7 @@ class ProjectsController < ApplicationController
::Projects::DestroyService.new(@project, current_user, {}).execute
flash[:alert] = "Project '#{@project.name}' was deleted."
if request.referer.include?('/admin')
redirect_to admin_namespaces_projects_path
else
redirect_to dashboard_projects_path
end
redirect_back_or_default(default: dashboard_projects_path, options: {})
rescue Projects::DestroyService::DestroyError => ex
redirect_to edit_project_path(@project), alert: ex.message
end
......
......@@ -42,4 +42,13 @@ module CiStatusHelper
icon(icon_name)
end
def render_ci_status(ci_commit)
link_to ci_status_path(ci_commit),
class: "c#{ci_status_color(ci_commit)}",
title: "Build status: #{ci_commit.status}",
data: { toggle: 'tooltip', placement: 'left' } do
ci_status_icon(ci_commit)
end
end
end
module ClipboardHelper
def clipboard_button
content_tag :button,
icon('clipboard'),
class: 'btn btn-xs btn-clipboard js-clipboard-trigger',
type: :button
end
end
......@@ -110,22 +110,4 @@ module TabHelper
'active'
end
end
# Use nav_tab for save controller/action but different params
def nav_tab(key, value, &block)
o = {}
o[:class] = ""
if value.nil?
o[:class] << " active" if params[key].blank?
else
o[:class] << " active" if params[key] == value
end
if block_given?
content_tag(:li, capture(&block), o)
else
content_tag(:li, nil, o)
end
end
end
......@@ -99,6 +99,7 @@ module Ci
def ordered_by_last_commit_date
last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)"
joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id").
joins(:gl_project).
order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC")
end
end
......
......@@ -27,9 +27,5 @@ module Ci
def human_status
status
end
def last_commit_for_ref(ref)
commits.where(ref: ref).last
end
end
end
......@@ -36,6 +36,7 @@ module Ci
scope :active, ->() { where(active: true) }
scope :paused, ->() { where(active: false) }
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
scope :ordered, ->() { order(id: :desc) }
acts_as_taggable
......
......@@ -20,7 +20,6 @@ class CommitStatus < ActiveRecord::Base
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
scope :ordered, -> { order(:ref, :stage_idx, :name) }
scope :for_ref, ->(ref) { where(ref: ref) }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
state_machine :status, initial: :pending do
event :run do
......
......@@ -159,11 +159,11 @@ class MergeRequest < ActiveRecord::Base
def last_commit
merge_request_diff ? merge_request_diff.last_commit : compare_commits.last
end
end
def first_commit
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
end
def last_commit_short_sha
last_commit.short_id
......@@ -257,7 +257,7 @@ class MergeRequest < ActiveRecord::Base
Note.where(
"(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" +
"(project_id = :source_project_id AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
"((project_id = :source_project_id OR project_id = :target_project_id) AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))",
mr_id: id,
commit_ids: commit_ids,
target_project_id: target_project_id,
......@@ -470,4 +470,10 @@ class MergeRequest < ActiveRecord::Base
unlock_mr if locked?
end
end
def ci_commit
if last_commit
source_project.ci_commit(last_commit.id)
end
end
end
......@@ -243,11 +243,12 @@ class Project < ActiveRecord::Base
# Use of unscoped ensures we're not secretly adding any ORDER BYs, which
# have a negative impact on performance (and aren't needed for this
# query).
unscoped.
projects = unscoped.
joins(:namespace).
iwhere('namespaces.path' => namespace_path).
iwhere('projects.path' => project_path).
take
iwhere('namespaces.path' => namespace_path)
projects.where('projects.path' => project_path).take ||
projects.iwhere('projects.path' => project_path).take
end
def visibility_levels
......@@ -567,7 +568,7 @@ class Project < ActiveRecord::Base
end
def empty_repo?
!repository.exists? || repository.empty?
!repository.exists? || !repository.has_visible_content?
end
def repo
......
......@@ -44,6 +44,19 @@ class Repository
raw_repository.empty?
end
#
# Git repository can contains some hidden refs like:
# /refs/notes/*
# /refs/git-as-svn/*
# /refs/pulls/*
# This refs by default not visible in project page and not cloned to client side.
#
# This method return true if repository contains some content visible in project page.
#
def has_visible_content?
!raw_repository.branches.empty?
end
def commit(id = 'HEAD')
return nil unless raw_repository
commit = Gitlab::Git::Commit.find(raw_repository, id)
......@@ -54,13 +67,16 @@ class Repository
end
def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
commits = Gitlab::Git::Commit.where(
options = {
repo: raw_repository,
ref: ref,
path: path,
limit: limit,
offset: offset,
)
follow: path.present?
}
commits = Gitlab::Git::Commit.where(options)
commits = Commit.decorate(commits, @project) if commits.present?
commits
end
......@@ -480,7 +496,7 @@ class Repository
def search_files(query, ref)
offset = 2
args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref})
args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref})
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
end
......
......@@ -401,15 +401,17 @@ class User < ActiveRecord::Base
end
end
def authorized_projects_id
@authorized_projects_id ||= begin
project_ids = personal_projects.pluck(:id)
project_ids.push(*groups_projects.pluck(:id))
project_ids.push(*projects.pluck(:id).uniq)
end
end
# Projects user has access to
def authorized_projects
@authorized_projects ||= begin
project_ids = personal_projects.pluck(:id)
project_ids.push(*groups_projects.pluck(:id))
project_ids.push(*projects.pluck(:id).uniq)
Project.where(id: project_ids)
end
@authorized_projects ||= Project.where(id: authorized_projects_id)
end
def owned_projects
......@@ -768,11 +770,14 @@ class User < ActiveRecord::Base
end
def ci_authorized_projects
@ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects)
@ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id)
end
def ci_authorized_runners
Ci::Runner.specific.includes(:runner_projects).
where(ci_runner_projects: { project_id: ci_authorized_projects } )
@ci_authorized_runners ||= begin
runner_ids = Ci::RunnerProject.joins(:project).
where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id)
Ci::Runner.specific.where(id: runner_ids)
end
end
end
module Ci
class ImageForBuildService
def execute(project, params)
image_name =
if params[:sha]
commit = project.commits.find_by(sha: params[:sha])
image_for_commit(commit)
elsif params[:ref]
commit = project.last_commit_for_ref(params[:ref])
image_for_commit(commit)
else
'build-unknown.svg'
sha = params[:sha]
sha ||=
if params[:ref]
project.gl_project.commit(params[:ref]).try(:sha)
end
commit = project.commits.ordered.find_by(sha: sha)
image_name = image_for_commit(commit)
image_path = Rails.root.join('public/ci', image_name)
OpenStruct.new(
......
......@@ -5,20 +5,19 @@ module MergeRequests
@oldrev, @newrev = oldrev, newrev
@branch_name = Gitlab::Git.ref_name(ref)
@fork_merge_requests = @project.fork_merge_requests.opened
@commits = []
# Leave a system note if a branch were deleted/added
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
find_new_commits
reload_merge_requests
# Leave a system note if a branch was deleted/added
if branch_added? || branch_removed?
comment_mr_branch_presence_changed
comment_mr_with_commits if @commits.present?
comment_mr_with_commits
else
@commits = @project.repository.commits_between(oldrev, newrev)
comment_mr_with_commits
close_merge_requests
end
reload_merge_requests
execute_mr_web_hooks
true
......@@ -54,7 +53,7 @@ module MergeRequests
# Note: we should update merge requests from forks too
def reload_merge_requests
merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a
merge_requests += @fork_merge_requests.by_branch(@branch_name).to_a
merge_requests += fork_merge_requests.by_branch(@branch_name).to_a
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request|
......@@ -77,29 +76,37 @@ module MergeRequests
end
end
# Add comment about branches being deleted or added to merge requests
def comment_mr_branch_presence_changed
presence = Gitlab::Git.blank_ref?(@oldrev) ? :add : :delete
def find_new_commits
if branch_added?
@commits = []
merge_requests_for_source_branch.each do |merge_request|
last_commit = merge_request.last_commit
merge_request = merge_requests_for_source_branch.first
return unless merge_request
# Only look at changed commits in restore branch case
unless Gitlab::Git.blank_ref?(@newrev)
begin
# Since any number of commits could have been made to the restored branch,
# find the common root to see what has been added.
common_ref = @project.repository.merge_base(last_commit.id, @newrev)
# If the a commit no longer exists in this repo, gitlab_git throws
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
@commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
rescue
end
last_commit = merge_request.last_commit
# Prevent system notes from seeing a blank SHA
@oldrev = nil
begin
# Since any number of commits could have been made to the restored branch,
# find the common root to see what has been added.
common_ref = @project.repository.merge_base(last_commit.id, @newrev)
# If the a commit no longer exists in this repo, gitlab_git throws
# a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52
@commits = @project.repository.commits_between(common_ref, @newrev) if common_ref
rescue
end
elsif branch_removed?
# No commits for a deleted branch.
@commits = []
else
@commits = @project.repository.commits_between(@oldrev, @newrev)
end
end
# Add comment about branches being deleted or added to merge requests
def comment_mr_branch_presence_changed
presence = branch_added? ? :add : :delete
merge_requests_for_source_branch.each do |merge_request|
SystemNoteService.change_branch_presence(
merge_request, merge_request.project, @current_user,
:source, @branch_name, presence)
......@@ -108,6 +115,8 @@ module MergeRequests
# Add comment about pushing new commits to merge requests
def comment_mr_with_commits
return unless @commits.present?
merge_requests_for_source_branch.each do |merge_request|
mr_commit_ids = Set.new(merge_request.commits.map(&:id))
......@@ -135,9 +144,21 @@ module MergeRequests
def merge_requests_for_source_branch
@source_merge_requests ||= begin
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a
merge_requests += fork_merge_requests.where(source_branch: @branch_name).to_a
filter_merge_requests(merge_requests)
end
end
def fork_merge_requests
@fork_merge_requests ||= @project.fork_merge_requests.opened
end
def branch_added?
Gitlab::Git.blank_ref?(@oldrev)
end
def branch_removed?
Gitlab::Git.blank_ref?(@newrev)
end
end
end
......@@ -327,7 +327,7 @@ class SystemNoteService
commit_ids = if count == 1
existing_commits.first.short_id
else
if oldrev
if oldrev && !Gitlab::Git.blank_ref?(oldrev)
"#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}"
else
"#{existing_commits.first.short_id}..#{existing_commits.last.short_id}"
......
%p.lead
To register new runner visit #{link_to 'this page ', ci_runners_path}
To register a new runner visit #{link_to 'this page ', ci_runners_path}
.row
.col-md-8
......
%p.lead
%span To register new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication.
%span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication.
%code #{GitlabCi::REGISTRATION_TOKEN}
.bs-callout
......@@ -21,7 +21,7 @@
\- run builds from assigned projects
%li
%span.label.label-danger paused
\- runner will not receive any new build
\- runner will not receive any new builds
.append-bottom-20.clearfix
.pull-left
......
......@@ -13,13 +13,13 @@
- if @runner.shared?
.bs-callout.bs-callout-success
%h4 This runner will process build from ALL UNASSIGNED projects
%h4 This runner will process builds from ALL UNASSIGNED projects
%p
If you want runners to build only specific projects, enable them in the table below.
Keep in mind that this is a one way transition.
- else
.bs-callout.bs-callout-info
%h4 This runner will process build only from ASSIGNED projects
%h4 This runner will process builds only from ASSIGNED projects
%p You can't make this a shared runner.
%hr
= form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f|
......@@ -53,13 +53,14 @@
%th
- @runner.runner_projects.each do |runner_project|
- project = runner_project.project
%tr.alert-info
%td
%strong
= project.name
%td
.pull-right
= link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
- if project.gl_project
%tr.alert-info
%td
%strong
= project.name
%td
.pull-right
= link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
%table.table
%thead
......@@ -103,21 +104,26 @@
%th Finished at
- @builds.each do |build|
- gl_project = build.gl_project
%tr.build
%td.id
- gl_project = build.project.gl_project
= link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
- if gl_project
= link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
= build.id
- else
= build.id
%td.status
= ci_status_with_icon(build.status)
%td.status
= build.project.name
- if gl_project
= gl_project.name_with_namespace
%td.build-link
= link_to ci_status_path(build.commit) do
%strong #{build.commit.short_sha}
- if gl_project
= link_to ci_status_path(build.commit) do
%strong #{build.commit.short_sha}
%td.timestamp
- if build.finished_at
......
......@@ -17,7 +17,7 @@
%td #{stage.capitalize} Job - #{build[:name]}
%td
%pre
= simple_format build[:script]
= simple_format build[:commands]
%br
%b Tag list:
......@@ -28,6 +28,11 @@
%br
%b Refs except:
= build[:except] && build[:except].join(", ")
%br
%b When:
= build[:when]
- if build[:allow_failure]
%b Allowed to fail
-else
%p
......
.login-block
%h2 Login using GitLab account
%p.light
Make sure you have account on GitLab server
Make sure you have an account on the GitLab server
= link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink
%hr
= link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' )
......@@ -6,33 +6,29 @@
.gray-content-block
.pull-right
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
Add new snippet
= icon('plus')
New Snippet
.oneline
Share code pastes with others out of git repository
%ul.nav.nav-tabs.prepend-top-20
= nav_tab :scope, nil do
= link_to dashboard_snippets_path do
.btn-group.btn-group-next.snippet-scope-menu
= link_to dashboard_snippets_path, class: "btn btn-default #{"active" unless params[:scope]}" do
All
%span.badge
= current_user.snippets.count
= nav_tab :scope, 'are_private' do
= link_to dashboard_snippets_path(scope: 'are_private') do
= link_to dashboard_snippets_path(scope: 'are_private'), class: "btn btn-default #{"active" if params[:scope] == "are_private"}" do
Private
%span.badge
= current_user.snippets.are_private.count
= nav_tab :scope, 'are_internal' do
= link_to dashboard_snippets_path(scope: 'are_internal') do
= link_to dashboard_snippets_path(scope: 'are_internal'), class: "btn btn-default #{"active" if params[:scope] == "are_internal"}" do
Internal
%span.badge
= current_user.snippets.are_internal.count
= nav_tab :scope, 'are_public' do
= link_to dashboard_snippets_path(scope: 'are_public') do
= link_to dashboard_snippets_path(scope: 'are_public'), class: "btn btn-default #{"active" if params[:scope] == "are_public"}" do
Public
%span.badge
= current_user.snippets.are_public.count
.my-snippets
= render 'snippets/snippets'
= render 'snippets/snippets'
......@@ -10,7 +10,8 @@
- if current_user
.pull-right
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
Add new snippet
= icon('plus')
New Snippet
.oneline
Public snippets created by you and other users are listed here
......
- if readme = @repository.readme
%article.file-holder.readme-holder
.file-title
= blob_icon readme.mode, readme.name
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
%strong
= readme.name
%article.readme-holder
.pull-right
- if can?(current_user, :push_code, @project)
= link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light edit-project-readme'
.file-content.wiki
= cache(readme_cache_key) do
= render_readme(readme)
......
......@@ -155,7 +155,7 @@
- if @builds.present?
.build-widget
%h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}:
%h4.title #{pluralize(@builds.count(:id), "other build")} for #{@build.short_sha}:
%table.table.builds
- @builds.each_with_index do |build, i|
%tr.build
......@@ -175,4 +175,4 @@
:javascript
new CiBuild("#{namespace_project_build_path(@project.namespace, @project, @build)}", "#{@build.status}")
new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}")
......@@ -5,7 +5,7 @@
= hidden_field_tag :notification_id, @membership.id
= hidden_field_tag :notification_level
%span.dropdown
%a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"}
%a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"}
= icon('bell')
= notification_label(@membership)
= icon('angle-down')
......@@ -14,7 +14,7 @@
= notification_list_item(level, @membership)
- when GroupMember
.btn.btn-new.disabled.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
.btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."}
= icon('bell')
= notification_label(@membership)
= icon('angle-down')
......@@ -5,4 +5,4 @@
You can add Specific runner for this project on Runners page
- if current_user.admin
or add Shared runner for whole application in admin are.
or add Shared runner for whole application in admin area.
......@@ -18,10 +18,10 @@
.pull-right
- if ci_commit
= link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do
= ci_status_icon(ci_commit)
= render_ci_status(ci_commit)
&nbsp;
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
= clipboard_button
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id}
.notes_count
- if note_count > 0
......
......@@ -2,53 +2,56 @@
- if current_user && can?(current_user, :download_code, @project)
= render 'shared/no_ssh'
= render 'shared/no_password'
= render "home_panel"
.gray-content-block.center
%h3.page-title
The repository for this project is empty
%p
If you already have files you can push them using command line instructions below.
%br
Otherwise you can start with
= link_to "adding README", new_readme_path, class: 'underlined-link'
file to this project.
- if can?(current_user, :download_code, @project)
%p
If you already have files you can push them using command line instructions below.
%br
- if can?(current_user, :push_code, @project)
Otherwise you can start with
= link_to "adding README", new_readme_path, class: 'underlined-link'
file to this project.
.prepend-top-20
.empty_wrapper
%h3.page-title-empty
Command line instructions
%div.git-empty
%fieldset
%h5 Git global setup
%pre.light-well
:preserve
git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}"
- if can?(current_user, :download_code, @project)
.prepend-top-20
.empty_wrapper
%h3.page-title-empty
Command line instructions
%div.git-empty
%fieldset
%h5 Git global setup
%pre.light-well
:preserve
git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}"
%fieldset
%h5 Create a new repository
%pre.light-well
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
cd #{h @project.path}
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master
%fieldset
%h5 Create a new repository
%pre.light-well
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
cd #{h @project.path}
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master
%fieldset
%h5 Existing folder or Git repository
%pre.light-well
:preserve
cd existing_folder
git init
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
git add .
git commit
git push -u origin master
%fieldset
%h5 Existing folder or Git repository
%pre.light-well
:preserve
cd existing_folder
git init
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
git add .
git commit
git push -u origin master
- if can? current_user, :remove_project, @project
.prepend-top-20
= link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
- if can? current_user, :remove_project, @project
.prepend-top-20
= link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
......@@ -17,8 +17,10 @@
- @participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
.col-md-3
%span.slead.has_tooltip{title: 'Cross-project reference'}
= cross_project_reference(@project, @issue)
.input-group.cross-project-reference
%span.slead.has_tooltip{title: 'Cross-project reference'}
= cross_project_reference(@project, @issue)
= clipboard_button
.row
%section.col-md-9
......
......@@ -5,8 +5,9 @@
.nothing-here-block No issues to show
- if @issues.present?
.pull-right
%span.issue_counter #{@issues.total_count}
issues for this filter
.issuable-filter-count
%span.pull-right
= @issues.total_count
issues for this filter
= paginate @issues, theme: "gitlab"
- ci_commit = merge_request.ci_commit
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
%span.merge-request-title-text
......@@ -6,6 +7,8 @@
- merge_request.labels.each do |label|
= link_to_label(label, project: merge_request.project)
.pull-right.light
- if ci_commit
= render_ci_status(ci_commit)
- if merge_request.merged?
%span
%i.fa.fa-check
......
......@@ -5,8 +5,10 @@
.nothing-here-block No merge requests to show
- if @merge_requests.present?
.pull-right
%span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
.issuable-filter-count
%span.pull-right
= @merge_requests.total_count
merge requests for this filter
= paginate @merge_requests, theme: "gitlab"
......@@ -3,11 +3,12 @@
.modal-content
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3 Check out, review and merge locally
%h3 Check out, review, and merge locally
.modal-body
%p
%strong Step 1.
%strong Step 1.
Fetch and check out the branch for this merge request
= clipboard_button
%pre.dark
- if @merge_request.for_fork?
:preserve
......@@ -24,6 +25,7 @@
%p
%strong Step 3.
Merge the branch and fix any conflicts that come up
= clipboard_button
%pre.dark
- if @merge_request.for_fork?
:preserve
......@@ -36,6 +38,7 @@
%p
%strong Step 4.
Push the result of the merge to GitLab
= clipboard_button
%pre.dark
:preserve
git push origin #{h @merge_request.target_branch}
......
- ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha)
- ci_commit = @merge_request.ci_commit
- if ci_commit
- status = ci_commit.status
.mr-widget-heading
......
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
= icon('plus')
New Snippet
- if can?(current_user, :admin_project_snippet, @snippet)
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
= icon('trash-o')
Delete
- if can?(current_user, :update_project_snippet, @snippet)
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
= icon('pencil-square-o')
Edit
- page_title "Snippets"
= render "header_title"
%h3.page-title
Snippets
- if can? current_user, :create_project_snippet, @project
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Snippet" do
Add new snippet
.gray-content-block.top-block
.pull-right
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
= icon('plus')
New Snippet
%p.light
Share code pastes with others out of git repository
.oneline
Share code pastes with others out of git repository
%ul.bordered-list
= render partial: "shared/snippets/snippet", collection: @snippets
- if @snippets.empty?
%li
.nothing-here-block Nothing here.
= render 'snippets/snippets'
- page_title @snippet.title, "Snippets"
= render "header_title"
%h3.page-title
= @snippet.title
.snippet-holder
= render 'shared/snippets/header'
.pull-right
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
Add new snippet
%article.file-holder
.file-title
= blob_icon 0, @snippet.file_name
%strong
= @snippet.file_name
.file-actions.hidden-xs
.btn-group.tree-btn-group
= link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
%hr
= render 'shared/snippets/blob'
.append-bottom-20
.pull-right
= "##{@snippet.id}"
%span.light
by
= link_to user_path(@snippet.author) do
= image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16"
= @snippet.author_name
.back-link
= link_to namespace_project_snippets_path(@project.namespace, @project) do
&larr; project snippets
.file-holder
.file-title
%i.fa.fa-file
%strong
= @snippet.file_name
.file-actions
.btn-group
- if can?(current_user, :update_project_snippet, @snippet)
= link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", title: 'Edit Snippet'
= link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
- if can?(current_user, :admin_project_snippet, @snippet)
= link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
= render 'shared/snippets/blob'
%div#notes= render "projects/notes/notes_with_form"
%div#notes= render "projects/notes/notes_with_form"
......@@ -21,9 +21,7 @@
.project-controls
- if ci && !project.empty_repo? && project.commit
- if ci_commit = project.ci_commit(project.commit.sha)
= link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}",
title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do
= ci_status_icon(ci_commit)
= render_ci_status(ci_commit)
&nbsp;
- if stars
%span
......
.snippet-details
.page-title
.snippet-box{class: visibility_level_color(@snippet.visibility_level)}
= visibility_level_icon(@snippet.visibility_level)
= visibility_level_label(@snippet.visibility_level)
%span.snippet-id Snippet ##{@snippet.id}
%span.creator
&middot; created by #{link_to_member(@project, @snippet.author, size: 24)}
&middot;
= time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
- if @snippet.updated_at != @snippet.created_at
%span
&middot;
= icon('edit', title: 'edited')
= time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
.pull-right
- if @snippet.project_id?
= render "projects/snippets/actions"
- else
= render "snippets/actions"
.gray-content-block.middle-block
%h2.snippet-title
= gfm escape_once(@snippet.title)
......@@ -18,4 +18,3 @@
= image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: ''
= snippet.author_name
authored #{time_ago_with_tooltip(snippet.created_at)}
= link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do
= icon('plus')
New Snippet
- if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do
= icon('trash-o')
Delete
- if can?(current_user, :update_personal_snippet, @snippet)
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
= icon('pencil-square-o')
Edit
- page_title @snippet.title, "Snippets"
%h4.page-title
= @snippet.title
- if @snippet.private?
%span.label.label-success
%i.fa.fa-lock
private
.pull-right
= link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do
Add new snippet
.append-bottom-10.prepend-top-10
.pull-right
%span.light
created by
= link_to user_snippets_path(@snippet.author) do
= @snippet.author_name
.back-link
- if @snippet.author == current_user
= link_to dashboard_snippets_path do
&larr; your snippets
- else
= link_to explore_snippets_path do
&larr; explore snippets
.file-holder
.file-title
%i.fa.fa-file
%strong
= @snippet.file_name
.file-actions
.btn-group
- if can?(current_user, :update_personal_snippet, @snippet)
= link_to "edit", edit_snippet_path(@snippet), class: "btn btn-sm", title: 'Edit Snippet'
= link_to "raw", raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
- if can?(current_user, :admin_personal_snippet, @snippet)
= link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet'
= render 'shared/snippets/blob'
.snippet-holder
= render 'shared/snippets/header'
%article.file-holder
.file-title
= blob_icon 0, @snippet.file_name
%strong
= @snippet.file_name
.file-actions.hidden-xs
.btn-group.tree-btn-group
= link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
= render 'shared/snippets/blob'
......@@ -318,10 +318,12 @@ production: &base
# ==========================
# GitLab Satellites
#
# Note for maintainers: keep the satellites.path setting until GitLab 9.0 at
# least. This setting is fed to 'rm -rf' in
# db/migrate/20151023144219_remove_satellites.rb
satellites:
# Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
path: /home/git/gitlab-satellites/
timeout: 30
## Backup settings
backup:
......
......@@ -242,9 +242,11 @@ Settings.git['max_size'] ||= 20971520 # 20.megabytes
Settings.git['bin_path'] ||= '/usr/bin/git'
Settings.git['timeout'] ||= 10
# Important: keep the satellites.path setting until GitLab 9.0 at
# least. This setting is fed to 'rm -rf' in
# db/migrate/20151023144219_remove_satellites.rb
Settings['satellites'] ||= Settingslogic.new({})
Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "tmp/repo_satellites/", Rails.root)
Settings.satellites['timeout'] ||= 30
#
# Extra customization
......
class FailBuildWithEmptyName < ActiveRecord::Migration
def change
execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'")
end
end
require 'fileutils'
class RemoveSatellites < ActiveRecord::Migration
def up
satellites = Gitlab.config['satellites']
return if satellites.nil?
satellites_path = satellites['path']
return if satellites_path.nil?
FileUtils.rm_rf(satellites_path)
end
def down
# Do nothing
end
end
class AddProjectPathIndex < ActiveRecord::Migration
def up
add_index :projects, :path
end
def down
remove_index :projects, :path
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20151020173906) do
ActiveRecord::Schema.define(version: 20151026182941) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -624,6 +624,7 @@ ActiveRecord::Schema.define(version: 20151020173906) do
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
add_index "projects", ["path"], name: "index_projects_on_path", using: :btree
add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
create_table "protected_branches", force: true do |t|
......
......@@ -90,7 +90,7 @@ you need to set MYSQL_ALLOW_EMPTY_PASSWORD.
- mysql
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
```
For other possible configuration variables check the
......
......@@ -346,11 +346,6 @@ The `secrets.yml` file stores encryption keys for sessions and secure variables.
Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups.
Otherwise your secrets are exposed if one of your backups is compromised.
### Install schedules
# Setup schedules
sudo -u gitlab_ci -H bundle exec whenever -w RAILS_ENV=production
### Install Init Script
Download the init script (will be `/etc/init.d/gitlab`):
......
......@@ -29,7 +29,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
Also you can choose what should be backed up by adding environment variable SKIP. Available options: db,
uploads (attachments), repositories. Use a comma to specify several options at the same time.
uploads (attachments), repositories, builds(CI build output logs). Use a comma to specify several options at the same time.
```
sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
......
......@@ -26,7 +26,7 @@ After getting used to these three steps the branching model becomes the challeng
Since many organizations new to git have no conventions how to work with it, it can quickly become a mess.
The biggest problem they run into is that many long running branches that each contain part of the changes are around.
People have a hard time figuring out which branch they should develop on or deploy to production.
Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html)
Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html).
We think there is still room for improvement and will detail a set of practices we call GitLab flow.
## Git flow and its problems
......
......@@ -10,6 +10,12 @@ Feature: Project Merge Requests
Then I should see "Bug NS-04" in merge requests
And I should not see "Feature NS-03" in merge requests
Scenario: I should see CI status for merge requests
Given project "Shop" have "Bug NS-05" open merge request with diffs inside
Given "Bug NS-05" has CI status
When I visit project "Shop" merge requests page
Then I should see merge request "Bug NS-05" with CI status
Scenario: I should see rejected merge requests
Given I click link "Closed"
Then I should see "Feature NS-03" in merge requests
......
......@@ -30,5 +30,5 @@ Feature: Project Snippets
Scenario: I destroy "Snippet one"
Given I visit snippet page "Snippet one"
And I click link "Remove Snippet"
And I click link "Delete"
Then I should not see "Snippet one" in snippets
......@@ -24,7 +24,7 @@ Feature: Snippets
Scenario: I destroy "Personal snippet one"
Given I visit snippet page "Personal snippet one"
And I click link "Destroy"
And I click link "Delete"
Then I should not see "Personal snippet one" in snippets
Scenario: I create new internal snippet
......
......@@ -338,6 +338,19 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
expect(page).to have_content('diff --git')
end
step '"Bug NS-05" has CI status' do
project = merge_request.source_project
project.enable_ci
ci_commit = create :ci_commit, gl_project: project, sha: merge_request.last_commit.id
create :ci_build, commit: ci_commit
end
step 'I should see merge request "Bug NS-05" with CI status' do
page.within ".mr-list" do
expect(page).to have_link "Build status: pending"
end
end
def merge_request
@merge_request ||= MergeRequest.find_by!(title: "Bug NS-05")
end
......
......@@ -22,7 +22,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
end
step 'I click link "New Snippet"' do
click_link "Add new snippet"
click_link "New Snippet"
end
step 'I click link "Snippet one"' do
......@@ -42,13 +42,13 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps
end
step 'I click link "Edit"' do
page.within ".file-title" do
page.within ".page-title" do
click_link "Edit"
end
end
step 'I click link "Remove Snippet"' do
click_link "remove"
step 'I click link "Delete"' do
click_link "Delete"
end
step 'I submit new snippet "Snippet three"' do
......
......@@ -13,13 +13,13 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps
end
step 'I click link "Edit"' do
page.within ".file-title" do
page.within ".page-title" do
click_link "Edit"
end
end
step 'I click link "Destroy"' do
click_link "remove"
step 'I click link "Delete"' do
click_link "Delete"
end
step 'I submit new snippet "Personal snippet three"' do
......
......@@ -32,19 +32,19 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps
end
step 'I click "Internal" filter' do
page.within('.nav-tabs') do
page.within('.snippet-scope-menu') do
click_link "Internal"
end
end
step 'I click "Private" filter' do
page.within('.nav-tabs') do
page.within('.snippet-scope-menu') do
click_link "Private"
end
end
step 'I click "Public" filter' do
page.within('.nav-tabs') do
page.within('.snippet-scope-menu') do
click_link "Public"
end
end
......
......@@ -25,7 +25,7 @@ module API
format :json
content_type :txt, "text/plain"
helpers APIHelpers
helpers Helpers
mount Groups
mount GroupMembers
......
module API
module APIHelpers
module Helpers
PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN"
PRIVATE_TOKEN_PARAM = :private_token
SUDO_HEADER ="HTTP_SUDO"
......
......@@ -26,7 +26,7 @@ module Ci
format :json
helpers Helpers
helpers ::API::APIHelpers
helpers ::API::Helpers
mount Builds
mount Commits
......
......@@ -139,66 +139,74 @@ module Ci
end
@jobs.each do |name, job|
validate_job!("#{name} job", job)
validate_job!(name, job)
end
true
end
def validate_job!(name, job)
if name.blank? || !validate_string(name)
raise ValidationError, "job name should be non-empty string"
end
job.keys.each do |key|
unless ALLOWED_JOB_KEYS.include? key
raise ValidationError, "#{name}: unknown parameter #{key}"
raise ValidationError, "#{name} job: unknown parameter #{key}"
end
end
if !job[:script].is_a?(String) && !validate_array_of_strings(job[:script])
raise ValidationError, "#{name}: script should be a string or an array of a strings"
if !validate_string(job[:script]) && !validate_array_of_strings(job[:script])
raise ValidationError, "#{name} job: script should be a string or an array of a strings"
end
if job[:stage]
unless job[:stage].is_a?(String) && job[:stage].in?(stages)
raise ValidationError, "#{name}: stage parameter should be #{stages.join(", ")}"
raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}"
end
end
if job[:image] && !job[:image].is_a?(String)
raise ValidationError, "#{name}: image should be a string"
if job[:image] && !validate_string(job[:image])
raise ValidationError, "#{name} job: image should be a string"
end
if job[:services] && !validate_array_of_strings(job[:services])
raise ValidationError, "#{name}: services should be an array of strings"
raise ValidationError, "#{name} job: services should be an array of strings"
end
if job[:tags] && !validate_array_of_strings(job[:tags])
raise ValidationError, "#{name}: tags parameter should be an array of strings"
raise ValidationError, "#{name} job: tags parameter should be an array of strings"
end
if job[:only] && !validate_array_of_strings(job[:only])
raise ValidationError, "#{name}: only parameter should be an array of strings"
raise ValidationError, "#{name} job: only parameter should be an array of strings"
end
if job[:except] && !validate_array_of_strings(job[:except])
raise ValidationError, "#{name}: except parameter should be an array of strings"
raise ValidationError, "#{name} job: except parameter should be an array of strings"
end
if job[:allow_failure] && !job[:allow_failure].in?([true, false])
raise ValidationError, "#{name}: allow_failure parameter should be an boolean"
raise ValidationError, "#{name} job: allow_failure parameter should be an boolean"
end
if job[:when] && !job[:when].in?(%w(on_success on_failure always))
raise ValidationError, "#{name}: when parameter should be on_success, on_failure or always"
raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always"
end
end
private
def validate_array_of_strings(values)
values.is_a?(Array) && values.all? {|tag| tag.is_a?(String)}
values.is_a?(Array) && values.all? { |value| validate_string(value) }
end
def validate_variables(variables)
variables.is_a?(Hash) && variables.all? {|key, value| key.is_a?(Symbol) && value.is_a?(String)}
variables.is_a?(Hash) && variables.all? { |key, value| validate_string(key) && validate_string(value) }
end
def validate_string(value)
value.is_a?(String) || value.is_a?(Symbol)
end
end
end
module Ci
module Migrate
class Builds
attr_reader :app_builds_dir, :backup_builds_tarball, :backup_dir
def initialize
@app_builds_dir = Settings.gitlab_ci.builds_path
@backup_dir = Gitlab.config.backup.path
@backup_builds_tarball = File.join(backup_dir, 'builds/builds.tar.gz')
end
def restore
backup_existing_builds_dir
FileUtils.mkdir_p(app_builds_dir, mode: 0700)
unless system('tar', '-C', app_builds_dir, '-zxf', backup_builds_tarball)
abort 'Restore failed'.red
end
end
def backup_existing_builds_dir
timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}")
if File.exists?(app_builds_dir)
FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path))
end
end
end
end
end
require 'yaml'
module Ci
module Migrate
class Database
attr_reader :config
def initialize
@config = YAML.load_file(File.join(Rails.root, 'config', 'database.yml'))[Rails.env]
end
def restore
decompress_rd, decompress_wr = IO.pipe
decompress_pid = spawn(*%W(gzip -cd), out: decompress_wr, in: db_file_name)
decompress_wr.close
restore_pid = case config["adapter"]
when /^mysql/ then
$progress.print "Restoring MySQL database #{config['database']} ... "
# Workaround warnings from MySQL 5.6 about passwords on cmd line
ENV['MYSQL_PWD'] = config["password"].to_s if config["password"]
spawn('mysql', *mysql_args, config['database'], in: decompress_rd)
when "postgresql" then
$progress.print "Restoring PostgreSQL database #{config['database']} ... "
pg_env
spawn('psql', config['database'], in: decompress_rd)
end
decompress_rd.close
success = [decompress_pid, restore_pid].all? { |pid| Process.waitpid(pid); $?.success? }
abort 'Restore failed' unless success
end
protected
def db_file_name
File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz')
end
def mysql_args
args = {
'host' => '--host',
'port' => '--port',
'socket' => '--socket',
'username' => '--user',
'encoding' => '--default-character-set'
}
args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
end
def pg_env
ENV['PGUSER'] = config["username"] if config["username"]
ENV['PGHOST'] = config["host"] if config["host"]
ENV['PGPORT'] = config["port"].to_s if config["port"]
ENV['PGPASSWORD'] = config["password"].to_s if config["password"]
end
def report_success(success)
if success
puts '[DONE]'.green
else
puts '[FAILED]'.red
end
end
end
end
end
module Ci
module Migrate
class Manager
CI_IMPORT_PREFIX = '8.0' # Only allow imports from CI 8.0.x
def cleanup
$progress.print "Deleting tmp directories ... "
backup_contents.each do |dir|
next unless File.exist?(File.join(Gitlab.config.backup.path, dir))
if FileUtils.rm_rf(File.join(Gitlab.config.backup.path, dir))
$progress.puts "done".green
else
puts "deleting tmp directory '#{dir}' failed".red
abort 'Backup failed'
end
end
end
def unpack
Dir.chdir(Gitlab.config.backup.path)
# check for existing backups in the backup dir
file_list = Dir.glob("*_gitlab_ci_backup.tar").each.map { |f| f.split(/_/).first.to_i }
puts "no backups found" if file_list.count == 0
if file_list.count > 1 && ENV["BACKUP"].nil?
puts "Found more than one backup, please specify which one you want to restore:"
puts "rake gitlab:backup:restore BACKUP=timestamp_of_backup"
exit 1
end
tar_file = ENV["BACKUP"].nil? ? File.join("#{file_list.first}_gitlab_ci_backup.tar") : File.join(ENV["BACKUP"] + "_gitlab_ci_backup.tar")
unless File.exists?(tar_file)
puts "The specified CI backup doesn't exist!"
exit 1
end
$progress.print "Unpacking backup ... "
unless Kernel.system(*%W(tar -xf #{tar_file}))
puts "unpacking backup failed".red
exit 1
else
$progress.puts "done".green
end
ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
# restoring mismatching backups can lead to unexpected problems
if !settings[:gitlab_version].start_with?(CI_IMPORT_PREFIX)
puts "GitLab CI version mismatch:".red
puts " Your current GitLab CI version (#{GitlabCi::VERSION}) differs from the GitLab CI (#{settings[:gitlab_version]}) version in the backup!".red
exit 1
end
end
private
def backup_contents
["db", "builds", "backup_information.yml"]
end
def settings
@settings ||= YAML.load_file("backup_information.yml")
end
end
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment